Merge branch 'master' of https://github.com/highfidelity/hifi into gameTable2

This commit is contained in:
Thijs Wenker 2017-04-17 13:12:12 +02:00
commit d101bdb952
314 changed files with 10843 additions and 4393 deletions
README.md
assignment-client/src
cmake
interface
CMakeLists.txt
resources
src

View file

@ -17,6 +17,8 @@ Documentation
=========
Documentation is available at [docs.highfidelity.com](https://docs.highfidelity.com), if something is missing, please suggest it via a new job on Worklist (add to the hifi-docs project).
There is also detailed [documentation on our coding standards](https://wiki.highfidelity.com/wiki/Coding_Standards).
Build Instructions
=========
All information required to build is found in the [build guide](BUILD.md).

View file

@ -29,10 +29,11 @@
#include <udt/PacketHeaders.h>
#include <ResourceCache.h>
#include <ScriptCache.h>
#include <SoundCache.h>
#include <ScriptEngines.h>
#include <SoundCache.h>
#include <UUID.h>
#include <recording/ClipCache.h>
#include <recording/Deck.h>
#include <recording/Recorder.h>
#include <recording/Frame.h>
@ -53,7 +54,8 @@ static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10;
Agent::Agent(ReceivedMessage& message) :
ThreadedAssignment(message),
_receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES) {
_receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES, RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES)
{
DependencyManager::get<EntityScriptingInterface>()->setPacketSender(&_entityEditSender);
ResourceManager::init();
@ -64,12 +66,16 @@ Agent::Agent(ReceivedMessage& message) :
DependencyManager::set<SoundCache>();
DependencyManager::set<AudioScriptingInterface>();
DependencyManager::set<AudioInjectorManager>();
DependencyManager::set<recording::Deck>();
DependencyManager::set<recording::Recorder>();
DependencyManager::set<RecordingScriptingInterface>();
DependencyManager::set<recording::ClipCache>();
DependencyManager::set<ScriptCache>();
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT);
DependencyManager::set<RecordingScriptingInterface>();
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListenerForTypes(
@ -327,6 +333,8 @@ void Agent::executeScript() {
_scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload));
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(_scriptEngine.get());
// setup an Avatar for the script to use
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
@ -336,6 +344,10 @@ void Agent::executeScript() {
// call model URL setters with empty URLs so our avatar, if user, will have the default models
scriptedAvatar->setSkeletonModelURL(QUrl());
// force lazy initialization of the head data for the scripted avatar
// since it is referenced below by computeLoudness and getAudioLoudness
scriptedAvatar->getHeadOrientation();
// give this AvatarData object to the script engine
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());
@ -466,6 +478,8 @@ void Agent::executeScript() {
Frame::clearFrameHandler(AUDIO_FRAME_TYPE);
Frame::clearFrameHandler(AVATAR_FRAME_TYPE);
DependencyManager::destroy<RecordingScriptingInterface>();
setFinished(true);
}
@ -749,8 +763,19 @@ void Agent::aboutToFinish() {
// cleanup the AudioInjectorManager (and any still running injectors)
DependencyManager::destroy<AudioInjectorManager>();
// destroy all other created dependencies
DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<ScriptEngines>();
DependencyManager::destroy<ResourceCacheSharedItems>();
DependencyManager::destroy<SoundCache>();
DependencyManager::destroy<AudioScriptingInterface>();
DependencyManager::destroy<recording::Deck>();
DependencyManager::destroy<recording::Recorder>();
DependencyManager::destroy<recording::ClipCache>();
emit stopAvatarAudioTimer();
_avatarAudioTimerThread.quit();

View file

@ -46,9 +46,6 @@ class Agent : public ThreadedAssignment {
public:
Agent(ReceivedMessage& message);
void setIsAvatar(bool isAvatar);
bool isAvatar() const { return _isAvatar; }
bool isPlayingAvatarSound() const { return _avatarSound != NULL; }
bool isListeningToAudioStream() const { return _isListeningToAudioStream; }
@ -65,6 +62,9 @@ public:
public slots:
void run() override;
void playAvatarSound(SharedSoundPointer avatarSound);
void setIsAvatar(bool isAvatar);
bool isAvatar() const { return _isAvatar; }
private slots:
void requestScript();

View file

@ -187,7 +187,7 @@ void AvatarMixer::start() {
// NOTE: nodeData->getAvatar() might be side effected, must be called when access to node/nodeData
// is guarenteed to not be accessed by other thread
// is guaranteed to not be accessed by other thread
void AvatarMixer::manageDisplayName(const SharedNodePointer& node) {
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
@ -200,7 +200,7 @@ void AvatarMixer::manageDisplayName(const SharedNodePointer& node) {
QString baseName = avatar.getDisplayName().trimmed();
const QRegularExpression curses { "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?).
baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk.
const QRegularExpression trailingDigits { "\\s*_\\d+$" }; // whitespace "_123"
const QRegularExpression trailingDigits { "\\s*(_\\d+\\s*)?(\\s*\\n[^$]*)?$" }; // trailing whitespace "_123" and any subsequent lines
baseName = baseName.remove(trailingDigits);
if (baseName.isEmpty()) {
baseName = "anonymous";
@ -437,17 +437,20 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage>
while (message->getBytesLeftToRead()) {
// parse out the UUID being ignored from the packet
QUuid ignoredUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
// Reset the lastBroadcastTime for the ignored avatar to 0
// so the AvatarMixer knows it'll have to send identity data about the ignored avatar
// to the ignorer if the ignorer unignores.
nodeData->setLastBroadcastTime(ignoredUUID, 0);
// Reset the lastBroadcastTime for the ignorer (FROM THE PERSPECTIVE OF THE IGNORED) to 0
// so the AvatarMixer knows it'll have to send identity data about the ignorer
// to the ignored if the ignorer unignores.
auto ignoredNode = nodeList->nodeWithUUID(ignoredUUID);
AvatarMixerClientData* ignoredNodeData = reinterpret_cast<AvatarMixerClientData*>(ignoredNode->getLinkedData());
ignoredNodeData->setLastBroadcastTime(senderNode->getUUID(), 0);
if (nodeList->nodeWithUUID(ignoredUUID)) {
// Reset the lastBroadcastTime for the ignored avatar to 0
// so the AvatarMixer knows it'll have to send identity data about the ignored avatar
// to the ignorer if the ignorer unignores.
nodeData->setLastBroadcastTime(ignoredUUID, 0);
// Reset the lastBroadcastTime for the ignorer (FROM THE PERSPECTIVE OF THE IGNORED) to 0
// so the AvatarMixer knows it'll have to send identity data about the ignorer
// to the ignored if the ignorer unignores.
auto ignoredNode = nodeList->nodeWithUUID(ignoredUUID);
AvatarMixerClientData* ignoredNodeData = reinterpret_cast<AvatarMixerClientData*>(ignoredNode->getLinkedData());
ignoredNodeData->setLastBroadcastTime(senderNode->getUUID(), 0);
}
if (addToIgnore) {
senderNode->addIgnoredNode(ignoredUUID);

View file

@ -67,15 +67,13 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) {
int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
int bytesSent = 0;
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray();
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
bytesSent += individualData.size();
identityPacket->write(individualData);
DependencyManager::get<NodeList>()->sendPacket(std::move(identityPacket), *destinationNode);
auto identityPackets = NLPacketList::create(PacketType::AvatarIdentity, QByteArray(), true, true);
identityPackets->write(individualData);
DependencyManager::get<NodeList>()->sendPacketList(std::move(identityPackets), *destinationNode);
_stats.numIdentityPackets++;
return bytesSent;
return individualData.size();
}
static const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
@ -265,8 +263,16 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
// make sure we haven't already sent this data from this sender to this receiver
// or that somehow we haven't sent
if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) {
++numAvatarsHeldBack;
shouldIgnore = true;
// don't ignore this avatar if we haven't sent any update for a long while
// in an effort to prevent other interfaces from deleting a stale avatar instance
uint64_t lastBroadcastTime = nodeData->getLastBroadcastTime(avatarNode->getUUID());
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
const uint64_t AVATAR_UPDATE_STALE = AVATAR_UPDATE_TIMEOUT - USECS_PER_SECOND;
if (lastBroadcastTime > otherNodeData->getIdentityChangeTimestamp() &&
lastBroadcastTime + AVATAR_UPDATE_STALE > startIgnoreCalculation) {
++numAvatarsHeldBack;
shouldIgnore = true;
}
} else if (lastSeqFromSender - lastSeqToReceiver > 1) {
// this is a skip - we still send the packet but capture the presence of the skip so we see it happening
++numAvatarsWithSkippedFrames;
@ -325,7 +331,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
_stats.overBudgetAvatars++;
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::NoData;
} else if (!isInView) {
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::NoData;
detail = PALIsOpen ? AvatarData::PALMinimum : AvatarData::MinimumData;
nodeData->incrementAvatarOutOfView();
} else {
detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO

View file

@ -49,7 +49,7 @@ private:
bool _stop { false };
};
// Slave pool for audio mixers
// Slave pool for avatar mixers
// AvatarMixerSlavePool is not thread-safe! It should be instantiated and used from a single thread.
class AvatarMixerSlavePool {
using Queue = tbb::concurrent_queue<SharedNodePointer>;

View file

@ -443,13 +443,8 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
(viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
viewFrustumChanged,
boundaryLevelAdjust, octreeSizeScale,
nodeData->getLastTimeBagEmpty(),
isFullScene, &nodeData->stats, _myServer->getJurisdiction(),
&nodeData->extraEncodeData,
nodeData->getUsesFrustum(),
nodeData);
viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
isFullScene, _myServer->getJurisdiction(), nodeData);
nodeData->copyCurrentViewFrustum(params.viewFrustum);
if (viewFrustumChanged) {
nodeData->copyLastKnownViewFrustum(params.lastViewFrustum);

View file

@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://github.com/ValveSoftware/openvr/archive/v1.0.3.zip
URL_MD5 b484b12901917cc739e40389583c8b0d
URL https://github.com/ValveSoftware/openvr/archive/v1.0.6.zip
URL_MD5 f6892cd3a3078f505d03b4297f5a1951
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -6,56 +6,60 @@
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
#
macro(SETUP_HIFI_CLIENT_SERVER_PLUGIN)
set(${TARGET_NAME}_SHARED 1)
setup_hifi_library(${ARGV})
if (NOT DEFINED SERVER_ONLY)
add_dependencies(interface ${TARGET_NAME})
endif()
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins")
set(${TARGET_NAME}_SHARED 1)
setup_hifi_library(${ARGV})
if (APPLE)
set(CLIENT_PLUGIN_PATH "${INTERFACE_BUNDLE_NAME}.app/Contents/PlugIns")
set(SERVER_PLUGIN_PATH "plugins")
else()
set(CLIENT_PLUGIN_PATH "plugins")
set(SERVER_PLUGIN_PATH "plugins")
endif()
if (NOT DEFINED SERVER_ONLY)
add_dependencies(interface ${TARGET_NAME})
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_GENERATOR STREQUAL "Unix Makefiles")
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/${CLIENT_PLUGIN_PATH}/")
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/${SERVER_PLUGIN_PATH}/")
elseif (APPLE)
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${CLIENT_PLUGIN_PATH}/")
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/$<CONFIGURATION>/${SERVER_PLUGIN_PATH}/")
else()
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${CLIENT_PLUGIN_PATH}/")
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/$<CONFIGURATION>/${SERVER_PLUGIN_PATH}/")
endif()
add_dependencies(assignment-client ${TARGET_NAME})
# create the destination for the client plugin binaries
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E make_directory
${CLIENT_PLUGIN_FULL_PATH}
)
# copy the client plugin binaries
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy
"$<TARGET_FILE:${TARGET_NAME}>"
${CLIENT_PLUGIN_FULL_PATH}
)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Plugins")
# create the destination for the server plugin binaries
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E make_directory
${SERVER_PLUGIN_FULL_PATH}
)
# copy the server plugin binaries
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy
"$<TARGET_FILE:${TARGET_NAME}>"
${SERVER_PLUGIN_FULL_PATH}
)
if (APPLE)
set(CLIENT_PLUGIN_PATH "${INTERFACE_BUNDLE_NAME}.app/Contents/PlugIns")
set(SERVER_PLUGIN_PATH "plugins")
else()
set(CLIENT_PLUGIN_PATH "plugins")
set(SERVER_PLUGIN_PATH "plugins")
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_GENERATOR STREQUAL "Unix Makefiles")
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/${CLIENT_PLUGIN_PATH}/")
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/${SERVER_PLUGIN_PATH}/")
elseif (APPLE)
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${CLIENT_PLUGIN_PATH}/")
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/$<CONFIGURATION>/${SERVER_PLUGIN_PATH}/")
else()
set(CLIENT_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/interface/$<CONFIGURATION>/${CLIENT_PLUGIN_PATH}/")
set(SERVER_PLUGIN_FULL_PATH "${CMAKE_BINARY_DIR}/assignment-client/$<CONFIGURATION>/${SERVER_PLUGIN_PATH}/")
endif()
# create the destination for the client plugin binaries
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E make_directory
${CLIENT_PLUGIN_FULL_PATH}
)
# copy the client plugin binaries
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy
"$<TARGET_FILE:${TARGET_NAME}>"
${CLIENT_PLUGIN_FULL_PATH}
)
# create the destination for the server plugin binaries
add_custom_command(
TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E make_directory
${SERVER_PLUGIN_FULL_PATH}
)
# copy the server plugin binaries
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy
"$<TARGET_FILE:${TARGET_NAME}>"
${SERVER_PLUGIN_FULL_PATH}
)
endmacro()

View file

@ -192,7 +192,7 @@ link_hifi_libraries(
shared octree ktx gpu gl gpu-gl procedural model render
recording fbx networking model-networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer ui auto-updater
render-utils entities-renderer avatars-renderer ui auto-updater
controllers plugins
ui-plugins display-plugins input-plugins
${NON_ANDROID_LIBRARIES}

View file

@ -1,7 +1,6 @@
import QtQuick 2.5
import QtQuick.Controls 1.4 as Controls
import "../../qml/menus"
import "../../qml/controls-uit"
import "../../qml/styles-uit"

View file

@ -2,6 +2,8 @@
"name": "Kinect to Standard",
"channels": [
{ "from": "Kinect.LeftHand", "to": "Standard.LeftHand" },
{ "from": "Kinect.RightHand", "to": "Standard.RightHand" }
{ "from": "Kinect.RightHand", "to": "Standard.RightHand" },
{ "from": "Kinect.LeftFoot", "to": "Standard.LeftFoot" },
{ "from": "Kinect.RightFoot", "to": "Standard.RightFoot" }
]
}

View file

@ -58,6 +58,9 @@
{ "from": "Standard.RT", "to": "Actions.RightHandClick" },
{ "from": "Standard.LeftHand", "to": "Actions.LeftHand" },
{ "from": "Standard.RightHand", "to": "Actions.RightHand" }
{ "from": "Standard.RightHand", "to": "Actions.RightHand" },
{ "from": "Standard.LeftFoot", "to": "Actions.LeftFoot" },
{ "from": "Standard.RightFoot", "to": "Actions.RightFoot" }
]
}

0
interface/resources/fonts/hifi-glyphs.ttf Executable file → Normal file
View file

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 576 576" style="enable-background:new 0 0 576 576;" xml:space="preserve">
<style type="text/css">
.st0{fill:#6D6E71;}
.st1{fill:#1398BB;}
</style>
<path class="st0" d="M498.5,272.1c0-1.8,0.2-3.6,0.4-5.4c-15.5,3.9-31,8-46.4,12.5c-3.9,1.1-7.8,3.5-10.6,6.4
c-10.1,10.2-19.7,21-29.6,31.6c-2.5-1.6-4.8-3-7-4.5c-35.4-23-70.9-45.8-106.2-69c-8.9-5.8-17.2-6.3-26.5-1.3
c-15.8,8.5-31.7,16.8-47.8,24.9c-2.3,1.1-5.7,0.1-8.5,0.1c0.3-2.8-0.3-6.3,1.1-8.3c7.6-10.6,15.5-21,23.8-31c3-3.7,7.3-7.6,11.6-8.7
c20.4-5.2,41.1-10.8,62.4-8.9c16.2,1.4,32.3,5,48.4,8c8.1,1.5,15.1,0.7,22.1-4.6c17.5-13.3,35.6-25.9,53.4-38.8
c1.6-1.2,3.9-2.2,4.5-3.9c1-2.7,2.1-6.8,0.8-8.4c-1.6-2-5.6-2.7-8.6-2.7c-1.9,0-3.8,2.2-5.6,3.5c-16.6,12-33.3,23.7-49.6,36.1
c-5.7,4.4-10.9,5.4-17.8,3.7c-27.4-7-55.1-11.2-83.2-4.6c-12.4,2.9-25.4,4.7-36.8,9.8c-7.4,3.3-13.6,4.7-21.4,3.7
c-10.3-1.4-19-4.9-27-11.9c-14.5-12.7-29.9-24.5-44.9-36.7c-1.6-1.3-3.3-3.3-4.9-3.3c-3,0-7,0.4-8.7,2.2c-1.4,1.5-0.6,5.6,0.2,8.3
c0.5,1.7,2.8,2.8,4.3,4.1c14.6,11.9,29.6,23.4,43.9,35.8c9.8,8.5,20,15.1,33.4,15.3c1.6,0,3.2,0.8,5.5,1.4
c-6.1,7.9-11.7,15.1-17.2,22.5c-7.3,9.7-7.7,18.5-1.1,26.4c6.6,8.1,16.2,9.6,26.7,4.1c15.3-7.9,30.8-15.7,45.9-24.1
c5.7-3.1,10-3,15.4,0.6c35.1,23,70.4,45.8,105.7,68.6c1.9,1.2,3.8,2.4,5.5,3.9c3.9,3.6,4.9,7.9,2.6,12.8c-2.3,4.9-6.3,7.1-11.5,6.2
c-2.8-0.5-5.5-1.9-8-3.3c-18-10.6-35.9-21.4-53.8-32.1c-2.1-1.3-4.4-3.3-6.5-3.2c-2.7,0.1-6.3,1.3-7.8,3.3c-1.2,1.6-0.3,5.5,0.9,7.7
c1,1.9,3.7,3.1,5.8,4.4c16.3,9.9,32.8,19.5,49,29.6c2.6,1.6,5.6,5.9,5.1,8.2c-0.7,3.1-4.3,6.9-7.2,7.6c-3.9,0.9-9,0.1-12.7-1.8
c-14-7.1-27.7-14.8-41.5-22.3c-1.8-1-3.7-2.7-5.4-2.5c-3.2,0.4-7.3,1-9,3.1c-1.2,1.5,0.5,5.9,1.9,8.6c0.7,1.5,3.2,2.3,5,3.2
c12.3,6.9,24.8,13.5,37,20.8c2.4,1.4,5.4,5.6,4.9,7.6c-0.8,3.1-4,7-7,7.8c-3.6,1-8.5,0.2-12.1-1.5c-10.4-4.7-20.5-10.2-30.7-15.4
c-7.4-3.7-12-3.4-13.9,1.4c-2.9,6.9,2.4,9.1,7,11.5c7.4,3.8,14.8,7.5,22.2,11.3c1.4,0.7,2.6,1.8,4.5,3.1c-16.9,8.5-32.4,0.9-48.5,0
c8.7-19.4,6.1-27.4-9-35.7c-2.5-8.3-3-15.8-6.7-20.9c-3.9-5.3-11-8.1-17.7-12.8c-0.2-9.3-5.8-16.9-16.1-20.6
c-10.6-3.9-19.5,0.2-27,7.7c-12.5-11.7-19.9-12.4-36.3-3.8c-4.3-5.7-9.3-11-12.9-17.2c-8.6-14.6-20.8-23.1-37.5-26.5
c-10.7-2.2-21-6.5-31.5-9.7c-0.4-0.1-0.9-0.3-1.3-0.4c0.3,0.8,0.5,1.7,0.6,2.5c0.2,2.3-0.6,5.7-2.3,7.4c-1.2,1.2-2.3,2.5-3.8,3.3
c0,0,0,0,0,0c0.7,0.3,1.4,0.6,2.2,0.8c1.3,0.4,2.6,0.8,3.8,1.2c13.9,4.4,27.8,8.8,41.7,13.3c2.1,0.7,4.5,1.5,5.8,3
c9,11.5,17.7,23.1,26.6,34.9c-2.9,4.2-5.7,8-8.1,12c-6,9.9-5.3,20.9,2.9,28.3c4.3,3.8,11.5,4.4,15.4,8.5c4.1,4.2,4.8,11.5,8.6,16.1
c6.1,7.6,15.2,7.6,23.9,6.2c9.1,13.6,17,16.6,32.2,12.8c10.4,11.1,21.4,14.3,30.7,7.1c5.3-4.1,10.1-4.4,16-3.3
c7.1,1.3,14.2,2.3,21.3,3.5c15.1,2.4,29,0,41.2-10c1.3-1.1,3.1-2,4.7-2.1c15.2-1.3,24.3-9.3,26.9-24.6c14.2-1.6,24.1-8.3,26.9-22.8
c1-0.2,1.6-0.5,2.3-0.5c17.2-1.8,26.5-13.8,24.5-31.1c-0.2-2,1-4.7,2.5-6.3c8.1-9,16.2-18.1,24.9-26.6c3.5-3.4,8.1-6.2,12.8-7.6
c13.9-4.2,28.1-7.6,42.1-11.7C500.9,278.6,498.4,275.6,498.5,272.1z M170.8,362.9c-3,4-7.6,4-11.5,1.3c-3.9-2.7-5.8-6.9-3.1-11.2
c4.6-7.4,9.8-14.5,15.1-21.5c1.3-1.8,4.1-2.5,5.2-3.2c7.9,0,12.5,6.9,9,12.8C181.2,348.7,176.1,355.9,170.8,362.9z M195.9,384.7
c-3.5,5.1-9.1,6.4-13.4,3.5c-4.5-3.1-5.3-8.3-1.7-13.6c9.1-13.6,18.5-27.1,27.6-40.6c2.9-4.3,7-6.3,11.5-4.2c2.8,1.3,4.6,4.6,6.9,7
c-1.6,3.5-2.4,5.9-3.7,7.8C214.1,358,205,371.4,195.9,384.7z M224.8,399.7c-1.4,2-4.3,2.9-5.6,3.7c-8.2,0.3-13.1-7.1-9.3-13.1
c7.2-11.3,14.8-22.4,22.7-33.3c2.9-4,7.7-4.8,12-1.7c4.1,3,5.6,7.3,2.7,11.8C240,378.1,232.5,389,224.8,399.7z M263.2,394.1
c-3.5,5.2-6.6,10.8-10.9,15.2c-1.8,1.8-6.7,2.3-9.2,1.2c-1.6-0.7-2.6-6.2-1.6-8.4c2.9-5.7,6.8-11.1,11-16c1.4-1.7,5.3-2.3,7.6-1.7
c2,0.5,3.5,3.4,5,5.1C264.2,391.6,263.9,393,263.2,394.1z"/>
<path class="st1" d="M288.4,507.4c-58.8,0-114-22.9-155.6-64.4c-41.5-41.5-64.4-96.8-64.4-155.6s22.9-114,64.4-155.6
c41.5-41.5,96.8-64.4,155.6-64.4s114,22.9,155.6,64.4c41.5,41.5,64.4,96.8,64.4,155.6s-22.9,114-64.4,155.6S347.1,507.4,288.4,507.4
z M288.4,80.8c-55.2,0-107,21.5-146.1,60.5s-60.5,90.9-60.5,146.1c0,55.2,21.5,107,60.5,146.1c39,39,90.9,60.5,146.1,60.5
c55.2,0,107-21.5,146.1-60.5c39-39,60.5-90.9,60.5-146.1c0-55.2-21.5-107-60.5-146.1C395.4,102.3,343.5,80.8,288.4,80.8z"/>
<path class="st1" d="M288.4,541.5c-67.9,0-131.7-26.4-179.7-74.4c-48-48-74.4-111.8-74.4-179.7s26.4-131.7,74.4-179.7
c48-48,111.8-74.4,179.7-74.4s131.7,26.4,179.7,74.4s74.4,111.8,74.4,179.7s-26.4,131.7-74.4,179.7S356.3,541.5,288.4,541.5z
M288.4,41.1c-65.8,0-127.6,25.6-174.1,72.1c-46.5,46.5-72.1,108.3-72.1,174.1S67.7,415,114.3,461.5
c46.5,46.5,108.3,72.1,174.1,72.1S416,508,462.5,461.5c46.5-46.5,72.1-108.3,72.1-174.1S509,159.8,462.5,113.3
C416,66.8,354.1,41.1,288.4,41.1z"/>
</svg>

After

(image error) Size: 4.8 KiB

Binary file not shown.

After

(image error) Size: 84 KiB

Binary file not shown.

After

(image error) Size: 87 KiB

Binary file not shown.

View file

@ -1,135 +1,85 @@
name = being_of_light
name = Jointy3
type = body+head
scale = 1
filename = being_of_light/being_of_light.fbx
texdir = being_of_light/textures
joint = jointRoot = Hips
filename = Jointy3/Jointy3.fbx
texdir = Jointy3/textures
joint = jointNeck = Neck
joint = jointLeftHand = LeftHand
joint = jointHead = HeadTop_End
joint = jointEyeRight = RightEye
joint = jointHead = Head
joint = jointRightHand = RightHand
joint = jointRoot = Hips
joint = jointLean = Spine
joint = jointEyeLeft = LeftEye
joint = jointRightHand = RightHand
joint = jointNeck = Head
joint = jointEyeRight = RightEye
freeJoint = LeftArm
freeJoint = LeftForeArm
freeJoint = RightArm
freeJoint = RightForeArm
bs = MouthFrown_L = Frown_Left = 1
bs = MouthLeft = Midmouth_Left = 1
bs = BrowsU_R = BrowsUp_Right = 1
bs = ChinUpperRaise = UpperLipUp_Right = 0.5
bs = ChinUpperRaise = UpperLipUp_Left = 0.5
bs = MouthSmile_R = Smile_Right = 1
bs = MouthDimple_L = Smile_Left = 0.25
bs = EyeBlink_L = Blink_Left = 1
bs = BrowsD_L = BrowsDown_Left = 1
bs = MouthFrown_R = Frown_Right = 1
bs = MouthDimple_R = Smile_Right = 0.25
bs = Sneer = Squint_Right = 0.5
bs = Sneer = Squint_Left = 0.5
bs = Sneer = NoseScrunch_Right = 0.75
bs = Sneer = NoseScrunch_Left = 0.75
bs = EyeSquint_L = Squint_Left = 1
bs = EyeBlink_R = Blink_Right = 1
bs = JawLeft = JawRotateY_Left = 0.5
bs = BrowsD_R = BrowsDown_Right = 1
bs = EyeSquint_R = Squint_Right = 1
bs = Puff = CheekPuff_Right = 1
bs = Puff = CheekPuff_Left = 1
bs = LipsUpperClose = UpperLipIn = 1
bs = JawOpen = MouthOpen = 0.69999999999999996
bs = LipsUpperUp = UpperLipUp_Right = 0.69999999999999996
bs = LipsUpperUp = UpperLipUp_Left = 0.69999999999999996
bs = LipsLowerDown = LowerLipDown_Right = 0.69999999999999996
bs = LipsLowerDown = LowerLipDown_Left = 0.69999999999999996
bs = LipsLowerOpen = LowerLipOut = 1
bs = EyeOpen_L = EyesWide_Left = 1
bs = LipsPucker = MouthNarrow_Right = 1
bs = LipsPucker = MouthNarrow_Left = 1
bs = EyeOpen_R = EyesWide_Right = 1
bs = JawRight = Jaw_Right = 1
bs = MouthRight = Midmouth_Right = 1
bs = ChinLowerRaise = Jaw_Up = 1
bs = LipsUpperOpen = UpperLipOut = 1
bs = BrowsU_C = BrowsUp_Right = 1
bs = BrowsU_C = BrowsUp_Left = 1
bs = JawFwd = JawForeward = 1
bs = BrowsU_L = BrowsUp_Left = 1
bs = MouthSmile_L = Smile_Left = 1
bs = LipsLowerClose = LowerLipIn = 1
bs = LipsFunnel = TongueUp = 1
bs = LipsFunnel = MouthWhistle_NarrowAdjust_Right = 0.5
bs = LipsFunnel = MouthWhistle_NarrowAdjust_Left = 0.5
bs = LipsFunnel = MouthNarrow_Right = 1
bs = LipsFunnel = MouthNarrow_Left = 1
bs = LipsFunnel = Jaw_Down = 0.35999999999999999
bs = LipsFunnel = JawForeward = 0.39000000000000001
jointIndex = LeftHandIndex1 = 50
jointIndex = LeftHandIndex2 = 51
jointIndex = LeftHandIndex3 = 52
jointIndex = LeftHandIndex4 = 53
jointIndex = Spine1 = 12
jointIndex = Spine2 = 13
jointIndex = RightHandThumb1 = 18
jointIndex = RightHandThumb2 = 19
jointIndex = RightHandThumb3 = 20
jointIndex = RightHandThumb4 = 21
jointIndex = LeftFoot = 8
jointIndex = LeftForeArm = 40
jointIndex = Neck = 62
jointIndex = Head = 63
jointIndex = Hips = 0
jointIndex = RightHandPinky1 = 30
jointIndex = RightHandPinky2 = 31
jointIndex = RightHandPinky3 = 32
jointIndex = RightHandPinky4 = 33
jointIndex = RightLeg = 2
jointIndex = RightForeArm = 16
jointIndex = LeftHandRing1 = 46
jointIndex = LeftHandRing2 = 47
jointIndex = LeftHandRing3 = 48
jointIndex = LeftHandRing4 = 49
jointIndex = LeftHandThumb1 = 54
jointIndex = LeftHandThumb2 = 55
jointIndex = LeftHandThumb3 = 56
jointIndex = LeftHandThumb4 = 57
jointIndex = HeadTop_End = 66
jointIndex = LeftUpLeg = 6
jointIndex = LeftToeBase = 9
jointIndex = LeftHandPinky1 = 42
jointIndex = LeftHandPinky2 = 43
jointIndex = LeftHandPinky3 = 44
jointIndex = LeftHandPinky4 = 45
jointIndex = LeftLeg = 7
jointIndex = RightEye = 65
jointIndex = RightHand = 17
jointIndex = RightToeBase = 4
jointIndex = RightUpLeg = 1
jointIndex = RightArm = 15
jointIndex = RightHandRing1 = 26
jointIndex = RightHandRing2 = 27
jointIndex = RightHandRing3 = 28
jointIndex = RightHandRing4 = 29
jointIndex = RightHandIndex1 = 22
jointIndex = RightHandIndex2 = 23
jointIndex = RightHandIndex3 = 24
jointIndex = RightHandIndex4 = 25
jointIndex = LeftToe_End = 10
jointIndex = LeftHandMiddle1 = 58
jointIndex = LeftHandMiddle2 = 59
jointIndex = LeftHandMiddle3 = 60
jointIndex = LeftShoulder = 38
jointIndex = LeftHandMiddle4 = 61
jointIndex = RightFoot = 3
jointIndex = LeftHand = 41
jointIndex = RightHandMiddle1 = 34
jointIndex = RightHandMiddle2 = 35
jointIndex = RightHandMiddle3 = 36
jointIndex = LeftHandIndex3 = 56
jointIndex = Hips = 0
jointIndex = LeftHandRing2 = 47
jointIndex = LeftHandThumb3 = 60
jointIndex = RightShoulder = 14
jointIndex = LeftEye = 64
jointIndex = RightHandMiddle4 = 37
jointIndex = Body = 67
jointIndex = LeftArm = 39
jointIndex = RightHandRing1 = 30
jointIndex = RightHandRing3 = 32
jointIndex = LeftHandPinky4 = 45
jointIndex = LeftHandRing1 = 46
jointIndex = LeftFoot = 8
jointIndex = RightHandIndex2 = 23
jointIndex = RightToeBase = 4
jointIndex = RightHandMiddle4 = 29
jointIndex = RightHandPinky4 = 37
jointIndex = LeftToe_End = 10
jointIndex = RightEye = 66
jointIndex = RightHandPinky2 = 35
jointIndex = RightHandRing2 = 31
jointIndex = LeftHand = 41
jointIndex = RightToe_End = 5
jointIndex = LeftEye = 65
jointIndex = LeftHandThumb2 = 59
jointIndex = pCylinder73Shape1 = 67
jointIndex = LeftShoulder = 38
jointIndex = LeftHandIndex2 = 55
jointIndex = RightForeArm = 16
jointIndex = LeftHandMiddle2 = 51
jointIndex = RightHandRing4 = 33
jointIndex = LeftLeg = 7
jointIndex = LeftHandThumb4 = 61
jointIndex = LeftForeArm = 40
jointIndex = HeadTop_End = 64
jointIndex = RightHandPinky1 = 34
jointIndex = RightHandIndex1 = 22
jointIndex = LeftHandIndex1 = 54
jointIndex = RightLeg = 2
jointIndex = RightHandIndex4 = 25
jointIndex = Neck = 62
jointIndex = LeftHandMiddle1 = 50
jointIndex = RightHandPinky3 = 36
jointIndex = LeftHandPinky2 = 43
jointIndex = RightHandMiddle3 = 28
jointIndex = RightHandThumb4 = 21
jointIndex = LeftUpLeg = 6
jointIndex = RightFoot = 3
jointIndex = LeftHandThumb1 = 58
jointIndex = LeftArm = 39
jointIndex = RightHandMiddle1 = 26
jointIndex = LeftHandRing3 = 48
jointIndex = LeftHandMiddle4 = 53
jointIndex = RightUpLeg = 1
jointIndex = RightHandMiddle2 = 27
jointIndex = LeftToeBase = 9
jointIndex = RightHandThumb2 = 19
jointIndex = Spine2 = 13
jointIndex = Spine = 11
jointIndex = LeftHandRing4 = 49
jointIndex = Head = 63
jointIndex = LeftHandPinky3 = 44
jointIndex = LeftHandPinky1 = 42
jointIndex = RightHandThumb1 = 18
jointIndex = LeftHandIndex4 = 57
jointIndex = LeftHandMiddle3 = 52
jointIndex = RightHandIndex3 = 24
jointIndex = Spine1 = 12
jointIndex = RightArm = 15
jointIndex = RightHandThumb3 = 20

View file

@ -2,6 +2,7 @@ import QtQuick 2.5
import QtQuick.Controls 1.2
import QtWebChannel 1.0
import QtWebEngine 1.2
import FileTypeProfile 1.0
import "controls-uit"
import "styles" as HifiStyles
@ -33,6 +34,10 @@ ScrollingWindow {
addressBar.text = webview.url
}
function setProfile(profile) {
webview.profile = profile;
}
function showPermissionsBar(){
permissionsContainer.visible=true;
}
@ -212,6 +217,11 @@ ScrollingWindow {
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
profile: FileTypeProfile {
id: webviewProfile
storageName: "qmlWebEngine"
}
webChannel.registeredObjects: [eventBridgeWrapper]

View file

@ -130,7 +130,7 @@ Item {
id: pingCol
spacing: 4; x: 4; y: 4;
StatText {
text: "Audio ping: " + root.audioPing
text: "Audio ping/loss: " + root.audioPing + "/" + root.audioPacketLoss + "%"
}
StatText {
text: "Avatar ping: " + root.avatarPing

View file

@ -0,0 +1,134 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebChannel 1.0
import QtWebEngine 1.2
import "controls"
import "styles" as HifiStyles
import "styles-uit"
import "windows"
import HFTabletWebEngineProfile 1.0
Item {
id: root
HifiConstants { id: hifi }
HifiStyles.HifiConstants { id: hifistyles }
//width: parent.width
height: 600
property variant permissionsBar: {'securityOrigin':'none','feature':'none'}
property alias url: webview.url
property WebEngineView webView: webview
property alias eventBridge: eventBridgeWrapper.eventBridge
property bool canGoBack: webview.canGoBack
property bool canGoForward: webview.canGoForward
signal loadingChanged(int status)
x: 0
y: 0
function setProfile(profile) {
webview.profile = profile;
}
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
WebEngineView {
id: webview
objectName: "webEngineView"
x: 0
y: 0
width: parent.width
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
profile: HFTabletWebEngineProfile {
id: webviewTabletProfile
storageName: "qmlTabletWebEngine"
}
property string userScriptUrl: ""
// creates a global EventBridge object.
WebEngineScript {
id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// detects when to raise and lower virtual keyboard
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
// User script.
WebEngineScript {
id: userScript
sourceUrl: webview.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
property string newUrl: ""
webChannel.registeredObjects: [eventBridgeWrapper]
Component.onCompleted: {
// Ensure the JS from the web-engine makes it to our logging
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
});
webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
web.address = url;
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
onLoadingChanged: {
keyboardRaised = false;
punctuationMode = false;
keyboard.resetShiftMode(false);
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
urlAppend(loadRequest.url.toString())
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
}
}
}
}
onNewViewRequested: {
request.openIn(webView);
}
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_L:
if (event.modifiers == Qt.ControlModifier) {
event.accepted = true
addressBar.selectAll()
addressBar.forceActiveFocus()
}
break;
}
}
}

View file

@ -19,12 +19,11 @@ Original.CheckBox {
property int colorScheme: hifi.colorSchemes.light
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
property bool isRedCheck: false
property int boxSize: 14
readonly property int boxRadius: 3
readonly property int checkSize: Math.max(boxSize - 8, 10)
readonly property int checkRadius: 2
activeFocusOnPress: true
style: CheckBoxStyle {
@ -37,6 +36,7 @@ Original.CheckBox {
border.color: pressed || hovered
? hifi.colors.checkboxCheckedBorder
: (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish)
gradient: Gradient {
GradientStop {
position: 0.2
@ -68,9 +68,9 @@ Original.CheckBox {
height: checkSize
radius: checkRadius
anchors.centerIn: parent
color: hifi.colors.checkboxChecked
color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked
border.width: 2
border.color: hifi.colors.checkboxCheckedBorder
border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder
visible: checked && !pressed || !checked && pressed
}
@ -90,7 +90,7 @@ Original.CheckBox {
label: Label {
text: control.text
colorScheme: checkBox.colorScheme
x: checkBox.boxSize / 2
x: 2
wrapMode: Text.Wrap
enabled: checkBox.enabled
}

View file

@ -216,7 +216,7 @@ FocusScope {
anchors.leftMargin: hifi.dimensions.textPadding
anchors.verticalCenter: parent.verticalCenter
id: popupText
text: listView.model[index] ? listView.model[index] : ""
text: listView.model[index] ? listView.model[index] : (listView.model.get(index).text ? listView.model.get(index).text : "")
size: hifi.fontSizes.textFieldInput
color: hifi.colors.baseGray
}

View file

@ -48,11 +48,12 @@ TableView {
HiFiGlyphs {
id: titleSort
text: sortIndicatorOrder == Qt.AscendingOrder ? hifi.glyphs.caratUp : hifi.glyphs.caratDn
color: hifi.colors.baseGrayHighlight
color: hifi.colors.darkGray
opacity: 0.6;
size: hifi.fontSizes.tableHeadingIcon
anchors {
left: titleText.right
leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 3 : 0)
leftMargin: -hifi.fontSizes.tableHeadingIcon / 3 - (centerHeaderText ? 5 : 0)
right: parent.right
rightMargin: hifi.dimensions.tablePadding
verticalCenter: titleText.verticalCenter
@ -89,7 +90,6 @@ TableView {
Rectangle {
color: "#00000000"
anchors { fill: parent; margins: -2 }
radius: hifi.dimensions.borderRadius
border.color: isLightColorScheme ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight
border.width: 2
}

View file

@ -0,0 +1,199 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebEngine 1.2
import QtWebChannel 1.0
import "../controls-uit" as HiFiControls
import "../styles" as HifiStyles
import "../styles-uit"
import HFWebEngineProfile 1.0
import HFTabletWebEngineProfile 1.0
import "../"
Item {
id: web
width: parent.width
height: parent.height
property var parentStackItem: null
property int headerHeight: 38
property string url
property string address: url //for compatibility
property string scriptURL
property alias eventBridge: eventBridgeWrapper.eventBridge
property bool keyboardEnabled: HMD.active
property bool keyboardRaised: false
property bool punctuationMode: false
property bool isDesktop: false
property WebEngineView view: loader.currentView
property int currentPage: -1 // used as a model for repeater
property alias pagesModel: pagesModel
Row {
id: buttons
HifiConstants { id: hifi }
HifiStyles.HifiConstants { id: hifistyles }
height: headerHeight
spacing: 4
anchors.top: parent.top
anchors.topMargin: 8
anchors.left: parent.left
anchors.leftMargin: 8
HiFiGlyphs {
id: back;
enabled: currentPage >= 0
text: hifi.glyphs.backward
color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: goBack() }
}
HiFiGlyphs {
id: forward;
enabled: currentPage < pagesModel.count - 1
text: hifi.glyphs.forward
color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: goForward() }
}
HiFiGlyphs {
id: reload;
enabled: view != null;
text: (view !== null && view.loading) ? hifi.glyphs.close : hifi.glyphs.reload
color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: reloadPage(); }
}
}
TextField {
id: addressBar
height: 30
anchors.right: parent.right
anchors.rightMargin: 8
anchors.left: buttons.right
anchors.leftMargin: 0
anchors.verticalCenter: buttons.verticalCenter
focus: true
text: address
Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i")
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true;
if (text.indexOf("http") != 0) {
text = "http://" + text;
}
//root.hidePermissionsBar();
web.keyboardRaised = false;
gotoPage(text);
break;
}
}
}
ListModel {
id: pagesModel
onCountChanged: {
currentPage = count - 1
}
}
function goBack() {
if (currentPage > 0) {
currentPage--;
} else if (parentStackItem) {
parentStackItem.pop();
}
}
function goForward() {
if (currentPage < pagesModel.count - 1) {
currentPage++;
}
}
function gotoPage(url) {
urlAppend(url)
}
function reloadPage() {
view.reloadAndBypassCache()
view.setActiveFocusOnPress(true);
view.setEnabled(true);
}
function urlAppend(url) {
var lurl = decodeURIComponent(url)
if (lurl[lurl.length - 1] !== "/")
lurl = lurl + "/"
if (currentPage === -1 || pagesModel.get(currentPage).webUrl !== lurl) {
pagesModel.append({webUrl: lurl})
}
}
onCurrentPageChanged: {
if (currentPage >= 0 && currentPage < pagesModel.count && loader.item !== null) {
loader.item.url = pagesModel.get(currentPage).webUrl
web.url = loader.item.url
web.address = loader.item.url
}
}
onUrlChanged: {
gotoPage(url)
}
QtObject {
id: eventBridgeWrapper
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
Loader {
id: loader
property WebEngineView currentView: null
width: parent.width
height: parent.height - web.headerHeight
asynchronous: true
anchors.top: buttons.bottom
active: false
source: "../TabletBrowser.qml"
onStatusChanged: {
if (loader.status === Loader.Ready) {
currentView = item.webView
item.webView.userScriptUrl = web.scriptURL
if (currentPage >= 0) {
//we got something to load already
item.url = pagesModel.get(currentPage).webUrl
web.address = loader.item.url
}
}
}
}
Component.onCompleted: {
web.isDesktop = (typeof desktop !== "undefined");
address = url;
loader.active = true
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_L:
if (event.modifiers == Qt.ControlModifier) {
event.accepted = true
addressBar.selectAll()
addressBar.forceActiveFocus()
}
break;
}
}
}

View file

@ -0,0 +1,22 @@
//
// WebEntityView.qml
//
// Created by Kunal Gosar on 16 March 2017
// 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
//
import QtQuick 2.5
import "."
import FileTypeProfile 1.0
WebView {
viewProfile: FileTypeProfile {
id: webviewProfile
storageName: "qmlWebEngine"
}
urlTag: "noDownload=true";
}

View file

@ -8,6 +8,9 @@ Item {
property alias url: root.url
property alias scriptURL: root.userScriptUrl
property alias eventBridge: eventBridgeWrapper.eventBridge
property alias canGoBack: root.canGoBack;
property var goBack: root.goBack;
property alias urlTag: root.urlTag
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
property bool punctuationMode: false
@ -25,6 +28,8 @@ Item {
WebChannel.id: "eventBridgeWrapper"
property var eventBridge;
}
property alias viewProfile: root.profile
WebEngineView {
id: root
@ -64,6 +69,8 @@ Item {
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
property string urlTag: "noDownload=false";
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
@ -92,6 +99,7 @@ Item {
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
var url = loadRequest.url.toString();
url = (url.indexOf("?") >= 0) ? url + urlTag : url + "?" + urlTag;
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
@ -101,11 +109,11 @@ Item {
}
onNewViewRequested:{
// desktop is not defined for web-entities
if (desktop) {
var component = Qt.createComponent("../Browser.qml");
var newWindow = component.createObject(desktop);
request.openIn(newWindow.webView);
// desktop is not defined for web-entities or tablet
if (typeof desktop !== "undefined") {
desktop.openBrowserWindow(request, profile);
} else {
console.log("onNewViewRequested: desktop not defined");
}
}
}

View file

@ -490,6 +490,13 @@ FocusScope {
desktop.forceActiveFocus();
}
function openBrowserWindow(request, profile) {
var component = Qt.createComponent("../Browser.qml");
var newWindow = component.createObject(desktop);
newWindow.webView.profile = profile;
request.openIn(newWindow.webView);
}
FocusHack { id: focusHack; }
Rectangle {

View file

@ -0,0 +1,32 @@
//
// TabletConnectionFailureDialog.qml
//
// Created by Vlad Stelmahovsky on 29 Mar 2017
// 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
//
import Hifi 1.0
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
Item {
Component.onCompleted: {
var object = tabletRoot.messageBox({
icon: OriginalDialogs.StandardIcon.Warning,
buttons: OriginalDialogs.StandardButton.Ok,
defaultButton: OriginalDialogs.StandardButton.NoButton,
title: "No Connection",
text: "Unable to connect to this domain. Click the 'GO TO' button on the toolbar to visit another domain."
});
object.selected.connect(function(button) {
if (button === OriginalDialogs.StandardButton.Ok) {
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet.gotoHomeScreen()
}
});
}
}

View file

@ -23,7 +23,7 @@ TabletModalWindow {
property var eventBridge;
signal sendToScript(var message);
property bool isHMD: false
property bool gotoPreviousApp: false;
color: hifi.colors.baseGray
property int colorScheme: hifi.colorSchemes.dark
@ -66,7 +66,14 @@ TabletModalWindow {
HifiConstants { id: hifi }
onCanceled: {
loginDialogRoot.Stack.view.pop()
if (loginDialogRoot.Stack.view) {
loginDialogRoot.Stack.view.pop();
} else if (gotoPreviousApp) {
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet.returnToPreviousApp();
} else {
Tablet.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen();
}
}
LoginDialog {

View file

@ -127,7 +127,7 @@ Rectangle {
text: hifi.glyphs.mic
color: hifi.colors.primaryHighlight
anchors.verticalCenter: parent.verticalCenter
font.pointSize: 27
size: 32
}
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter
@ -182,7 +182,7 @@ Rectangle {
text: hifi.glyphs.unmuted
color: hifi.colors.primaryHighlight
anchors.verticalCenter: parent.verticalCenter
font.pointSize: 27
size: 32
}
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter

View file

@ -243,12 +243,7 @@ Rectangle {
}
}
}
DropShadow {
anchors.fill: actionIcon
radius: 8.0
color: "#80000000"
source: actionIcon
}
MouseArea {
id: messageArea;
width: rectIcon.width;

View file

@ -0,0 +1,180 @@
//
// ComboDialog.qml
// qml/hifi
//
// Created by Zach Fox on 3/31/2017
// 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
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../styles-uit"
import "../controls-uit"
Item {
property var dialogTitleText : "";
property var optionTitleText: "";
property var optionBodyText: "";
property var optionValues: [];
property var selectedOptionIndex: 0;
property var callbackFunction;
property int dialogWidth;
property int dialogHeight;
property int comboOptionTextSize: 18;
FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; }
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
visible: false;
id: combo;
anchors.fill: parent;
onVisibleChanged: {
populateComboListViewModel();
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
onClicked: {
combo.visible = false;
}
}
Rectangle {
id: dialogBackground;
anchors.fill: parent;
color: "black";
opacity: 0.5;
}
Rectangle {
id: dialogContainer;
color: "white";
anchors.centerIn: dialogBackground;
width: combo.dialogWidth;
height: combo.dialogHeight;
RalewayRegular {
id: dialogTitle;
text: combo.dialogTitleText;
anchors.top: parent.top;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.leftMargin: 20;
size: 24;
color: 'black';
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignTop;
}
HiFiGlyphs {
id: closeGlyphButton;
text: hifi.glyphs.close;
size: 32;
anchors.verticalCenter: dialogTitle.verticalCenter;
anchors.right: parent.right;
anchors.rightMargin: 20;
MouseArea {
anchors.fill: closeGlyphButton;
hoverEnabled: true;
onEntered: {
parent.text = hifi.glyphs.closeInverted;
}
onExited: {
parent.text = hifi.glyphs.close;
}
onClicked: {
combo.visible = false;
}
}
}
ListModel {
id: comboListViewModel;
}
ListView {
id: comboListView;
anchors.top: dialogTitle.bottom;
anchors.topMargin: 20;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
clip: true;
model: comboListViewModel;
delegate: comboListViewDelegate;
Component {
id: comboListViewDelegate;
Rectangle {
id: comboListViewItemContainer;
// Size
height: optionTitle.height + optionBody.height + 20;
width: dialogContainer.width;
color: selectedOptionIndex === index ? '#cee6ff' : 'white';
Rectangle {
id: comboOptionSelected;
color: selectedOptionIndex == index ? hifi.colors.blueAccent : 'white';
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.top: parent.top;
anchors.topMargin: 20;
width: 25;
height: width;
radius: width;
border.width: 3;
border.color: selectedOptionIndex === index ? hifi.colors.blueHighlight: hifi.colors.lightGrayText;
}
RalewaySemiBold {
id: optionTitle;
text: titleText;
anchors.top: parent.top;
anchors.topMargin: 7;
anchors.left: comboOptionSelected.right;
anchors.leftMargin: 10;
anchors.right: parent.right;
anchors.rightMargin: 10;
height: 30;
size: comboOptionTextSize;
wrapMode: Text.WordWrap;
}
RalewayRegular {
id: optionBody;
text: bodyText;
anchors.top: optionTitle.bottom;
anchors.left: comboOptionSelected.right;
anchors.leftMargin: 25;
anchors.right: parent.right;
anchors.rightMargin: 10;
size: comboOptionTextSize;
wrapMode: Text.WordWrap;
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true;
onEntered: comboListViewItemContainer.color = hifi.colors.blueHighlight
onExited: comboListViewItemContainer.color = selectedOptionIndex === index ? '#cee6ff' : 'white';
onClicked: {
callbackFunction(optionValue);
combo.visible = false;
}
}
}
}
}
}
function populateComboListViewModel() {
comboListViewModel.clear();
optionTitleText.forEach(function(titleText, index) {
comboListViewModel.insert(index, {"titleText": titleText, "bodyText": optionBodyText[index], "optionValue": optionValues[index]});
});
}
}

View file

@ -85,6 +85,28 @@ Item {
wrapMode: Text.WordWrap
textFormat: Text.StyledText
}
HiFiGlyphs {
id: closeGlyphButton
text: hifi.glyphs.close
size: headerTextPixelSize
anchors.top: parent.top
anchors.topMargin: -20
anchors.right: parent.right
anchors.rightMargin: -25
MouseArea {
anchors.fill: closeGlyphButton
hoverEnabled: true
onEntered: {
parent.text = hifi.glyphs.closeInverted;
}
onExited: {
parent.text = hifi.glyphs.close;
}
onClicked: {
letterbox.visible = false;
}
}
}
}
// Popup Text
Text {

View file

@ -14,384 +14,562 @@ import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
import "../styles-uit"
import "../controls-uit" as HifiControls
import "toolbars"
// references Users, UserActivityLogger, MyAvatar, Vec3, Quat, AddressManager from root context
Item {
id: thisNameCard
// Anchors
anchors {
verticalCenter: parent.verticalCenter
leftMargin: 10
rightMargin: 10
}
// Size
width: isMyCard ? pal.myCardWidth - anchors.leftMargin : pal.nearbyNameCardWidth;
height: isMyCard ? pal.myCardHeight : pal.rowHeight;
anchors.left: parent.left
anchors.leftMargin: 5
anchors.top: parent.top;
// Properties
property string profileUrl: "";
property string defaultBaseUrl: AddressManager.metaverseServerUrl;
property string connectionStatus : ""
property string uuid: ""
property string displayName: ""
property string userName: ""
property real displayNameTextPixelSize: 18
property int usernameTextHeight: 12
property int usernameTextPixelSize: 14
property real audioLevel: 0.0
property real avgAudioLevel: 0.0
property bool isMyCard: false
property bool selected: false
property bool isAdmin: false
property bool currentlyEditingDisplayName: false
/* User image commented out for now - will probably be re-introduced later.
Column {
property bool isPresent: true
property string placeName: ""
property string profilePicBorderColor: (connectionStatus == "connection" ? hifi.colors.indigoAccent : (connectionStatus == "friend" ? hifi.colors.greenHighlight : "transparent"))
property alias avImage: avatarImage
Item {
id: avatarImage
visible: profileUrl !== "" && userName !== "";
// Size
height: parent.height
width: height
height: isMyCard ? 70 : 42;
width: visible ? height : 0;
anchors.top: parent.top;
anchors.topMargin: isMyCard ? 0 : 8;
anchors.left: parent.left
clip: true
Image {
id: userImage
source: "../../icons/defaultNameCardUser.png"
source: profileUrl !== "" ? ((0 === profileUrl.indexOf("http")) ? profileUrl : (defaultBaseUrl + profileUrl)) : "";
mipmap: true;
// Anchors
width: parent.width
height: parent.height
anchors.fill: parent
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Item {
width: userImage.width;
height: userImage.height;
Rectangle {
anchors.centerIn: parent;
width: userImage.width; // This works because userImage is square
height: width;
radius: width;
}
}
}
}
AnimatedImage {
source: "../../icons/profilePicLoading.gif"
anchors.fill: parent;
visible: userImage.status != Image.Ready;
}
StateImage {
id: infoHoverImage;
visible: false;
imageURL: "../../images/info-icon-2-state.svg";
size: 32;
buttonState: 1;
anchors.centerIn: parent;
}
MouseArea {
anchors.fill: parent
enabled: (selected && activeTab == "nearbyTab") || isMyCard;
hoverEnabled: enabled
onClicked: {
userInfoViewer.url = defaultBaseUrl + "/users/" + userName;
userInfoViewer.visible = true;
}
onEntered: infoHoverImage.visible = true;
onExited: infoHoverImage.visible = false;
}
}
*/
Item {
id: textContainer
// Colored border around avatarImage
Rectangle {
id: avatarImageBorder;
visible: avatarImage.visible;
anchors.verticalCenter: avatarImage.verticalCenter;
anchors.horizontalCenter: avatarImage.horizontalCenter;
width: avatarImage.width + border.width;
height: avatarImage.height + border.width;
color: "transparent"
radius: avatarImage.height;
border.color: profilePicBorderColor;
border.width: 4;
}
// DisplayName field for my card
Rectangle {
id: myDisplayName
visible: isMyCard
// Size
width: parent.width - /*avatarImage.width - parent.spacing - */parent.anchors.leftMargin - parent.anchors.rightMargin
height: selected || isMyCard ? childrenRect.height : childrenRect.height - 15
anchors.verticalCenter: parent.verticalCenter
// DisplayName field for my card
Rectangle {
id: myDisplayName
visible: isMyCard
// Size
width: parent.width + 70
height: 35
// Anchors
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: -10
// Style
color: hifi.colors.textFieldLightBackground
border.color: hifi.colors.blueHighlight
border.width: 0
TextInput {
id: myDisplayNameText
// Properties
text: thisNameCard.displayName
maximumLength: 256
clip: true
// Size
width: parent.width
height: parent.height
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
anchors.right: parent.right
anchors.rightMargin: editGlyph.width + editGlyph.anchors.rightMargin
// Style
color: hifi.colors.darkGray
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
font.family: firaSansSemiBold.name
font.pixelSize: displayNameTextPixelSize
selectionColor: hifi.colors.blueHighlight
selectedTextColor: "black"
// Text Positioning
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignLeft
// Signals
onEditingFinished: {
pal.sendToScript({method: 'displayNameUpdate', params: text})
cursorPosition = 0
focus = false
myDisplayName.border.width = 0
color = hifi.colors.darkGray
currentlyEditingDisplayName = false
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onClicked: {
myDisplayName.border.width = 1
myDisplayNameText.focus ? myDisplayNameText.cursorPosition = myDisplayNameText.positionAt(mouseX, mouseY, TextInput.CursorOnCharacter) : myDisplayNameText.selectAll();
myDisplayNameText.focus = true
myDisplayNameText.color = "black"
currentlyEditingDisplayName = true
}
onDoubleClicked: {
myDisplayNameText.selectAll();
myDisplayNameText.focus = true;
currentlyEditingDisplayName = true
}
onEntered: myDisplayName.color = hifi.colors.lightGrayText
onExited: myDisplayName.color = hifi.colors.textFieldLightBackground
}
// Edit pencil glyph
HiFiGlyphs {
id: editGlyph
text: hifi.glyphs.editPencil
// Text Size
size: displayNameTextPixelSize*1.5
// Anchors
anchors.right: parent.right
anchors.rightMargin: 5
anchors.verticalCenter: parent.verticalCenter
// Style
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: hifi.colors.baseGray
}
}
// Spacer for DisplayName for my card
Item {
id: myDisplayNameSpacer
width: 1
height: 4
// Anchors
anchors.top: myDisplayName.bottom
}
// DisplayName container for others' cards
Item {
id: displayNameContainer
visible: !isMyCard
// Size
width: parent.width
height: displayNameTextPixelSize + 4
// Anchors
anchors.top: parent.top
anchors.left: parent.left
// DisplayName Text for others' cards
FiraSansSemiBold {
id: displayNameText
// Properties
text: thisNameCard.displayName
elide: Text.ElideRight
// Size
width: isAdmin ? Math.min(displayNameTextMetrics.tightBoundingRect.width + 8, parent.width - adminLabelText.width - adminLabelQuestionMark.width + 8) : parent.width
// Anchors
anchors.top: parent.top
anchors.left: parent.left
// Text Size
size: displayNameTextPixelSize
// Text Positioning
verticalAlignment: Text.AlignVCenter
// Style
color: hifi.colors.darkGray
}
TextMetrics {
id: displayNameTextMetrics
font: displayNameText.font
text: displayNameText.text
}
// "ADMIN" label for other users' cards
RalewaySemiBold {
id: adminLabelText
visible: isAdmin
text: "ADMIN"
// Text size
size: displayNameText.size - 4
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: displayNameText.right
// Style
font.capitalization: Font.AllUppercase
color: hifi.colors.redHighlight
// Alignment
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
}
// This Rectangle refers to the [?] popup button next to "ADMIN"
Item {
id: adminLabelQuestionMark
visible: isAdmin
// Size
width: 20
height: displayNameText.height
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: adminLabelText.right
RalewayRegular {
id: adminLabelQuestionMarkText
text: "[?]"
size: adminLabelText.size
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: letterbox(hifi.glyphs.question,
"Domain Admin",
"This user is an admin on this domain. Admins can <b>Silence</b> and <b>Ban</b> other users at their discretion - so be extra nice!")
onEntered: adminLabelQuestionMarkText.color = "#94132e"
onExited: adminLabelQuestionMarkText.color = hifi.colors.redHighlight
}
}
}
// UserName Text
FiraSansRegular {
id: userNameText
width: parent.width - avatarImage.width - anchors.leftMargin - anchors.rightMargin*2;
height: 40
// Anchors
anchors.top: avatarImage.top
anchors.left: avatarImage.right
anchors.leftMargin: avatarImage.visible ? 5 : 0;
anchors.rightMargin: 5;
// Style
color: hifi.colors.textFieldLightBackground
border.color: hifi.colors.blueHighlight
border.width: 0
TextInput {
id: myDisplayNameText
// Properties
text: thisNameCard.userName
elide: Text.ElideRight
visible: thisNameCard.displayName
text: thisNameCard.displayName
maximumLength: 256
clip: true
// Size
width: parent.width
height: parent.height
// Anchors
anchors.top: isMyCard ? myDisplayNameSpacer.bottom : displayNameContainer.bottom
// Text Size
size: thisNameCard.usernameTextHeight
// Text Positioning
verticalAlignment: Text.AlignVCenter
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
anchors.right: parent.right
anchors.rightMargin: editGlyph.width + editGlyph.anchors.rightMargin
// Style
color: hifi.colors.darkGray
FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; }
font.family: firaSansSemiBold.name
font.pixelSize: displayNameTextPixelSize
selectionColor: hifi.colors.blueAccent
selectedTextColor: "black"
// Text Positioning
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignLeft
autoScroll: false;
// Signals
onEditingFinished: {
if (MyAvatar.displayName !== text) {
MyAvatar.displayName = text;
UserActivityLogger.palAction("display_name_change", text);
}
cursorPosition = 0
focus = false
myDisplayName.border.width = 0
color = hifi.colors.darkGray
pal.currentlyEditingDisplayName = false
autoScroll = false;
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
myDisplayName.border.width = 1
myDisplayNameText.focus ? myDisplayNameText.cursorPosition = myDisplayNameText.positionAt(mouseX, mouseY, TextInput.CursorOnCharacter) : myDisplayNameText.selectAll();
myDisplayNameText.focus = true
myDisplayNameText.color = "black"
pal.currentlyEditingDisplayName = true
myDisplayNameText.autoScroll = true;
}
onDoubleClicked: {
myDisplayNameText.selectAll();
myDisplayNameText.focus = true;
pal.currentlyEditingDisplayName = true
myDisplayNameText.autoScroll = true;
}
onEntered: myDisplayName.color = hifi.colors.lightGrayText;
onExited: myDisplayName.color = hifi.colors.textFieldLightBackground;
}
// Edit pencil glyph
HiFiGlyphs {
id: editGlyph
text: hifi.glyphs.editPencil
// Text Size
size: displayNameTextPixelSize*1.5
// Anchors
anchors.right: parent.right
anchors.rightMargin: 5
anchors.verticalCenter: parent.verticalCenter
// Style
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: hifi.colors.baseGray
}
// Spacer
Item {
id: userNameSpacer
height: 4
width: parent.width
// Anchors
anchors.top: userNameText.bottom
}
// VU Meter
Rectangle {
id: nameCardVUMeter
// Size
width: isMyCard ? myDisplayName.width - 70 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width
height: 8
// Anchors
anchors.top: userNameSpacer.bottom
// Style
radius: 4
color: "#c5c5c5"
visible: isMyCard || selected
// Rectangle for the zero-gain point on the VU meter
Rectangle {
id: vuMeterZeroGain
visible: gainSlider.visible
// Size
width: 4
height: 18
// Style
color: hifi.colors.darkGray
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: (-gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue) * gainSlider.width - 4
}
// Rectangle for the VU meter line
Rectangle {
id: vuMeterLine
width: gainSlider.width
visible: gainSlider.visible
// Style
color: vuMeterBase.color
radius: nameCardVUMeter.radius
height: nameCardVUMeter.height / 2
anchors.verticalCenter: nameCardVUMeter.verticalCenter
}
// Rectangle for the VU meter base
Rectangle {
id: vuMeterBase
// Anchors
anchors.fill: parent
visible: isMyCard || selected
// Style
color: parent.color
radius: parent.radius
}
// Rectangle for the VU meter audio level
Rectangle {
id: vuMeterLevel
visible: isMyCard || selected
// Size
width: (thisNameCard.audioLevel) * parent.width
// Style
color: parent.color
radius: parent.radius
// Anchors
anchors.bottom: parent.bottom
anchors.top: parent.top
anchors.left: parent.left
}
// Gradient for the VU meter audio level
LinearGradient {
anchors.fill: vuMeterLevel
source: vuMeterLevel
start: Qt.point(0, 0)
end: Qt.point(parent.width, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#2c8e72" }
GradientStop { position: 0.9; color: "#1fc6a6" }
GradientStop { position: 0.91; color: "#ea4c5f" }
GradientStop { position: 1.0; color: "#ea4c5f" }
}
}
}
// Per-Avatar Gain Slider
Slider {
id: gainSlider
// Size
width: parent.width
height: 14
// Anchors
anchors.verticalCenter: nameCardVUMeter.verticalCenter
}
// DisplayName container for others' cards
Item {
id: displayNameContainer
visible: !isMyCard && pal.activeTab !== "connectionsTab"
// Size
width: parent.width - anchors.leftMargin - avatarImage.width - anchors.leftMargin;
height: displayNameTextPixelSize + 4
// Anchors
anchors.top: avatarImage.top;
anchors.left: avatarImage.right
anchors.leftMargin: avatarImage.visible ? 5 : 0;
// DisplayName Text for others' cards
FiraSansSemiBold {
id: displayNameText
// Properties
visible: !isMyCard && selected
value: Users.getAvatarGain(uuid)
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
updateValueWhileDragging: true
onValueChanged: updateGainFromQML(uuid, value, false)
onPressedChanged: {
if (!pressed) {
updateGainFromQML(uuid, value, true)
text: thisNameCard.displayName
elide: Text.ElideRight
// Size
width: isAdmin ? Math.min(displayNameTextMetrics.tightBoundingRect.width + 8, parent.width - adminLabelText.width - adminLabelQuestionMark.width + 8) : parent.width
// Anchors
anchors.top: parent.top
anchors.left: parent.left
// Text Size
size: displayNameTextPixelSize
// Text Positioning
verticalAlignment: Text.AlignTop
// Style
color: hifi.colors.darkGray;
MouseArea {
anchors.fill: parent
enabled: selected && pal.activeTab == "nearbyTab" && thisNameCard.userName !== "" && isPresent;
hoverEnabled: enabled
onClicked: {
goToUserInDomain(thisNameCard.uuid);
UserActivityLogger.palAction("go_to_user_in_domain", thisNameCard.uuid);
}
onEntered: {
displayNameText.color = hifi.colors.blueHighlight;
userNameText.color = hifi.colors.blueHighlight;
}
onExited: {
displayNameText.color = hifi.colors.darkGray
userNameText.color = hifi.colors.blueAccent;
}
}
}
TextMetrics {
id: displayNameTextMetrics
font: displayNameText.font
text: displayNameText.text
}
// "ADMIN" label for other users' cards
RalewaySemiBold {
id: adminLabelText
visible: isAdmin
text: "ADMIN"
// Text size
size: displayNameText.size - 4
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: displayNameText.right
// Style
font.capitalization: Font.AllUppercase
color: hifi.colors.redHighlight
// Alignment
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
}
// This Rectangle refers to the [?] popup button next to "ADMIN"
Item {
id: adminLabelQuestionMark
visible: isAdmin
// Size
width: 20
height: displayNameText.height
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: adminLabelText.right
RalewayRegular {
id: adminLabelQuestionMarkText
text: "[?]"
size: adminLabelText.size
font.capitalization: Font.AllUppercase
color: hifi.colors.redHighlight
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onWheel: {
// Do nothing.
}
onDoubleClicked: {
gainSlider.value = 0.0
}
onPressed: {
// Pass through to Slider
mouse.accepted = false
}
onReleased: {
// the above mouse.accepted seems to make this
// never get called, nonetheless...
mouse.accepted = false
}
enabled: isPresent
hoverEnabled: enabled
onClicked: letterbox(hifi.glyphs.question,
"Domain Admin",
"This user is an admin on this domain. Admins can <b>Silence</b> and <b>Ban</b> other users at their discretion - so be extra nice!")
onEntered: adminLabelQuestionMarkText.color = "#94132e"
onExited: adminLabelQuestionMarkText.color = hifi.colors.redHighlight
}
style: SliderStyle {
groove: Rectangle {
color: "#c5c5c5"
implicitWidth: gainSlider.width
implicitHeight: 4
radius: 2
opacity: 0
}
handle: Rectangle {
anchors.centerIn: parent
color: (control.pressed || control.hovered) ? "#00b4ef" : "#8F8F8F"
implicitWidth: 10
implicitHeight: 16
}
}
}
// UserName Text
FiraSansRegular {
id: userNameText
// Properties
text: thisNameCard.userName === "Unknown user" ? "not logged in" : thisNameCard.userName;
elide: Text.ElideRight
visible: thisNameCard.userName !== "";
// Size
width: parent.width
height: usernameTextPixelSize + 4
// Anchors
anchors.top: isMyCard ? myDisplayName.bottom : pal.activeTab == "nearbyTab" ? displayNameContainer.bottom : undefined //(parent.height - displayNameTextPixelSize/2));
anchors.verticalCenter: pal.activeTab == "connectionsTab" && !isMyCard ? avatarImage.verticalCenter : undefined
anchors.left: avatarImage.right;
anchors.leftMargin: avatarImage.visible ? 5 : 0;
anchors.rightMargin: 5;
// Text Size
size: pal.activeTab == "nearbyTab" || isMyCard ? usernameTextPixelSize : displayNameTextPixelSize;
// Text Positioning
verticalAlignment: Text.AlignVCenter;
// Style
color: hifi.colors.blueAccent;
MouseArea {
anchors.fill: parent
enabled: selected && pal.activeTab == "nearbyTab" && thisNameCard.userName !== "" && isPresent;
hoverEnabled: enabled
onClicked: {
goToUserInDomain(thisNameCard.uuid);
UserActivityLogger.palAction("go_to_user_in_domain", thisNameCard.uuid);
}
onEntered: {
displayNameText.color = hifi.colors.blueHighlight;
userNameText.color = hifi.colors.blueHighlight;
}
onExited: {
displayNameText.color = hifi.colors.darkGray;
userNameText.color = hifi.colors.blueAccent;
}
}
}
StateImage {
id: nameCardConnectionInfoImage
visible: selected && !isMyCard && pal.activeTab == "connectionsTab"
imageURL: "../../images/info-icon-2-state.svg" // PLACEHOLDER!!!
size: 32;
buttonState: 0;
anchors.left: avatarImage.right
anchors.bottom: parent.bottom
}
MouseArea {
anchors.fill:nameCardConnectionInfoImage
enabled: selected
hoverEnabled: true
onClicked: {
userInfoViewer.url = defaultBaseUrl + "/users/" + userName;
userInfoViewer.visible = true;
}
onEntered: {
nameCardConnectionInfoImage.buttonState = 1;
}
onExited: {
nameCardConnectionInfoImage.buttonState = 0;
}
}
FiraSansRegular {
id: nameCardConnectionInfoText
visible: selected && !isMyCard && pal.activeTab == "connectionsTab" && !isMyCard
width: parent.width
height: displayNameTextPixelSize
size: displayNameTextPixelSize - 4
anchors.left: nameCardConnectionInfoImage.right
anchors.verticalCenter: nameCardConnectionInfoImage.verticalCenter
anchors.leftMargin: 5
verticalAlignment: Text.AlignVCenter
text: "Info"
color: hifi.colors.baseGray
}
HiFiGlyphs {
id: nameCardRemoveConnectionImage
visible: selected && !isMyCard && pal.activeTab == "connectionsTab"
text: hifi.glyphs.close
size: 28;
x: 120
anchors.verticalCenter: nameCardConnectionInfoImage.verticalCenter
}
MouseArea {
anchors.fill:nameCardRemoveConnectionImage
enabled: selected
hoverEnabled: true
onClicked: {
// send message to pal.js to forgetConnection
pal.sendToScript({method: 'removeConnection', params: thisNameCard.userName});
}
onEntered: {
nameCardRemoveConnectionImage.text = hifi.glyphs.closeInverted;
}
onExited: {
nameCardRemoveConnectionImage.text = hifi.glyphs.close;
}
}
FiraSansRegular {
id: nameCardRemoveConnectionText
visible: selected && !isMyCard && pal.activeTab == "connectionsTab" && !isMyCard
width: parent.width
height: displayNameTextPixelSize
size: displayNameTextPixelSize - 4
anchors.left: nameCardRemoveConnectionImage.right
anchors.verticalCenter: nameCardRemoveConnectionImage.verticalCenter
anchors.leftMargin: 5
verticalAlignment: Text.AlignVCenter
text: "Forget"
color: hifi.colors.baseGray
}
HifiControls.Button {
id: visitConnectionButton
visible: selected && !isMyCard && pal.activeTab == "connectionsTab" && !isMyCard
text: "Visit"
enabled: thisNameCard.placeName !== ""
anchors.verticalCenter: nameCardRemoveConnectionImage.verticalCenter
x: 240
onClicked: {
AddressManager.goToUser(thisNameCard.userName);
UserActivityLogger.palAction("go_to_user", thisNameCard.userName);
}
}
// VU Meter
Rectangle {
id: nameCardVUMeter
// Size
width: isMyCard ? myDisplayName.width - 20 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * (gainSlider.width);
height: 8
// Anchors
anchors.bottom: isMyCard ? avatarImage.bottom : parent.bottom;
anchors.bottomMargin: isMyCard ? 0 : height;
anchors.left: isMyCard ? userNameText.left : parent.left;
// Style
radius: 4
color: "#c5c5c5"
visible: (isMyCard || (selected && pal.activeTab == "nearbyTab")) && isPresent
// Rectangle for the zero-gain point on the VU meter
Rectangle {
id: vuMeterZeroGain
visible: gainSlider.visible
// Size
width: 4
height: 18
// Style
color: hifi.colors.darkGray
// Anchors
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: (-gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue) * gainSlider.width - 4
}
// Rectangle for the VU meter line
Rectangle {
id: vuMeterLine
width: gainSlider.width
visible: gainSlider.visible
// Style
color: vuMeterBase.color
radius: nameCardVUMeter.radius
height: nameCardVUMeter.height / 2
anchors.verticalCenter: nameCardVUMeter.verticalCenter
}
// Rectangle for the VU meter base
Rectangle {
id: vuMeterBase
// Anchors
anchors.fill: parent
visible: isMyCard || selected
// Style
color: parent.color
radius: parent.radius
}
// Rectangle for the VU meter audio level
Rectangle {
id: vuMeterLevel
visible: isMyCard || selected
// Size
width: (thisNameCard.audioLevel) * parent.width
// Style
color: parent.color
radius: parent.radius
// Anchors
anchors.bottom: parent.bottom
anchors.top: parent.top
anchors.left: parent.left
}
// Gradient for the VU meter audio level
LinearGradient {
anchors.fill: vuMeterLevel
source: vuMeterLevel
start: Qt.point(0, 0)
end: Qt.point(parent.width, 0)
gradient: Gradient {
GradientStop { position: 0.0; color: "#2c8e72" }
GradientStop { position: 0.9; color: "#1fc6a6" }
GradientStop { position: 0.91; color: "#ea4c5f" }
GradientStop { position: 1.0; color: "#ea4c5f" }
}
}
}
// Per-Avatar Gain Slider
Slider {
id: gainSlider
// Size
width: thisNameCard.width;
height: 14
// Anchors
anchors.verticalCenter: nameCardVUMeter.verticalCenter;
anchors.left: nameCardVUMeter.left;
// Properties
visible: !isMyCard && selected && pal.activeTab == "nearbyTab" && isPresent;
value: Users.getAvatarGain(uuid)
minimumValue: -60.0
maximumValue: 20.0
stepSize: 5
updateValueWhileDragging: true
onValueChanged: {
if (uuid !== "") {
updateGainFromQML(uuid, value, false);
}
}
onPressedChanged: {
if (!pressed) {
updateGainFromQML(uuid, value, true)
}
}
MouseArea {
anchors.fill: parent
onWheel: {
// Do nothing.
}
onDoubleClicked: {
gainSlider.value = 0.0
}
onPressed: {
// Pass through to Slider
mouse.accepted = false
}
onReleased: {
// the above mouse.accepted seems to make this
// never get called, nonetheless...
mouse.accepted = false
}
}
style: SliderStyle {
groove: Rectangle {
color: "#c5c5c5"
implicitWidth: gainSlider.width
implicitHeight: 4
radius: 2
opacity: 0
}
handle: Rectangle {
anchors.centerIn: parent
color: (control.pressed || control.hovered) ? "#00b4ef" : "#8F8F8F"
implicitWidth: 10
implicitHeight: 16
}
}
}
@ -402,4 +580,22 @@ Item {
UserActivityLogger.palAction("avatar_gain_changed", avatarUuid);
}
}
// Function body by Howard Stearns 2017-01-08
function goToUserInDomain(avatarUuid) {
var avatar = AvatarList.getAvatar(avatarUuid);
if (!avatar) {
console.log("This avatar is no longer present. goToUserInDomain() failed.");
return;
}
var vector = Vec3.subtract(avatar.position, MyAvatar.position);
var distance = Vec3.length(vector);
var target = Vec3.multiply(Vec3.normalize(vector), distance - 2.0);
// FIXME: We would like the avatar to recompute the avatar's "maybe fly" test at the new position, so that if high enough up,
// the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now.
// FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script.
// Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target.
MyAvatar.orientation = Quat.lookAtSimple(MyAvatar.position, avatar.position);
MyAvatar.position = Vec3.sum(MyAvatar.position, target);
}
}

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,7 @@ Rectangle {
property alias text: label.text
property alias pixelSize: label.font.pixelSize;
property bool selected: false
property bool hovered: false
property int spacing: 2
property var action: function () {}
property string highlightColor: hifi.colors.blueHighlight;
@ -37,14 +38,14 @@ Rectangle {
Rectangle {
id: indicator
width: parent.width
height: 3
height: selected ? 3 : 1
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
color: hifi.colors.blueHighlight
visible: parent.selected
visible: parent.selected || hovered
}
MouseArea {
@ -53,6 +54,8 @@ Rectangle {
acceptedButtons: Qt.LeftButton;
onClicked: action(parent);
hoverEnabled: true;
onEntered: hovered = true
onExited: hovered = false
}
}

View file

@ -21,6 +21,9 @@ Rectangle {
id: root
objectName: "AssetServer"
property string title: "Asset Browser"
property bool keyboardRaised: false
property var eventBridge;
signal sendToScript(var message);
property bool isHMD: false
@ -415,7 +418,6 @@ Rectangle {
Column {
width: parent.width
y: hifi.dimensions.tabletMenuHeader //-bgNavBar
spacing: 10
HifiControls.TabletContentSection {

View file

@ -20,7 +20,7 @@ import "../../windows"
Rectangle {
id: root
objectName: "RunningScripts"
property var title: "Running Scripts"
property string title: "Running Scripts"
HifiConstants { id: hifi }
signal sendToScript(var message);
property var eventBridge;
@ -81,9 +81,9 @@ Rectangle {
Flickable {
id: flickable
width: parent.width
width: tabletRoot.width
height: parent.height - (keyboard.raised ? keyboard.raisedHeight : 0)
contentWidth: parent.width
contentWidth: column.width
contentHeight: column.childrenRect.height
clip: true
@ -121,9 +121,8 @@ Rectangle {
model: runningScriptsModel
id: table
height: 185
width: parent.width
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
anchors.right: parent.right
expandSelectedRow: true
itemDelegate: Item {

View file

@ -22,15 +22,20 @@ Rectangle {
color: hifi.colors.baseGray;
property var eventBridge;
signal sendToScript(var message);
property bool keyboardEnabled: false
property bool punctuationMode: false
property bool keyboardRasied: false
Column {
Item {
id: column1
anchors.rightMargin: 10
anchors.leftMargin: 10
anchors.bottomMargin: 10
anchors.topMargin: 10
anchors.fill: parent
spacing: 5
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: keyboard.top
Text {
id: text1
@ -43,17 +48,42 @@ Rectangle {
id: modelURL
height: 20
text: qsTr("")
color: "white"
anchors.top: text1.bottom
anchors.topMargin: 5
anchors.left: parent.left
anchors.leftMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
font.pixelSize: 12
onAccepted: {
newModelDialog.keyboardEnabled = false;
}
MouseArea {
anchors.fill: parent
onClicked: {
newModelDialog.keyboardEnabled = HMD.active
parent.focus = true;
parent.forceActiveFocus()
}
}
}
Rectangle {
id: textInputBox
color: "white"
anchors.fill: modelURL
opacity: 0.1
}
Row {
id: row1
height: 400
spacing: 30
anchors.top: modelURL.top
anchors.topMargin: 25
anchors.left: parent.left
anchors.leftMargin: 0
anchors.right: parent.right
@ -155,4 +185,15 @@ Rectangle {
}
}
}
Keyboard {
id: keyboard
raised: parent.keyboardEnabled
numeric: parent.punctuationMode
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
}
}

View file

@ -202,7 +202,7 @@ Item {
RalewaySemiBold {
id: usernameText
text: tablet.parent.parent.username
text: tabletRoot.username
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 20

View file

@ -25,22 +25,33 @@ StackView {
HifiConstants { id: hifi }
HifiStyles.HifiConstants { id: hifiStyleConstants }
initialItem: addressBarDialog
width: parent.width
height: parent.height
width: parent !== null ? parent.width : undefined
height: parent !== null ? parent.height : undefined
property var eventBridge;
property var allStories: [];
property int cardWidth: 460;
property int cardHeight: 320;
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
property var tablet: null;
property bool isDesktop: false;
Component { id: tabletStoryCard; TabletStoryCard {} }
Component.onCompleted: {
root.currentItem.focus = true;
root.currentItem.forceActiveFocus();
addressLine.focus = true;
addressLine.forceActiveFocus();
fillDestinations();
updateLocationText();
updateLocationText(false);
root.parentChanged.connect(center);
center();
isDesktop = (typeof desktop !== "undefined");
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
if (desktop) {
root.title = "GOTO";
}
}
Component.onDestruction: {
root.parentChanged.disconnect(center);
@ -59,11 +70,12 @@ StackView {
if (0 !== targetString.indexOf('hifi://')) {
var card = tabletStoryCard.createObject();
card.setUrl(addressBarDialog.metaverseServerUrl + targetString);
card.eventBridge = root.eventBridge;
root.push(card);
return;
}
addressLine.text = targetString;
toggleOrGo(true);
location.text = targetString;
toggleOrGo(true, targetString);
clearAddressLineTimer.start();
}
@ -106,7 +118,9 @@ StackView {
imageURL: "../../../images/home.svg"
onClicked: {
addressBarDialog.loadHome();
root.shown = false;
tabletRoot.shown = false;
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
tablet.gotoHomeScreen();
}
anchors {
left: parent.left
@ -141,7 +155,9 @@ StackView {
anchors {
top: navBar.bottom
right: parent.right
rightMargin: 16
left: parent.left
leftMargin: 16
}
property int inputAreaHeight: 70
@ -239,7 +255,7 @@ StackView {
TabletTextButton {
id: allTab;
text: "ALL";
property string includeActions: 'snapshot, concurrency';
property string includeActions: 'snapshot,concurrency';
selected: allTab === selectedTab;
action: tabSelect;
}
@ -290,9 +306,8 @@ StackView {
left: parent.left
right: parent.right
leftMargin: 10
verticalCenter: parent.verticalCenter;
horizontalCenter: parent.horizontalCenter;
}
model: suggestions
orientation: ListView.Vertical
@ -538,18 +553,29 @@ StackView {
}
}
function toggleOrGo(fromSuggestions) {
function toggleOrGo(fromSuggestions, address) {
if (address !== undefined && address !== "") {
addressBarDialog.loadAddress(address, fromSuggestions)
}
if (addressLine.text !== "") {
addressBarDialog.loadAddress(addressLine.text, fromSuggestions)
}
root.shown = false;
if (isDesktop) {
tablet.gotoHomeScreen();
} else {
HMD.closeTablet();
}
tabletRoot.shown = false;
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
root.shown = false
tabletRoot.shown = false
clearAddressLineTimer.start();
event.accepted = true
break

View file

@ -18,7 +18,7 @@ StackView {
initialItem: root
objectName: "stack"
property string title: "General Settings"
property alias gotoPreviousApp: root.gotoPreviousApp;
property var eventBridge;
signal sendToScript(var message);

View file

@ -13,6 +13,9 @@ Item {
property var openMessage: null;
property string subMenu: ""
signal showDesktop();
property bool shown: true
property int currentApp: -1;
property alias tabletApps: tabletApps
function setOption(value) {
option = value;
@ -55,13 +58,48 @@ Item {
}
function loadSource(url) {
tabletApps.clear();
loader.source = ""; // make sure we load the qml fresh each time.
loader.source = url;
tabletApps.append({"appUrl": url, "isWebUrl": false, "scriptUrl": "", "appWebUrl": ""});
}
function loadQMLOnTop(url) {
tabletApps.append({"appUrl": url, "isWebUrl": false, "scriptUrl": "", "appWebUrl": ""});
loader.source = "";
loader.source = tabletApps.get(currentApp).appUrl;
if (loader.item.hasOwnProperty("gotoPreviousApp")) {
loader.item.gotoPreviousApp = true;
}
}
function loadWebOnTop(url, injectJavaScriptUrl) {
tabletApps.append({"appUrl": loader.source, "isWebUrl": true, "scriptUrl": injectJavaScriptUrl, "appWebUrl": url});
loader.item.url = tabletApps.get(currentApp).appWebUrl;
loader.item.scriptUrl = tabletApps.get(currentApp).scriptUrl;
if (loader.item.hasOwnProperty("gotoPreviousApp")) {
loader.item.gotoPreviousApp = true;
}
}
function returnToPreviousApp() {
tabletApps.remove(currentApp);
var isWebPage = tabletApps.get(currentApp).isWebUrl;
if (isWebPage) {
var webUrl = tabletApps.get(currentApp).appWebUrl;
var scriptUrl = tabletApps.get(currentApp).scriptUrl;
loadSource("TabletWebView.qml");
loadWebUrl(webUrl, scriptUrl);
} else {
loader.source = tabletApps.get(currentApp).appUrl;
}
}
function loadWebUrl(url, injectedJavaScriptUrl) {
tabletApps.clear();
loader.item.url = url;
loader.item.scriptURL = injectedJavaScriptUrl;
tabletApps.append({"appUrl": "TabletWebView.qml", "isWebUrl": true, "scriptUrl": injectedJavaScriptUrl, "appWebUrl": url});
}
// used to send a message from qml to interface script.
@ -96,6 +134,13 @@ Item {
username = newUsername;
}
ListModel {
id: tabletApps
onCountChanged: {
currentApp = count - 1
}
}
Loader {
id: loader
objectName: "loader"

View file

@ -17,7 +17,8 @@ import "../../windows"
import "../"
import "../toolbars"
import "../../styles-uit" as HifiStyles
import "../../controls-uit" as HifiControls
import "../../controls-uit" as HifiControlsUit
import "../../controls" as HifiControls
Rectangle {
@ -26,104 +27,17 @@ Rectangle {
width: parent.width
height: parent.height
property string address: ""
property alias eventBridge: webview.eventBridge
function setUrl(url) {
cardRoot.address = url;
webview.url = url;
}
function goBack() {
}
function visit() {
}
Rectangle {
id: header
anchors {
left: parent.left
right: parent.right
top: parent.top
}
width: parent.width
height: 50
color: hifi.colors.white
Row {
anchors.fill: parent
spacing: 80
Item {
id: backButton
anchors {
top: parent.top
left: parent.left
leftMargin: 100
}
height: parent.height
width: parent.height
HifiStyles.FiraSansSemiBold {
text: "BACK"
elide: Text.ElideRight
anchors.fill: parent
size: 16
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: hifi.colors.lightGray
MouseArea {
id: backButtonMouseArea
anchors.fill: parent
hoverEnabled: enabled
onClicked: {
webview.goBack();
}
}
}
}
Item {
id: closeButton
anchors {
top: parent.top
right: parent.right
rightMargin: 100
}
height: parent.height
width: parent.height
HifiStyles.FiraSansSemiBold {
text: "CLOSE"
elide: Text.ElideRight
anchors.fill: parent
size: 16
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: hifi.colors.lightGray
MouseArea {
id: closeButtonMouseArea
anchors.fill: parent
hoverEnabled: enabled
onClicked: root.pop();
}
}
}
}
}
HifiControls.WebView {
HifiControls.TabletWebView {
id: webview
parentStackItem: root
anchors {
top: header.bottom
top: parent.top
right: parent.right
left: parent.left
bottom: parent.bottom

View file

@ -31,6 +31,7 @@ Item {
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
property bool gotoPreviousApp: false
property var tablet;
@ -55,7 +56,13 @@ Item {
}
function closeDialog() {
Tablet.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen();
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
if (gotoPreviousApp) {
tablet.returnToPreviousApp();
} else {
tablet.gotoHomeScreen();
}
}
Rectangle {

View file

@ -89,14 +89,17 @@ Preference {
if (categoryPreferences) {
console.log("Category " + root.name + " with " + categoryPreferences.length + " preferences");
for (var j = 0; j < categoryPreferences.length; ++j) {
buildPreference(categoryPreferences[j]);
//provide component position within column
//lowest numbers on top
buildPreference(categoryPreferences[j], j);
}
}
}
function buildPreference(preference) {
function buildPreference(preference, itemNum) {
console.log("\tPreference type " + preference.type + " name " + preference.name)
var builder;
var zpos;
switch (preference.type) {
case Preference.Editable:
checkBoxCount = 0;
@ -136,11 +139,14 @@ Preference {
case Preference.ComboBox:
checkBoxCount = 0;
builder = comboBoxBuilder;
//make sure that combo boxes sitting higher will have higher z coordinate
//to be not overlapped when drop down is active
zpos = root.z + 1000 - itemNum
break;
};
if (builder) {
preferences.push(builder.createObject(contentContainer, { preference: preference, isFirstCheckBox: (checkBoxCount === 1) }));
preferences.push(builder.createObject(contentContainer, { preference: preference, isFirstCheckBox: (checkBoxCount === 1) , z: zpos}));
}
}
}

View file

@ -12,7 +12,10 @@ Item {
property bool pinned: false
clip: true
function updateYOffset() { yOffset = size * buttonState; }
function updateYOffset() {
//make sure offset not set outside image
yOffset = (size * buttonState >= image.height) ? image.height - size : size * buttonState
}
onButtonStateChanged: updateYOffset();
Component.onCompleted: {

View file

@ -70,6 +70,10 @@ Item {
readonly property color indigoAccent: "#9495FF"
readonly property color magentaHighlight: "#EF93D1"
readonly property color magentaAccent: "#A2277C"
readonly property color checkboxCheckedRed: "#FF0000"
readonly property color checkboxCheckedBorderRed: "#D00000"
readonly property color lightBlueHighlight: "#d6f6ff"
// Semitransparent
readonly property color darkGray30: "#4d121212"
readonly property color darkGray0: "#00121212"
@ -172,7 +176,7 @@ Item {
readonly property real textFieldInputLabel: dimensions.largeScreen ? 13 : 9
readonly property real textFieldSearchIcon: dimensions.largeScreen ? 30 : 24
readonly property real tableHeading: dimensions.largeScreen ? 12 : 10
readonly property real tableHeadingIcon: dimensions.largeScreen ? 40 : 33
readonly property real tableHeadingIcon: dimensions.largeScreen ? 60 : 33
readonly property real tableText: dimensions.largeScreen ? 15 : 12
readonly property real buttonLabel: dimensions.largeScreen ? 13 : 9
readonly property real iconButton: dimensions.largeScreen ? 13 : 9

View file

@ -118,6 +118,7 @@
#include <udt/PacketHeaders.h>
#include <UserActivityLogger.h>
#include <UsersScriptingInterface.h>
#include <recording/ClipCache.h>
#include <recording/Deck.h>
#include <recording/Recorder.h>
#include <shared/StringHelpers.h>
@ -129,6 +130,7 @@
#include "AudioClient.h"
#include "audio/AudioScope.h"
#include "avatar/AvatarManager.h"
#include "avatar/ScriptAvatar.h"
#include "CrashHandler.h"
#include "devices/DdeFaceTracker.h"
#include "devices/EyeTracker.h"
@ -141,6 +143,8 @@
#include "LODManager.h"
#include "ModelPackager.h"
#include "networking/HFWebEngineProfile.h"
#include "networking/HFTabletWebEngineProfile.h"
#include "networking/FileTypeProfile.h"
#include "scripting/TestScriptingInterface.h"
#include "scripting/AccountScriptingInterface.h"
#include "scripting/AssetMappingsScriptingInterface.h"
@ -179,6 +183,7 @@
#include "FrameTimingsScriptingInterface.h"
#include <GPUIdent.h>
#include <gl/GLHelpers.h>
#include <src/scripting/LimitlessVoiceRecognitionScriptingInterface.h>
#include <EntityScriptClient.h>
#include <ModelScriptingInterface.h>
@ -216,6 +221,7 @@ static const QString FST_EXTENSION = ".fst";
static const QString FBX_EXTENSION = ".fbx";
static const QString OBJ_EXTENSION = ".obj";
static const QString AVA_JSON_EXTENSION = ".ava.json";
static const QString WEB_VIEW_TAG = "noDownload=true";
static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f;
@ -462,6 +468,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<StatTracker>();
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT);
DependencyManager::set<Preferences>();
DependencyManager::set<recording::ClipCache>();
DependencyManager::set<recording::Deck>();
DependencyManager::set<recording::Recorder>();
DependencyManager::set<AddressManager>();
@ -521,7 +528,9 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<OffscreenQmlSurfaceCache>();
DependencyManager::set<EntityScriptClient>();
DependencyManager::set<EntityScriptServerLogClient>();
DependencyManager::set<LimitlessVoiceRecognitionScriptingInterface>();
DependencyManager::set<OctreeStatsProvider>(nullptr, qApp->getOcteeSceneStats());
return previousSessionCrashed;
}
@ -548,8 +557,7 @@ const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 100.0f;
const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f;
const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
const bool DEFAULT_TABLET_VISIBLE_TO_OTHERS = false;
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = true;
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runServer, QString runServerPathOption) :
QApplication(argc, argv),
@ -571,7 +579,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_desktopTabletScale("desktopTabletScale", DEFAULT_DESKTOP_TABLET_SCALE_PERCENT),
_desktopTabletBecomesToolbarSetting("desktopTabletBecomesToolbar", DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR),
_hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR),
_tabletVisibleToOthersSetting("tabletVisibleToOthers", DEFAULT_TABLET_VISIBLE_TO_OTHERS),
_preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS),
_constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true),
_scaleMirror(1.0f),
@ -1798,10 +1805,11 @@ Application::~Application() {
_physicsEngine->setCharacterController(nullptr);
// remove avatars from physics engine
DependencyManager::get<AvatarManager>()->clearAllAvatars();
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
VectorOfMotionStates motionStates;
DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
_physicsEngine->removeObjects(motionStates);
DependencyManager::get<AvatarManager>()->deleteAllAvatars();
DependencyManager::destroy<AvatarManager>();
DependencyManager::destroy<AnimationCache>();
@ -1878,9 +1886,9 @@ void Application::initializeGL() {
assert(items.canCast<RenderFetchCullSortTask::Output>());
static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
if (QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD)) {
_renderEngine->addJob<RenderForwardTask>("Forward", items.get<RenderFetchCullSortTask::Output>());
_renderEngine->addJob<RenderForwardTask>("Forward", items);
} else {
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", items.get<RenderFetchCullSortTask::Output>());
_renderEngine->addJob<RenderDeferredTask>("RenderDeferredTask", items);
}
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);
@ -1931,6 +1939,8 @@ void Application::initializeUi() {
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
qmlRegisterType<HFWebEngineProfile>("HFWebEngineProfile", 1, 0, "HFWebEngineProfile");
qmlRegisterType<HFTabletWebEngineProfile>("HFTabletWebEngineProfile", 1, 0, "HFTabletWebEngineProfile");
qmlRegisterType<FileTypeProfile>("FileTypeProfile", 1, 0, "FileTypeProfile");
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_glWidget->qglContext());
@ -2007,6 +2017,7 @@ void Application::initializeUi() {
rootContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
rootContext->setContextProperty("Account", AccountScriptingInterface::getInstance());
rootContext->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
rootContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
rootContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
rootContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
@ -2359,11 +2370,6 @@ void Application::setHmdTabletBecomesToolbarSetting(bool value) {
updateSystemTabletMode();
}
void Application::setTabletVisibleToOthersSetting(bool value) {
_tabletVisibleToOthersSetting.set(value);
updateSystemTabletMode();
}
void Application::setPreferAvatarFingerOverStylus(bool value) {
_preferAvatarFingerOverStylusSetting.set(value);
}
@ -3788,7 +3794,6 @@ void Application::loadSettings() {
}
getMyAvatar()->loadData();
setTabletVisibleToOthersSetting(false);
_settingsLoaded = true;
}
@ -4386,6 +4391,10 @@ void Application::update(float deltaTime) {
auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix));
controller::Pose leftFootPose = userInputMapper->getPoseState(controller::Action::LEFT_FOOT);
controller::Pose rightFootPose = userInputMapper->getPoseState(controller::Action::RIGHT_FOOT);
myAvatar->setFootControllerPosesInSensorFrame(leftFootPose.transform(avatarToSensorMatrix), rightFootPose.transform(avatarToSensorMatrix));
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
updateDialogs(deltaTime); // update various stats dialogs if present
@ -4570,6 +4579,8 @@ void Application::update(float deltaTime) {
}
AnimDebugDraw::getInstance().update();
DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update();
}
void Application::sendAvatarViewFrustum() {
@ -5033,7 +5044,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
// TODO fix shadows and make them use the GPU library
// The pending changes collecting the changes here
render::PendingChanges pendingChanges;
render::Transaction transaction;
// FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities
// Background rendering decision
@ -5041,7 +5052,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
auto backgroundRenderData = make_shared<BackgroundRenderData>();
auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);
BackgroundRenderData::_item = _main3DScene->allocateID();
pendingChanges.resetItem(BackgroundRenderData::_item, backgroundRenderPayload);
transaction.resetItem(BackgroundRenderData::_item, backgroundRenderPayload);
}
// Assuming nothing get's rendered through that
@ -5059,7 +5070,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
static_cast<int>(RenderArgs::RENDER_DEBUG_HULLS));
}
renderArgs->_debugFlags = renderDebugFlags;
//ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, pendingChanges);
//ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction);
}
}
@ -5071,9 +5082,9 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
WorldBoxRenderData::_item = _main3DScene->allocateID();
pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);
transaction.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);
} else {
pendingChanges.updateItem<WorldBoxRenderData>(WorldBoxRenderData::_item,
transaction.updateItem<WorldBoxRenderData>(WorldBoxRenderData::_item,
[](WorldBoxRenderData& payload) {
payload._val++;
});
@ -5086,10 +5097,10 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
}
{
PerformanceTimer perfTimer("SceneProcessPendingChanges");
_main3DScene->enqueuePendingChanges(pendingChanges);
PerformanceTimer perfTimer("SceneProcessTransaction");
_main3DScene->enqueueTransaction(transaction);
_main3DScene->processPendingChangesQueue();
_main3DScene->processTransactionQueue();
}
// For now every frame pass the renderContext
@ -5430,6 +5441,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
// AvatarManager has some custom types
AvatarManager::registerMetaTypes(scriptEngine);
// give the script engine to the RecordingScriptingInterface for its callbacks
DependencyManager::get<RecordingScriptingInterface>()->setScriptEngine(scriptEngine);
if (property(hifi::properties::TEST).isValid()) {
scriptEngine->registerGlobalObject("Test", TestScriptingInterface::getInstance());
}
@ -5516,6 +5530,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
scriptEngine->registerGlobalObject("LimitlessSpeechRecognition", DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>().data());
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine, steamClient.get()));
}
@ -5540,7 +5556,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
bool Application::canAcceptURL(const QString& urlString) const {
QUrl url(urlString);
if (urlString.startsWith(HIFI_URL_SCHEME)) {
if (url.query().contains(WEB_VIEW_TAG)) {
return false;
} else if (urlString.startsWith(HIFI_URL_SCHEME)) {
return true;
}
QHashIterator<QString, AcceptURLMethod> i(_acceptedExtensions);
@ -5648,7 +5666,9 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
QUrl scriptURL { scriptFilenameOrURL };
if (scriptURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) {
shortName = shortName.mid(shortName.lastIndexOf('/') + 1);
int startIndex = shortName.lastIndexOf('/') + 1;
int endIndex = shortName.lastIndexOf('?');
shortName = shortName.mid(startIndex, endIndex - startIndex);
}
QString message = "Would you like to run this script:\n" + shortName;
@ -5781,22 +5801,10 @@ void Application::toggleRunningScriptsWidget() const {
}
void Application::showScriptLogs() {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
auto scriptEngines = DependencyManager::get<ScriptEngines>();
QUrl defaultScriptsLoc = defaultScriptsLocation();
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/debugging/debugWindow.js");
if (tablet->getToolbarMode()) {
scriptEngines->loadScript(defaultScriptsLoc.toString());
} else {
QQuickItem* tabletRoot = tablet->getTabletRoot();
if (!tabletRoot && !isHMDMode()) {
scriptEngines->loadScript(defaultScriptsLoc.toString());
} else {
tablet->pushOntoStack("../../hifi/dialogs/TabletDebugWindow.qml");
}
}
scriptEngines->loadScript(defaultScriptsLoc.toString());
}
void Application::showAssetServerWidget(QString filePath) {
@ -6414,7 +6422,7 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
// If we're not doing an animated snapshot as well...
if (!includeAnimated || !(SnapshotAnimated::alsoTakeAnimatedSnapshot.get())) {
// Tell the dependency manager that the capture of the still snapshot has taken place.
emit DependencyManager::get<WindowScriptingInterface>()->snapshotTaken(path, "", notify);
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify);
} else {
// Get an animated GIF snapshot and save it
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>());

View file

@ -220,8 +220,6 @@ public:
void setDesktopTabletBecomesToolbarSetting(bool value);
bool getHmdTabletBecomesToolbarSetting() { return _hmdTabletBecomesToolbarSetting.get(); }
void setHmdTabletBecomesToolbarSetting(bool value);
bool getTabletVisibleToOthersSetting() { return _tabletVisibleToOthersSetting.get(); }
void setTabletVisibleToOthersSetting(bool value);
bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); }
void setPreferAvatarFingerOverStylus(bool value);
@ -569,7 +567,6 @@ private:
Setting::Handle<float> _desktopTabletScale;
Setting::Handle<bool> _desktopTabletBecomesToolbarSetting;
Setting::Handle<bool> _hmdTabletBecomesToolbarSetting;
Setting::Handle<bool> _tabletVisibleToOthersSetting;
Setting::Handle<bool> _preferAvatarFingerOverStylusSetting;
Setting::Handle<bool> _constrainToolbarPosition;

View file

@ -40,9 +40,10 @@ void DiscoverabilityManager::updateLocation() {
auto accountManager = DependencyManager::get<AccountManager>();
auto addressManager = DependencyManager::get<AddressManager>();
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
bool discoverable = (_mode.get() != Discoverability::None);
if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
if (accountManager->isLoggedIn()) {
// construct a QJsonObject given the user's current address information
QJsonObject rootObject;
@ -50,34 +51,40 @@ void DiscoverabilityManager::updateLocation() {
QString pathString = addressManager->currentPath();
const QString PATH_KEY_IN_LOCATION = "path";
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
const QString CONNECTED_KEY_IN_LOCATION = "connected";
locationObject.insert(CONNECTED_KEY_IN_LOCATION, domainHandler.isConnected());
locationObject.insert(CONNECTED_KEY_IN_LOCATION, discoverable && domainHandler.isConnected());
if (!addressManager->getRootPlaceID().isNull()) {
const QString PLACE_ID_KEY_IN_LOCATION = "place_id";
locationObject.insert(PLACE_ID_KEY_IN_LOCATION,
uuidStringWithoutCurlyBraces(addressManager->getRootPlaceID()));
if (discoverable || _lastLocationObject.isEmpty()) { // Don't consider changes to these as update-worthy if we're not discoverable.
const QString PATH_KEY_IN_LOCATION = "path";
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
if (!addressManager->getRootPlaceID().isNull()) {
const QString PLACE_ID_KEY_IN_LOCATION = "place_id";
locationObject.insert(PLACE_ID_KEY_IN_LOCATION,
uuidStringWithoutCurlyBraces(addressManager->getRootPlaceID()));
}
if (!domainHandler.getUUID().isNull()) {
const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION,
uuidStringWithoutCurlyBraces(domainHandler.getUUID()));
}
// in case the place/domain isn't in the database, we send the network address and port
auto& domainSockAddr = domainHandler.getSockAddr();
const QString NETWORK_ADDRESS_KEY_IN_LOCATION = "network_address";
locationObject.insert(NETWORK_ADDRESS_KEY_IN_LOCATION, domainSockAddr.getAddress().toString());
const QString NETWORK_ADDRESS_PORT_IN_LOCATION = "network_port";
locationObject.insert(NETWORK_ADDRESS_PORT_IN_LOCATION, domainSockAddr.getPort());
const QString NODE_ID_IN_LOCATION = "node_id";
const int UUID_REAL_LENGTH = 36;
locationObject.insert(NODE_ID_IN_LOCATION, DependencyManager::get<NodeList>()->getSessionUUID().toString().mid(1, UUID_REAL_LENGTH));
}
if (!domainHandler.getUUID().isNull()) {
const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION,
uuidStringWithoutCurlyBraces(domainHandler.getUUID()));
}
// in case the place/domain isn't in the database, we send the network address and port
auto& domainSockAddr = domainHandler.getSockAddr();
const QString NETWORK_ADRESS_KEY_IN_LOCATION = "network_address";
locationObject.insert(NETWORK_ADRESS_KEY_IN_LOCATION, domainSockAddr.getAddress().toString());
const QString NETWORK_ADDRESS_PORT_IN_LOCATION = "network_port";
locationObject.insert(NETWORK_ADDRESS_PORT_IN_LOCATION, domainSockAddr.getPort());
const QString FRIENDS_ONLY_KEY_IN_LOCATION = "friends_only";
locationObject.insert(FRIENDS_ONLY_KEY_IN_LOCATION, (_mode.get() == Discoverability::Friends));
const QString AVAILABILITY_KEY_IN_LOCATION = "availability";
locationObject.insert(AVAILABILITY_KEY_IN_LOCATION, findableByString(static_cast<Discoverability::Mode>(_mode.get())));
JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = this;
@ -139,19 +146,29 @@ void DiscoverabilityManager::setDiscoverabilityMode(Discoverability::Mode discov
// update the setting to the new value
_mode.set(static_cast<int>(discoverabilityMode));
if (static_cast<int>(_mode.get()) == Discoverability::None) {
// if we just got set to no discoverability, make sure that we delete our location in DB
removeLocation();
} else {
// we have a discoverability mode that says we should send a location, do that right away
updateLocation();
}
updateLocation(); // update right away
emit discoverabilityModeChanged(discoverabilityMode);
}
}
QString DiscoverabilityManager::findableByString(Discoverability::Mode discoverabilityMode) {
if (discoverabilityMode == Discoverability::None) {
return "none";
} else if (discoverabilityMode == Discoverability::Friends) {
return "friends";
} else if (discoverabilityMode == Discoverability::Connections) {
return "connections";
} else if (discoverabilityMode == Discoverability::All) {
return "all";
} else {
qDebug() << "GlobalServices findableByString called with an unrecognized value.";
return "";
}
}
void DiscoverabilityManager::setVisibility() {
Menu* menu = Menu::getInstance();

View file

@ -19,6 +19,7 @@ namespace Discoverability {
enum Mode {
None,
Friends,
Connections,
All
};
}
@ -42,6 +43,9 @@ public slots:
signals:
void discoverabilityModeChanged(Discoverability::Mode discoverabilityMode);
public:
static QString findableByString(Discoverability::Mode discoverabilityMode);
private slots:
void handleHeartbeatResponse(QNetworkReply& requestReply);

View file

@ -193,6 +193,9 @@ void Avatar::animateScaleChanges(float deltaTime) {
}
setScale(glm::vec3(animatedScale)); // avatar scale is uniform
// flag the joints as having changed for force update to RenderItem
_hasNewJointData = true;
// TODO: rebuilding the shape constantly is somehwat expensive.
// We should only rebuild after significant change.
rebuildCollisionShape();
@ -200,8 +203,12 @@ void Avatar::animateScaleChanges(float deltaTime) {
}
void Avatar::setTargetScale(float targetScale) {
AvatarData::setTargetScale(targetScale);
_isAnimatingScale = true;
float newValue = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
if (_targetScale != newValue) {
_targetScale = newValue;
_scaleChanged = usecTimestampNow();
_isAnimatingScale = true;
}
}
void Avatar::updateAvatarEntities() {
@ -476,34 +483,30 @@ static TextRenderer3D* textRenderer(TextRendererType type) {
return displayNameRenderer;
}
bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
void Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
auto avatarPayload = new render::Payload<AvatarData>(self);
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
_renderItemID = scene->allocateID();
pendingChanges.resetItem(_renderItemID, avatarPayloadPointer);
_skeletonModel->addToScene(scene, pendingChanges);
transaction.resetItem(_renderItemID, avatarPayloadPointer);
_skeletonModel->addToScene(scene, transaction);
for (auto& attachmentModel : _attachmentModels) {
attachmentModel->addToScene(scene, pendingChanges);
attachmentModel->addToScene(scene, transaction);
}
_inScene = true;
return true;
}
void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {
pendingChanges.removeItem(_renderItemID);
void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
transaction.removeItem(_renderItemID);
render::Item::clearID(_renderItemID);
_skeletonModel->removeFromScene(scene, pendingChanges);
_skeletonModel->removeFromScene(scene, transaction);
for (auto& attachmentModel : _attachmentModels) {
attachmentModel->removeFromScene(scene, pendingChanges);
attachmentModel->removeFromScene(scene, transaction);
}
_inScene = false;
}
void Avatar::updateRenderItem(render::PendingChanges& pendingChanges) {
void Avatar::updateRenderItem(render::Transaction& transaction) {
if (render::Item::isValidID(_renderItemID)) {
pendingChanges.updateItem<render::Payload<AvatarData>>(_renderItemID, [](render::Payload<AvatarData>& p) {});
transaction.updateItem<render::Payload<AvatarData>>(_renderItemID, [](render::Payload<AvatarData>& p) {});
}
}
@ -680,24 +683,24 @@ void Avatar::fixupModelsInScene() {
// check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene
render::ScenePointer scene = qApp->getMain3DScene();
render::PendingChanges pendingChanges;
render::Transaction transaction;
if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) {
_skeletonModel->removeFromScene(scene, pendingChanges);
_skeletonModel->addToScene(scene, pendingChanges);
_skeletonModel->removeFromScene(scene, transaction);
_skeletonModel->addToScene(scene, transaction);
}
for (auto attachmentModel : _attachmentModels) {
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
attachmentModel->removeFromScene(scene, pendingChanges);
attachmentModel->addToScene(scene, pendingChanges);
attachmentModel->removeFromScene(scene, transaction);
attachmentModel->addToScene(scene, transaction);
}
}
for (auto attachmentModelToRemove : _attachmentsToRemove) {
attachmentModelToRemove->removeFromScene(scene, pendingChanges);
attachmentModelToRemove->removeFromScene(scene, transaction);
}
_attachmentsToDelete.insert(_attachmentsToDelete.end(), _attachmentsToRemove.begin(), _attachmentsToRemove.end());
_attachmentsToRemove.clear();
scene->enqueuePendingChanges(pendingChanges);
scene->enqueueTransaction(transaction);
}
bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
@ -933,6 +936,21 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const {
return translation;
}
glm::quat Avatar::getAbsoluteDefaultJointRotationInObjectFrame(int index) const {
glm::quat rotation;
auto rig = _skeletonModel->getRig();
glm::quat rot = rig->getAnimSkeleton()->getAbsoluteDefaultPose(index).rot();
return Quaternions::Y_180 * rot;
}
glm::vec3 Avatar::getAbsoluteDefaultJointTranslationInObjectFrame(int index) const {
glm::vec3 translation;
auto rig = _skeletonModel->getRig();
glm::vec3 trans = rig->getAnimSkeleton()->getAbsoluteDefaultPose(index).trans();
glm::mat4 y180Mat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3());
return transformPoint(y180Mat * rig->getGeometryToRigTransform(), trans);
}
glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
if (index < 0) {
index += numeric_limits<unsigned short>::max() + 1; // 65536
@ -1395,23 +1413,47 @@ void Avatar::setParentJointIndex(quint16 parentJointIndex) {
}
}
QList<QVariant> Avatar::getSkeleton() {
SkeletonModelPointer skeletonModel = _skeletonModel;
if (skeletonModel) {
RigPointer rig = skeletonModel->getRig();
if (rig) {
AnimSkeleton::ConstPointer skeleton = rig->getAnimSkeleton();
if (skeleton) {
QList<QVariant> list;
list.reserve(skeleton->getNumJoints());
for (int i = 0; i < skeleton->getNumJoints(); i++) {
QVariantMap obj;
obj["name"] = skeleton->getJointName(i);
obj["index"] = i;
obj["parentIndex"] = skeleton->getParentIndex(i);
list.push_back(obj);
}
return list;
}
}
}
return QList<QVariant>();
}
void Avatar::addToScene(AvatarSharedPointer myHandle) {
render::ScenePointer scene = qApp->getMain3DScene();
if (scene) {
render::PendingChanges pendingChanges;
render::Transaction transaction;
auto nodelist = DependencyManager::get<NodeList>();
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()
&& !nodelist->isIgnoringNode(getSessionUUID())
&& !nodelist->isRadiusIgnoringNode(getSessionUUID())) {
addToScene(myHandle, scene, pendingChanges);
addToScene(myHandle, scene, transaction);
}
scene->enqueuePendingChanges(pendingChanges);
scene->enqueueTransaction(transaction);
} else {
qCWarning(interfaceapp) << "AvatarManager::addAvatar() : Unexpected null scene, possibly during application shutdown";
}
}
void Avatar::ensureInScene(AvatarSharedPointer self) {
if (!_inScene) {
if (!render::Item::isValidID(_renderItemID)) {
addToScene(self);
}
}

View file

@ -55,6 +55,16 @@ class Texture;
class Avatar : public AvatarData {
Q_OBJECT
/**jsdoc
* An avatar is representation of yourself or another user. The Avatar API can be used to query or manipulate the avatar of a user.
* NOTE: Avatar extends AvatarData, see those namespace for more properties/methods.
*
* @namespace Avatar
* @augments AvatarData
*
* @property skeletonOffset {Vec3} can be used to apply a translation offset between the avatar's position and the registration point of the 3d model.
*/
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
public:
@ -71,13 +81,13 @@ public:
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
bool addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges);
void addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
render::Transaction& transaction);
void removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
render::PendingChanges& pendingChanges);
render::Transaction& transaction);
void updateRenderItem(render::PendingChanges& pendingChanges);
void updateRenderItem(render::Transaction& transaction);
virtual void postUpdate(float deltaTime);
@ -111,6 +121,26 @@ public:
Q_INVOKABLE virtual glm::quat getDefaultJointRotation(int index) const;
Q_INVOKABLE virtual glm::vec3 getDefaultJointTranslation(int index) const;
/**jsdoc
* Provides read only access to the default joint rotations in avatar coordinates.
* The default pose of the avatar is defined by the position and orientation of all bones
* in the avatar's model file. Typically this is a t-pose.
* @function Avatar.getAbsoluteDefaultJointRotationInObjectFrame
* @param index {number} index number
* @returns {Quat} The rotation of this joint in avatar coordinates.
*/
Q_INVOKABLE virtual glm::quat getAbsoluteDefaultJointRotationInObjectFrame(int index) const;
/**jsdoc
* Provides read only access to the default joint translations in avatar coordinates.
* The default pose of the avatar is defined by the position and orientation of all bones
* in the avatar's model file. Typically this is a t-pose.
* @function Avatar.getAbsoluteDefaultJointTranslationInObjectFrame
* @param index {number} index number
* @returns {Vec3} The position of this joint in avatar coordinates.
*/
Q_INVOKABLE virtual glm::vec3 getAbsoluteDefaultJointTranslationInObjectFrame(int index) const;
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
@ -169,6 +199,21 @@ public:
Q_INVOKABLE virtual quint16 getParentJointIndex() const override { return SpatiallyNestable::getParentJointIndex(); }
Q_INVOKABLE virtual void setParentJointIndex(quint16 parentJointIndex) override;
/**jsdoc
* Information about a single joint in an Avatar's skeleton hierarchy.
* @typedef Avatar.SkeletonJoint
* @property {string} name - name of joint
* @property {number} index - joint index
* @property {number} parentIndex - index of this joint's parent (-1 if no parent)
*/
/**jsdoc
* Returns an array of joints, where each joint is an object containing name, index and parentIndex fields.
* @function Avatar.getSkeleton
* @returns {Avatar.SkeletonJoint[]} returns a list of information about each joint in this avatar's skeleton.
*/
Q_INVOKABLE QList<QVariant> getSkeleton();
// NOT thread safe, must be called on main thread.
glm::vec3 getUncachedLeftPalmPosition() const;
glm::quat getUncachedLeftPalmRotation() const;
@ -260,6 +305,7 @@ protected:
void addToScene(AvatarSharedPointer self);
void ensureInScene(AvatarSharedPointer self);
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
// Some rate tracking support
RateCounter<> _simulationRate;
@ -285,7 +331,6 @@ private:
int _nameRectGeometryID { 0 };
bool _initialized;
bool _isLookAtTarget { false };
bool _inScene { false };
bool _isAnimatingScale { false };
float getBoundingRadius() const;

View file

@ -68,7 +68,7 @@ void AvatarManager::registerMetaTypes(QScriptEngine* engine) {
}
AvatarManager::AvatarManager(QObject* parent) :
_avatarFades(),
_avatarsToFade(),
_myAvatar(std::make_shared<MyAvatar>(std::make_shared<Rig>()))
{
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
@ -100,15 +100,16 @@ void AvatarManager::init() {
_avatarHash.insert(MY_AVATAR_KEY, _myAvatar);
}
_shouldRender = DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars();
connect(DependencyManager::get<SceneScriptingInterface>().data(), &SceneScriptingInterface::shouldRenderAvatarsChanged,
this, &AvatarManager::updateAvatarRenderStatus, Qt::QueuedConnection);
render::ScenePointer scene = qApp->getMain3DScene();
render::PendingChanges pendingChanges;
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) {
_myAvatar->addToScene(_myAvatar, scene, pendingChanges);
if (_shouldRender) {
render::ScenePointer scene = qApp->getMain3DScene();
render::Transaction transaction;
_myAvatar->addToScene(_myAvatar, scene, transaction);
scene->enqueueTransaction(transaction);
}
scene->enqueuePendingChanges(pendingChanges);
}
void AvatarManager::updateMyAvatar(float deltaTime) {
@ -151,7 +152,7 @@ float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QStri
void AvatarManager::updateOtherAvatars(float deltaTime) {
// lock the hash for read to check the size
QReadLocker lock(&_hashLock);
if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) {
if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) {
return;
}
lock.unlock();
@ -181,30 +182,24 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
// DO NOT update or fade out uninitialized Avatars
return true; // ignore it
}
if (avatar->shouldDie()) {
removeAvatar(avatar->getID());
return true; // ignore it
}
if (avatar->isDead()) {
return true; // ignore it
}
return false;
});
render::PendingChanges pendingChanges;
uint64_t startTime = usecTimestampNow();
const uint64_t UPDATE_BUDGET = 2000; // usec
uint64_t updateExpiry = startTime + UPDATE_BUDGET;
int numAvatarsUpdated = 0;
int numAVatarsNotUpdated = 0;
render::Transaction transaction;
while (!sortedAvatars.empty()) {
const AvatarPriority& sortData = sortedAvatars.top();
const auto& avatar = std::static_pointer_cast<Avatar>(sortData.avatar);
// for ALL avatars...
avatar->ensureInScene(avatar);
if (_shouldRender) {
avatar->ensureInScene(avatar);
}
if (!avatar->getMotionState()) {
ShapeInfo shapeInfo;
avatar->computeShapeInfo(shapeInfo);
@ -218,6 +213,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
}
}
avatar->animateScaleChanges(deltaTime);
if (avatar->shouldDie()) {
avatar->die();
removeAvatar(avatar->getID());
}
const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY;
uint64_t now = usecTimestampNow();
@ -228,7 +227,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
numAvatarsUpdated++;
}
avatar->simulate(deltaTime, inView);
avatar->updateRenderItem(pendingChanges);
avatar->updateRenderItem(transaction);
avatar->setLastRenderUpdateTime(startTime);
} else {
// we've spent our full time budget --> bail on the rest of the avatar updates
@ -259,10 +258,24 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
sortedAvatars.pop();
}
if (_shouldRender) {
if (!_avatarsToFade.empty()) {
QReadLocker lock(&_hashLock);
QVector<AvatarSharedPointer>::iterator itr = _avatarsToFade.begin();
while (itr != _avatarsToFade.end() && usecTimestampNow() > updateExpiry) {
auto avatar = std::static_pointer_cast<Avatar>(*itr);
avatar->animateScaleChanges(deltaTime);
avatar->simulate(deltaTime, true);
avatar->updateRenderItem(transaction);
++itr;
}
}
qApp->getMain3DScene()->enqueueTransaction(transaction);
}
_avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;
_numAvatarsUpdated = numAvatarsUpdated;
_numAvatarsNotUpdated = numAVatarsNotUpdated;
qApp->getMain3DScene()->enqueuePendingChanges(pendingChanges);
simulateAvatarFades(deltaTime);
}
@ -277,54 +290,76 @@ void AvatarManager::postUpdate(float deltaTime) {
}
void AvatarManager::simulateAvatarFades(float deltaTime) {
QVector<AvatarSharedPointer>::iterator fadingIterator = _avatarFades.begin();
if (_avatarsToFade.empty()) {
return;
}
const float SHRINK_RATE = 0.15f;
const float MIN_FADE_SCALE = MIN_AVATAR_SCALE;
render::ScenePointer scene = qApp->getMain3DScene();
render::PendingChanges pendingChanges;
while (fadingIterator != _avatarFades.end()) {
auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator);
QReadLocker locker(&_hashLock);
QVector<AvatarSharedPointer>::iterator itr = _avatarsToFade.begin();
while (itr != _avatarsToFade.end()) {
auto avatar = std::static_pointer_cast<Avatar>(*itr);
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
avatar->animateScaleChanges(deltaTime);
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
// only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine
// fading to zero is such a rare event we push unique transaction for each one
if (avatar->isInScene()) {
render::ScenePointer scene = qApp->getMain3DScene();
render::Transaction transaction;
avatar->removeFromScene(*itr, scene, transaction);
if (scene) {
scene->enqueueTransaction(transaction);
}
}
// only remove from _avatarsToFade if we're sure its motionState has been removed from PhysicsEngine
if (_motionStatesToRemoveFromPhysics.empty()) {
fadingIterator = _avatarFades.erase(fadingIterator);
itr = _avatarsToFade.erase(itr);
} else {
++fadingIterator;
++itr;
}
} else {
const bool inView = true; // HACK
avatar->simulate(deltaTime, inView);
++fadingIterator;
++itr;
}
}
scene->enqueuePendingChanges(pendingChanges);
}
AvatarSharedPointer AvatarManager::newSharedAvatar() {
return std::make_shared<Avatar>(std::make_shared<Rig>());
}
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
auto newAvatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer);
auto rawRenderableAvatar = std::static_pointer_cast<Avatar>(newAvatar);
rawRenderableAvatar->addToScene(rawRenderableAvatar);
return newAvatar;
}
// virtual
void AvatarManager::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) {
QWriteLocker locker(&_hashLock);
auto removedAvatar = _avatarHash.take(sessionUUID);
if (removedAvatar) {
handleRemovedAvatar(removedAvatar, removalReason);
void AvatarManager::processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
PerformanceTimer perfTimer("receiveAvatar");
// enumerate over all of the avatars in this packet
// only add them if mixerWeakPointer points to something (meaning that mixer is still around)
while (message->getBytesLeftToRead()) {
AvatarSharedPointer avatarData = parseAvatarData(message, sendingNode);
if (avatarData) {
auto avatar = std::static_pointer_cast<Avatar>(avatarData);
if (avatar->isInScene()) {
if (!_shouldRender) {
// rare transition so we process the transaction immediately
render::ScenePointer scene = qApp->getMain3DScene();
if (scene) {
render::Transaction transaction;
avatar->removeFromScene(avatar, scene, transaction);
scene->enqueueTransaction(transaction);
}
}
} else if (_shouldRender) {
// very rare transition so we process the transaction immediately
render::ScenePointer scene = qApp->getMain3DScene();
if (scene) {
render::Transaction transaction;
avatar->addToScene(avatar, scene, transaction);
scene->enqueueTransaction(transaction);
}
}
}
}
}
@ -353,35 +388,46 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
DependencyManager::get<NodeList>()->removeFromIgnoreMuteSets(avatar->getSessionUUID());
DependencyManager::get<UsersScriptingInterface>()->avatarDisconnected(avatar->getSessionUUID());
}
_avatarFades.push_back(removedAvatar);
_avatarsToFade.push_back(removedAvatar);
}
void AvatarManager::clearOtherAvatars() {
// clear any avatars that came from an avatar-mixer
QWriteLocker locker(&_hashLock);
// Remove other avatars from the world but don't actually remove them from _avatarHash
// each will either be removed on timeout or will re-added to the world on receipt of update.
render::ScenePointer scene = qApp->getMain3DScene();
render::Transaction transaction;
QReadLocker locker(&_hashLock);
AvatarHash::iterator avatarIterator = _avatarHash.begin();
while (avatarIterator != _avatarHash.end()) {
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
if (avatar == _myAvatar || !avatar->isInitialized()) {
// don't remove myAvatar or uninitialized avatars from the list
++avatarIterator;
} else {
auto removedAvatar = avatarIterator.value();
avatarIterator = _avatarHash.erase(avatarIterator);
handleRemovedAvatar(removedAvatar);
if (avatar != _myAvatar) {
if (avatar->isInScene()) {
avatar->removeFromScene(avatar, scene, transaction);
}
AvatarMotionState* motionState = avatar->getMotionState();
if (motionState) {
_motionStatesThatMightUpdate.remove(motionState);
_motionStatesToAddToPhysics.remove(motionState);
_motionStatesToRemoveFromPhysics.push_back(motionState);
}
}
++avatarIterator;
}
if (scene) {
scene->enqueueTransaction(transaction);
}
_myAvatar->clearLookAtTargetAvatar();
}
void AvatarManager::clearAllAvatars() {
clearOtherAvatars();
QWriteLocker locker(&_hashLock);
handleRemovedAvatar(_myAvatar);
void AvatarManager::deleteAllAvatars() {
QReadLocker locker(&_hashLock);
AvatarHash::iterator avatarIterator = _avatarHash.begin();
while (avatarIterator != _avatarHash.end()) {
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
avatarIterator = _avatarHash.erase(avatarIterator);
avatar->die();
}
}
void AvatarManager::setLocalLights(const QVector<AvatarManager::LocalLight>& localLights) {
@ -475,26 +521,25 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
}
void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars()) {
_shouldRender = shouldRenderAvatars;
render::ScenePointer scene = qApp->getMain3DScene();
render::Transaction transaction;
if (_shouldRender) {
for (auto avatarData : _avatarHash) {
auto avatar = std::static_pointer_cast<Avatar>(avatarData);
render::ScenePointer scene = qApp->getMain3DScene();
render::PendingChanges pendingChanges;
avatar->addToScene(avatar, scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
avatar->addToScene(avatar, scene, transaction);
}
} else {
for (auto avatarData : _avatarHash) {
auto avatar = std::static_pointer_cast<Avatar>(avatarData);
render::ScenePointer scene = qApp->getMain3DScene();
render::PendingChanges pendingChanges;
avatar->removeFromScene(avatar, scene, pendingChanges);
scene->enqueuePendingChanges(pendingChanges);
avatar->removeFromScene(avatar, scene, transaction);
}
}
if (scene) {
scene->enqueueTransaction(transaction);
}
}
AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) const {
if (sessionID == AVATAR_SELF_ID || sessionID == _myAvatar->getSessionUUID()) {
return _myAvatar;

View file

@ -24,6 +24,7 @@
#include "Avatar.h"
#include "AvatarMotionState.h"
#include "ScriptAvatar.h"
class MyAvatar;
class AudioInjector;
@ -41,6 +42,10 @@ public:
void init();
std::shared_ptr<MyAvatar> getMyAvatar() { return _myAvatar; }
// Null/Default-constructed QUuids will return MyAvatar
Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) override { return new ScriptAvatar(getAvatarBySessionID(avatarID)); }
AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const override;
int getNumAvatarsUpdated() const { return _numAvatarsUpdated; }
@ -53,7 +58,7 @@ public:
void postUpdate(float deltaTime);
void clearOtherAvatars();
void clearAllAvatars();
void deleteAllAvatars();
bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; }
@ -91,8 +96,8 @@ public slots:
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
void updateAvatarRenderStatus(bool shouldRenderAvatars);
private slots:
virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
protected slots:
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
private:
explicit AvatarManager(QObject* parent = 0);
@ -100,12 +105,15 @@ private:
void simulateAvatarFades(float deltaTime);
// virtual overrides
virtual AvatarSharedPointer newSharedAvatar() override;
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) override;
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
AvatarSharedPointer newSharedAvatar() override;
void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
QVector<AvatarSharedPointer> _avatarsToFade;
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
SetOfMotionStates _motionStatesToAddToPhysics;
QVector<AvatarSharedPointer> _avatarFades;
std::shared_ptr<MyAvatar> _myAvatar;
quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate.
@ -115,14 +123,11 @@ private:
std::list<QPointer<AudioInjector>> _collisionInjectors;
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
SetOfMotionStates _motionStatesToAddToPhysics;
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
RateCounter<> _myAvatarSendRate;
int _numAvatarsUpdated { 0 };
int _numAvatarsNotUpdated { 0 };
float _avatarSimulationTime { 0.0f };
bool _shouldRender { true };
};
Q_DECLARE_METATYPE(AvatarManager::LocalLight)

View file

@ -205,10 +205,10 @@ void CauterizedModel::updateRenderItems() {
uint32_t deleteGeometryCounter = self->getGeometryCounter();
render::PendingChanges pendingChanges;
render::Transaction transaction;
QList<render::ItemID> keys = self->getRenderItems().keys();
foreach (auto itemID, keys) {
pendingChanges.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) {
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, deleteGeometryCounter](CauterizedMeshPartPayload& data) {
if (data._model && data._model->isLoaded()) {
// Ensure the model geometry was not reset between frames
if (deleteGeometryCounter == data._model->getGeometryCounter()) {
@ -233,7 +233,7 @@ void CauterizedModel::updateRenderItems() {
});
}
scene->enqueuePendingChanges(pendingChanges);
scene->enqueueTransaction(transaction);
});
} else {
Model::updateRenderItems();

View file

@ -71,10 +71,19 @@ void Head::reset() {
}
void Head::simulate(float deltaTime, bool isMine) {
const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for
// grab the audio loudness from the owning avatar, if we have one
float audioLoudness = 0.0f;
if (_owningAvatar) {
audioLoudness = _owningAvatar->getAudioLoudness();
}
// Update audio trailing average for rendering facial animations
const float AUDIO_AVERAGING_SECS = 0.05f;
const float AUDIO_LONG_TERM_AVERAGING_SECS = 30.0f;
_averageLoudness = glm::mix(_averageLoudness, _audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f));
_averageLoudness = glm::mix(_averageLoudness, audioLoudness, glm::min(deltaTime / AUDIO_AVERAGING_SECS, 1.0f));
if (_longTermAverageLoudness == -1.0f) {
_longTermAverageLoudness = _averageLoudness;
@ -94,7 +103,7 @@ void Head::simulate(float deltaTime, bool isMine) {
if (typeid(*faceTracker) == typeid(DdeFaceTracker)) {
if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) {
calculateMouthShapes();
calculateMouthShapes(deltaTime);
const int JAW_OPEN_BLENDSHAPE = 21;
const int MMMM_BLENDSHAPE = 34;
@ -116,7 +125,7 @@ void Head::simulate(float deltaTime, bool isMine) {
_isEyeTrackerConnected = eyeTracker->isTracking();
}
}
if (!_isFaceTrackerConnected) {
if (!_isEyeTrackerConnected) {
@ -144,22 +153,23 @@ void Head::simulate(float deltaTime, bool isMine) {
_timeWithoutTalking += deltaTime;
if ((_averageLoudness - _longTermAverageLoudness) > TALKING_LOUDNESS) {
_timeWithoutTalking = 0.0f;
} else if (_timeWithoutTalking < BLINK_AFTER_TALKING && _timeWithoutTalking >= BLINK_AFTER_TALKING) {
forceBlink = true;
}
// Update audio attack data for facial animation (eyebrows and mouth)
const float AUDIO_ATTACK_AVERAGING_RATE = 0.9f;
_audioAttack = AUDIO_ATTACK_AVERAGING_RATE * _audioAttack + (1.0f - AUDIO_ATTACK_AVERAGING_RATE) * fabs((_audioLoudness - _longTermAverageLoudness) - _lastLoudness);
_lastLoudness = (_audioLoudness - _longTermAverageLoudness);
float audioAttackAveragingRate = (10.0f - deltaTime * NORMAL_HZ) / 10.0f; // --> 0.9 at 60 Hz
_audioAttack = audioAttackAveragingRate * _audioAttack +
(1.0f - audioAttackAveragingRate) * fabs((audioLoudness - _longTermAverageLoudness) - _lastLoudness);
_lastLoudness = (audioLoudness - _longTermAverageLoudness);
const float BROW_LIFT_THRESHOLD = 100.0f;
if (_audioAttack > BROW_LIFT_THRESHOLD) {
_browAudioLift += sqrtf(_audioAttack) * 0.01f;
}
_browAudioLift = glm::clamp(_browAudioLift *= 0.7f, 0.0f, 1.0f);
const float BLINK_SPEED = 10.0f;
const float BLINK_SPEED_VARIABILITY = 1.0f;
const float BLINK_START_VARIABILITY = 0.25f;
@ -182,23 +192,23 @@ void Head::simulate(float deltaTime, bool isMine) {
} else {
_leftEyeBlink = glm::clamp(_leftEyeBlink + _leftEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED);
_rightEyeBlink = glm::clamp(_rightEyeBlink + _rightEyeBlinkVelocity * deltaTime, FULLY_OPEN, FULLY_CLOSED);
if (_leftEyeBlink == FULLY_CLOSED) {
_leftEyeBlinkVelocity = -BLINK_SPEED;
} else if (_leftEyeBlink == FULLY_OPEN) {
_leftEyeBlinkVelocity = 0.0f;
}
if (_rightEyeBlink == FULLY_CLOSED) {
_rightEyeBlinkVelocity = -BLINK_SPEED;
} else if (_rightEyeBlink == FULLY_OPEN) {
_rightEyeBlinkVelocity = 0.0f;
}
}
// use data to update fake Faceshift blendshape coefficients
calculateMouthShapes();
calculateMouthShapes(deltaTime);
DependencyManager::get<Faceshift>()->updateFakeCoefficients(_leftEyeBlink,
_rightEyeBlink,
_browAudioLift,
@ -216,7 +226,7 @@ void Head::simulate(float deltaTime, bool isMine) {
if (Menu::getInstance()->isOptionChecked(MenuOption::FixGaze)) { // if debug menu turns off, use no saccade
_saccade = glm::vec3();
}
_leftEyePosition = _rightEyePosition = getPosition();
_eyePosition = getPosition();
@ -230,7 +240,7 @@ void Head::simulate(float deltaTime, bool isMine) {
_eyePosition = calculateAverageEyePosition();
}
void Head::calculateMouthShapes() {
void Head::calculateMouthShapes(float deltaTime) {
const float JAW_OPEN_SCALE = 0.015f;
const float JAW_OPEN_RATE = 0.9f;
const float JAW_CLOSE_RATE = 0.90f;
@ -242,20 +252,24 @@ void Head::calculateMouthShapes() {
const float SMILE_SPEED = 1.0f;
const float FUNNEL_SPEED = 2.335f;
const float STOP_GAIN = 5.0f;
const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for
float deltaTimeRatio = deltaTime / (1.0f / NORMAL_HZ);
// From the change in loudness, decide how much to open or close the jaw
float audioDelta = sqrtf(glm::max(_averageLoudness - _longTermAverageLoudness, 0.0f)) * JAW_OPEN_SCALE;
if (audioDelta > _audioJawOpen) {
_audioJawOpen += (audioDelta - _audioJawOpen) * JAW_OPEN_RATE;
_audioJawOpen += (audioDelta - _audioJawOpen) * JAW_OPEN_RATE * deltaTimeRatio;
} else {
_audioJawOpen *= JAW_CLOSE_RATE;
_audioJawOpen *= powf(JAW_CLOSE_RATE, deltaTimeRatio);
}
_audioJawOpen = glm::clamp(_audioJawOpen, 0.0f, 1.0f);
_trailingAudioJawOpen = glm::mix(_trailingAudioJawOpen, _audioJawOpen, 0.99f);
float trailingAudioJawOpenRatio = (100.0f - deltaTime * NORMAL_HZ) / 100.0f; // --> 0.99 at 60 Hz
_trailingAudioJawOpen = glm::mix(_trailingAudioJawOpen, _audioJawOpen, trailingAudioJawOpenRatio);
// Advance time at a rate proportional to loudness, and move the mouth shapes through
// Advance time at a rate proportional to loudness, and move the mouth shapes through
// a cycle at differing speeds to create a continuous random blend of shapes.
_mouthTime += sqrtf(_averageLoudness) * TIMESTEP_CONSTANT;
_mouthTime += sqrtf(_averageLoudness) * TIMESTEP_CONSTANT * deltaTimeRatio;
_mouth2 = (sinf(_mouthTime * MMMM_SPEED) + 1.0f) * MMMM_POWER * glm::min(1.0f, _trailingAudioJawOpen * STOP_GAIN);
_mouth3 = (sinf(_mouthTime * FUNNEL_SPEED) + 1.0f) * FUNNEL_POWER * glm::min(1.0f, _trailingAudioJawOpen * STOP_GAIN);
_mouth4 = (sinf(_mouthTime * SMILE_SPEED) + 1.0f) * SMILE_POWER * glm::min(1.0f, _trailingAudioJawOpen * STOP_GAIN);
@ -321,7 +335,7 @@ glm::quat Head::getFinalOrientationInLocalFrame() const {
//
// Everyone else's head also keeps track of a correctedLookAtPosition that may be different for the same head within
// different Interfaces. If that head is not looking at me, the correctedLookAtPosition is the same as the lookAtPosition.
// However, if that head is looking at me, then I will attempt to adjust the lookAtPosition by the difference between
// However, if that head is looking at me, then I will attempt to adjust the lookAtPosition by the difference between
// my (singular) eye position and my actual camera position. This adjustment is used on their eyeballs during rendering
// (and also on any lookAt vector display for that head, during rendering). Note that:
// 1. this adjustment can be made directly to the other head's eyeball joints, because we won't be send their joint information to others.

View file

@ -138,7 +138,7 @@ private:
int _rightEyeLookAtID;
// private methods
void calculateMouthShapes();
void calculateMouthShapes(float timeRatio);
void applyEyelidOffset(glm::quat headOrientation);
};

View file

@ -54,6 +54,7 @@
#include "DebugDraw.h"
#include "EntityEditPacketSender.h"
#include "MovingEntitiesOperator.h"
#include "SceneScriptingInterface.h"
using namespace std;
@ -403,8 +404,8 @@ void MyAvatar::update(float deltaTime) {
// Also get the AudioClient so we can update the avatar bounding box data
// on the AudioClient side.
auto audio = DependencyManager::get<AudioClient>();
head->setAudioLoudness(audio->getLastInputLoudness());
head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
setAudioLoudness(audio->getLastInputLoudness());
setAudioAverageLoudness(audio->getAudioAverageInputLoudness());
glm::vec3 halfBoundingBoxDimensions(_characterController.getCapsuleRadius(), _characterController.getCapsuleHalfHeight(), _characterController.getCapsuleRadius());
halfBoundingBoxDimensions += _characterController.getCapsuleLocalOffset();
@ -1345,6 +1346,45 @@ controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const {
return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
void MyAvatar::setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
if (controller::InputDevice::getLowVelocityFilter()) {
auto oldLeftPose = getLeftFootControllerPoseInSensorFrame();
auto oldRightPose = getRightFootControllerPoseInSensorFrame();
_leftFootControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldLeftPose, left));
_rightFootControllerPoseInSensorFrameCache.set(applyLowVelocityFilter(oldRightPose, right));
} else {
_leftFootControllerPoseInSensorFrameCache.set(left);
_rightFootControllerPoseInSensorFrameCache.set(right);
}
}
controller::Pose MyAvatar::getLeftFootControllerPoseInSensorFrame() const {
return _leftFootControllerPoseInSensorFrameCache.get();
}
controller::Pose MyAvatar::getRightFootControllerPoseInSensorFrame() const {
return _rightFootControllerPoseInSensorFrameCache.get();
}
controller::Pose MyAvatar::getLeftFootControllerPoseInWorldFrame() const {
return _leftFootControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getRightFootControllerPoseInWorldFrame() const {
return _rightFootControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getLeftFootControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getLeftFootControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
controller::Pose MyAvatar::getRightFootControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getRightFootControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
void MyAvatar::updateMotors() {
_characterController.clearMotors();
glm::quat motorRotation;
@ -1595,7 +1635,7 @@ void MyAvatar::postUpdate(float deltaTime) {
Avatar::postUpdate(deltaTime);
render::ScenePointer scene = qApp->getMain3DScene();
if (_skeletonModel->initWhenReady(scene)) {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars() && _skeletonModel->initWhenReady(scene)) {
initHeadBones();
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();

View file

@ -445,6 +445,14 @@ public:
controller::Pose getLeftHandControllerPoseInAvatarFrame() const;
controller::Pose getRightHandControllerPoseInAvatarFrame() const;
void setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right);
controller::Pose getLeftFootControllerPoseInSensorFrame() const;
controller::Pose getRightFootControllerPoseInSensorFrame() const;
controller::Pose getLeftFootControllerPoseInWorldFrame() const;
controller::Pose getRightFootControllerPoseInWorldFrame() const;
controller::Pose getLeftFootControllerPoseInAvatarFrame() const;
controller::Pose getRightFootControllerPoseInAvatarFrame() const;
bool hasDriveInput() const;
Q_INVOKABLE void setCharacterControllerEnabled(bool enabled);
@ -684,6 +692,9 @@ private:
ThreadSafeValueCache<controller::Pose> _leftHandControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightHandControllerPoseInSensorFrameCache { controller::Pose() };
ThreadSafeValueCache<controller::Pose> _leftFootControllerPoseInSensorFrameCache{ controller::Pose() };
ThreadSafeValueCache<controller::Pose> _rightFootControllerPoseInSensorFrameCache{ controller::Pose() };
bool _hmdLeanRecenterEnabled = true;
AnimPose _prePhysicsRoomPose;

View file

@ -0,0 +1,146 @@
//
// ScriptAvatar.cpp
// interface/src/avatars
//
// Created by Stephen Birarda on 4/10/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
//
#include "ScriptAvatar.h"
ScriptAvatar::ScriptAvatar(AvatarSharedPointer avatarData) :
ScriptAvatarData(avatarData)
{
}
std::shared_ptr<Avatar> ScriptAvatar::lockAvatar() const {
if (auto lockedAvatarData = _avatarData.lock()) {
return std::dynamic_pointer_cast<Avatar>(lockedAvatarData);
} else {
return std::shared_ptr<Avatar>();
}
}
glm::quat ScriptAvatar::getDefaultJointRotation(int index) const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getDefaultJointRotation(index);
} else {
return glm::quat();
}
}
glm::vec3 ScriptAvatar::getDefaultJointTranslation(int index) const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getDefaultJointTranslation(index);
} else {
return glm::vec3();
}
}
glm::vec3 ScriptAvatar::getSkeletonOffset() const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getSkeletonOffset();
} else {
return glm::vec3();
}
}
glm::vec3 ScriptAvatar::getJointPosition(int index) const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getJointPosition(index);
} else {
return glm::vec3();
}
}
glm::vec3 ScriptAvatar::getJointPosition(const QString& name) const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getJointPosition(name);
} else {
return glm::vec3();
}
}
glm::vec3 ScriptAvatar::getNeckPosition() const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getNeckPosition();
} else {
return glm::vec3();
}
}
glm::vec3 ScriptAvatar::getAcceleration() const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getAcceleration();
} else {
return glm::vec3();
}
}
QUuid ScriptAvatar::getParentID() const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getParentID();
} else {
return QUuid();
}
}
quint16 ScriptAvatar::getParentJointIndex() const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getParentJointIndex();
} else {
return INVALID_JOINT_INDEX;
}
}
QVariantList ScriptAvatar::getSkeleton() const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getSkeleton();
} else {
return QVariantList();
}
}
float ScriptAvatar::getSimulationRate(const QString& rateName) const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getSimulationRate(rateName);
} else {
return 0.0f;;
}
}
glm::vec3 ScriptAvatar::getLeftPalmPosition() const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getLeftPalmPosition();
} else {
return glm::vec3();
}
}
glm::quat ScriptAvatar::getLeftPalmRotation() const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getLeftPalmRotation();
} else {
return glm::quat();
}
}
glm::vec3 ScriptAvatar::getRightPalmPosition() const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getRightPalmPosition();
} else {
return glm::vec3();
}
}
glm::quat ScriptAvatar::getRightPalmRotation() const {
if (auto lockedAvatar = lockAvatar()) {
return lockedAvatar->getRightPalmRotation();
} else {
return glm::quat();
}
}

View file

@ -0,0 +1,56 @@
//
// ScriptAvatar.h
// interface/src/avatars
//
// Created by Stephen Birarda on 4/10/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_ScriptAvatar_h
#define hifi_ScriptAvatar_h
#include <ScriptAvatarData.h>
#include "Avatar.h"
class ScriptAvatar : public ScriptAvatarData {
Q_OBJECT
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset)
public:
ScriptAvatar(AvatarSharedPointer avatarData);
public slots:
glm::quat getDefaultJointRotation(int index) const;
glm::vec3 getDefaultJointTranslation(int index) const;
glm::vec3 getSkeletonOffset() const;
glm::vec3 getJointPosition(int index) const;
glm::vec3 getJointPosition(const QString& name) const;
glm::vec3 getNeckPosition() const;
glm::vec3 getAcceleration() const;
QUuid getParentID() const;
quint16 getParentJointIndex() const;
QVariantList getSkeleton() const;
float getSimulationRate(const QString& rateName = QString("")) const;
glm::vec3 getLeftPalmPosition() const;
glm::quat getLeftPalmRotation() const;
glm::vec3 getRightPalmPosition() const;
glm::quat getRightPalmRotation() const;
private:
std::shared_ptr<Avatar> lockAvatar() const;
};
#endif // hifi_ScriptAvatar_h

View file

@ -132,31 +132,49 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
_rig->updateFromHeadParameters(headParams, deltaTime);
Rig::HandParameters handParams;
Rig::HandAndFeetParameters handAndFeetParams;
auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame();
if (leftPose.isValid()) {
handParams.isLeftEnabled = true;
handParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation();
handParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation();
handAndFeetParams.isLeftEnabled = true;
handAndFeetParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation();
handAndFeetParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation();
} else {
handParams.isLeftEnabled = false;
handAndFeetParams.isLeftEnabled = false;
}
auto rightPose = myAvatar->getRightHandControllerPoseInAvatarFrame();
if (rightPose.isValid()) {
handParams.isRightEnabled = true;
handParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation();
handParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation();
handAndFeetParams.isRightEnabled = true;
handAndFeetParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation();
handAndFeetParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation();
} else {
handParams.isRightEnabled = false;
handAndFeetParams.isRightEnabled = false;
}
handParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius();
handParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight();
handParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset();
auto leftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame();
if (leftFootPose.isValid()) {
handAndFeetParams.isLeftFootEnabled = true;
handAndFeetParams.leftFootPosition = Quaternions::Y_180 * leftFootPose.getTranslation();
handAndFeetParams.leftFootOrientation = Quaternions::Y_180 * leftFootPose.getRotation();
} else {
handAndFeetParams.isLeftFootEnabled = false;
}
_rig->updateFromHandParameters(handParams, deltaTime);
auto rightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame();
if (rightFootPose.isValid()) {
handAndFeetParams.isRightFootEnabled = true;
handAndFeetParams.rightFootPosition = Quaternions::Y_180 * rightFootPose.getTranslation();
handAndFeetParams.rightFootOrientation = Quaternions::Y_180 * rightFootPose.getRotation();
} else {
handAndFeetParams.isRightFootEnabled = false;
}
handAndFeetParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius();
handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight();
handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset();
_rig->updateFromHandAndFeetParameters(handAndFeetParams, deltaTime);
Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState());
@ -222,8 +240,8 @@ void SkeletonModel::updateAttitude() {
// Called by Avatar::simulate after it has set the joint states (fullUpdate true if changed),
// but just before head has been simulated.
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
updateAttitude();
if (fullUpdate) {
updateAttitude();
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
Model::simulate(deltaTime, fullUpdate);

View file

@ -0,0 +1,26 @@
//
// FileTypeProfile.cpp
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// 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
//
#include "FileTypeProfile.h"
#include "FileTypeRequestInterceptor.h"
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
FileTypeProfile::FileTypeProfile(QObject* parent) :
QQuickWebEngineProfile(parent)
{
static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
auto requestInterceptor = new FileTypeRequestInterceptor(this);
setRequestInterceptor(requestInterceptor);
}

View file

@ -0,0 +1,25 @@
//
// FileTypeProfile.h
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// 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
//
#pragma once
#ifndef hifi_FileTypeProfile_h
#define hifi_FileTypeProfile_h
#include <QtWebEngine/QQuickWebEngineProfile>
class FileTypeProfile : public QQuickWebEngineProfile {
public:
FileTypeProfile(QObject* parent = Q_NULLPTR);
};
#endif // hifi_FileTypeProfile_h

View file

@ -0,0 +1,21 @@
//
// FileTypeRequestInterceptor.cpp
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// 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
//
#include "FileTypeRequestInterceptor.h"
#include <QtCore/QDebug>
#include "RequestFilters.h"
void FileTypeRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
RequestFilters::interceptHFWebEngineRequest(info);
RequestFilters::interceptFileType(info);
}

View file

@ -0,0 +1,26 @@
//
// FileTypeRequestInterceptor.h
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// 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
//
#pragma once
#ifndef hifi_FileTypeRequestInterceptor_h
#define hifi_FileTypeRequestInterceptor_h
#include <QWebEngineUrlRequestInterceptor>
class FileTypeRequestInterceptor : public QWebEngineUrlRequestInterceptor {
public:
FileTypeRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {};
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
#endif // hifi_FileTypeRequestInterceptor_h

View file

@ -0,0 +1,26 @@
//
// HFTabletWebEngineProfile.h
// interface/src/networking
//
// Created by Dante Ruiz on 2017-03-31.
// 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
//
#include "HFTabletWebEngineProfile.h"
#include "HFTabletWebEngineRequestInterceptor.h"
static const QString QML_WEB_ENGINE_NAME = "qmlTabletWebEngine";
HFTabletWebEngineProfile::HFTabletWebEngineProfile(QObject* parent) : QQuickWebEngineProfile(parent) {
static const QString WEB_ENGINE_USER_AGENT = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
auto requestInterceptor = new HFTabletWebEngineRequestInterceptor(this);
setRequestInterceptor(requestInterceptor);
}

View file

@ -0,0 +1,23 @@
//
// HFTabletWebEngineProfile.h
// interface/src/networking
//
// Created by Dante Ruiz on 2017-03-31.
// 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_HFTabletWebEngineProfile_h
#define hifi_HFTabletWebEngineProfile_h
#include <QtWebEngine/QQuickWebEngineProfile>
class HFTabletWebEngineProfile : public QQuickWebEngineProfile {
public:
HFTabletWebEngineProfile(QObject* parent = Q_NULLPTR);
};
#endif // hifi_HFTabletWebEngineProfile_h

View file

@ -0,0 +1,42 @@
//
// HFTabletWebEngineRequestInterceptor.cpp
// interface/src/networking
//
// Created by Dante Ruiz on 2017-3-31.
// 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
//
#include "HFTabletWebEngineRequestInterceptor.h"
#include <QtCore/QDebug>
#include <AccountManager.h>
bool isTabletAuthableHighFidelityURL(const QUrl& url) {
static const QStringList HF_HOSTS = {
"highfidelity.com", "highfidelity.io",
"metaverse.highfidelity.com", "metaverse.highfidelity.io"
};
return url.scheme() == "https" && HF_HOSTS.contains(url.host());
}
void HFTabletWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
// check if this is a request to a highfidelity URL
if (isTabletAuthableHighFidelityURL(info.requestUrl())) {
// if we have an access token, add it to the right HTTP header for authorization
auto accountManager = DependencyManager::get<AccountManager>();
if (accountManager->hasValidAccessToken()) {
static const QString OAUTH_AUTHORIZATION_HEADER = "Authorization";
QString bearerTokenString = "Bearer " + accountManager->getAccountInfo().getAccessToken().token;
info.setHttpHeader(OAUTH_AUTHORIZATION_HEADER.toLocal8Bit(), bearerTokenString.toLocal8Bit());
}
}
static const QString USER_AGENT = "User-Agent";
QString tokenString = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
}

View file

@ -0,0 +1,24 @@
//
// HFTabletWebEngineRequestInterceptor.h
// interface/src/networking
//
// Created by Dante Ruiz on 2017-3-31.
// 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_HFTabletWebEngineRequestInterceptor_h
#define hifi_HFTabletWebEngineRequestInterceptor_h
#include <QWebEngineUrlRequestInterceptor>
class HFTabletWebEngineRequestInterceptor : public QWebEngineUrlRequestInterceptor {
public:
HFTabletWebEngineRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {};
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
};
#endif // hifi_HFWebEngineRequestInterceptor_h

View file

@ -14,27 +14,8 @@
#include <QtCore/QDebug>
#include <AccountManager.h>
bool isAuthableHighFidelityURL(const QUrl& url) {
static const QStringList HF_HOSTS = {
"highfidelity.com", "highfidelity.io",
"metaverse.highfidelity.com", "metaverse.highfidelity.io"
};
return url.scheme() == "https" && HF_HOSTS.contains(url.host());
}
#include "RequestFilters.h"
void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
// check if this is a request to a highfidelity URL
if (isAuthableHighFidelityURL(info.requestUrl())) {
// if we have an access token, add it to the right HTTP header for authorization
auto accountManager = DependencyManager::get<AccountManager>();
if (accountManager->hasValidAccessToken()) {
static const QString OAUTH_AUTHORIZATION_HEADER = "Authorization";
QString bearerTokenString = "Bearer " + accountManager->getAccountInfo().getAccessToken().token;
info.setHttpHeader(OAUTH_AUTHORIZATION_HEADER.toLocal8Bit(), bearerTokenString.toLocal8Bit());
}
}
RequestFilters::interceptHFWebEngineRequest(info);
}

View file

@ -0,0 +1,65 @@
//
// RequestFilters.cpp
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// 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
//
#include "RequestFilters.h"
#include "NetworkingConstants.h"
#include <QtCore/QDebug>
#include <AccountManager.h>
namespace {
bool isAuthableHighFidelityURL(const QUrl& url) {
static const QStringList HF_HOSTS = {
"highfidelity.com", "highfidelity.io",
"metaverse.highfidelity.com", "metaverse.highfidelity.io"
};
const auto& scheme = url.scheme();
const auto& host = url.host();
return (scheme == "https" && HF_HOSTS.contains(host)) ||
((scheme == NetworkingConstants::METAVERSE_SERVER_URL.scheme()) && (host == NetworkingConstants::METAVERSE_SERVER_URL.host()));
}
bool isScript(const QString filename) {
return filename.endsWith(".js", Qt::CaseInsensitive);
}
bool isJSON(const QString filename) {
return filename.endsWith(".json", Qt::CaseInsensitive);
}
}
void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info) {
// check if this is a request to a highfidelity URL
if (isAuthableHighFidelityURL(info.requestUrl())) {
// if we have an access token, add it to the right HTTP header for authorization
auto accountManager = DependencyManager::get<AccountManager>();
if (accountManager->hasValidAccessToken()) {
static const QString OAUTH_AUTHORIZATION_HEADER = "Authorization";
QString bearerTokenString = "Bearer " + accountManager->getAccountInfo().getAccessToken().token;
info.setHttpHeader(OAUTH_AUTHORIZATION_HEADER.toLocal8Bit(), bearerTokenString.toLocal8Bit());
}
}
}
void RequestFilters::interceptFileType(QWebEngineUrlRequestInfo& info) {
QString filename = info.requestUrl().fileName();
if (isScript(filename) || isJSON(filename)) {
static const QString CONTENT_HEADER = "Accept";
static const QString TYPE_VALUE = "text/plain,text/html";
info.setHttpHeader(CONTENT_HEADER.toLocal8Bit(), TYPE_VALUE.toLocal8Bit());
}
}

View file

@ -0,0 +1,28 @@
//
// RequestFilters.h
// interface/src/networking
//
// Created by Kunal Gosar on 2017-03-10.
// 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
//
#pragma once
#ifndef hifi_RequestFilters_h
#define hifi_RequestFilters_h
#include <QObject>
#include <QWebEngineUrlRequestInfo>
class RequestFilters : public QObject {
Q_OBJECT
public:
static void interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info);
static void interceptFileType(QWebEngineUrlRequestInfo& info);
};
#endif // hifi_RequestFilters_h

View file

@ -53,33 +53,19 @@ void GlobalServicesScriptingInterface::loggedOut() {
emit GlobalServicesScriptingInterface::disconnected(QString("logout"));
}
QString GlobalServicesScriptingInterface::findableByString(Discoverability::Mode discoverabilityMode) const {
if (discoverabilityMode == Discoverability::None) {
return "none";
} else if (discoverabilityMode == Discoverability::Friends) {
return "friends";
} else if (discoverabilityMode == Discoverability::All) {
return "all";
} else {
qDebug() << "GlobalServices findableByString called with an unrecognized value.";
return "";
}
}
QString GlobalServicesScriptingInterface::getFindableBy() const {
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
return findableByString(discoverabilityManager->getDiscoverabilityMode());
return DiscoverabilityManager::findableByString(discoverabilityManager->getDiscoverabilityMode());
}
void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabilityMode) {
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
if (discoverabilityMode.toLower() == "none") {
discoverabilityManager->setDiscoverabilityMode(Discoverability::None);
} else if (discoverabilityMode.toLower() == "friends") {
discoverabilityManager->setDiscoverabilityMode(Discoverability::Friends);
} else if (discoverabilityMode.toLower() == "connections") {
discoverabilityManager->setDiscoverabilityMode(Discoverability::Connections);
} else if (discoverabilityMode.toLower() == "all") {
discoverabilityManager->setDiscoverabilityMode(Discoverability::All);
} else {
@ -88,7 +74,7 @@ void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabil
}
void GlobalServicesScriptingInterface::discoverabilityModeChanged(Discoverability::Mode discoverabilityMode) {
emit findableByChanged(findableByString(discoverabilityMode));
emit findableByChanged(DiscoverabilityManager::findableByString(discoverabilityMode));
}
DownloadInfoResult::DownloadInfoResult() :

View file

@ -18,6 +18,7 @@
#include <QScriptValue>
#include <QString>
#include <QStringList>
#include <DiscoverabilityManager.h>
class DownloadInfoResult {
public:
@ -35,7 +36,7 @@ class GlobalServicesScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(QString username READ getUsername)
Q_PROPERTY(QString findableBy READ getFindableBy WRITE setFindableBy)
Q_PROPERTY(QString findableBy READ getFindableBy WRITE setFindableBy NOTIFY findableByChanged)
public:
static GlobalServicesScriptingInterface* getInstance();
@ -65,8 +66,6 @@ private:
GlobalServicesScriptingInterface();
~GlobalServicesScriptingInterface();
QString findableByString(Discoverability::Mode discoverabilityMode) const;
bool _downloading;
};

View file

@ -27,7 +27,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
Q_OBJECT
Q_PROPERTY(glm::vec3 position READ getPosition)
Q_PROPERTY(glm::quat orientation READ getOrientation)
Q_PROPERTY(bool mounted READ isMounted)
Q_PROPERTY(bool mounted READ isMounted NOTIFY mountedChanged)
Q_PROPERTY(bool showTablet READ getShouldShowTablet)
Q_PROPERTY(QUuid tabletID READ getCurrentTabletFrameID WRITE setCurrentTabletFrameID)
Q_PROPERTY(QUuid homeButtonID READ getCurrentHomeButtonID WRITE setCurrentHomeButtonID)
@ -80,6 +80,7 @@ public:
signals:
bool shouldShowHandControllersChanged();
void mountedChanged();
public:
HMDScriptingInterface();

View file

@ -0,0 +1,91 @@
#include <QJsonDocument>
#include <QJsonArray>
#include <src/InterfaceLogging.h>
#include <src/ui/AvatarInputs.h>
#include "LimitlessConnection.h"
#include "LimitlessVoiceRecognitionScriptingInterface.h"
LimitlessConnection::LimitlessConnection() :
_streamingAudioForTranscription(false)
{
}
void LimitlessConnection::startListening(QString authCode) {
_transcribeServerSocket.reset(new QTcpSocket(this));
connect(_transcribeServerSocket.get(), &QTcpSocket::readyRead, this,
&LimitlessConnection::transcriptionReceived);
connect(_transcribeServerSocket.get(), &QTcpSocket::disconnected, this, [this](){stopListening();});
static const auto host = "gserv_devel.studiolimitless.com";
_transcribeServerSocket->connectToHost(host, 1407);
_transcribeServerSocket->waitForConnected();
QString requestHeader = QString::asprintf("Authorization: %s\r\nfs: %i\r\n",
authCode.toLocal8Bit().data(), AudioConstants::SAMPLE_RATE);
qCDebug(interfaceapp) << "Sending Limitless Audio Stream Request: " << requestHeader;
_transcribeServerSocket->write(requestHeader.toLocal8Bit());
_transcribeServerSocket->waitForBytesWritten();
}
void LimitlessConnection::stopListening() {
emit onFinishedSpeaking(_currentTranscription);
_streamingAudioForTranscription = false;
_currentTranscription = "";
if (!isConnected())
return;
_transcribeServerSocket->close();
disconnect(_transcribeServerSocket.get(), &QTcpSocket::readyRead, this,
&LimitlessConnection::transcriptionReceived);
_transcribeServerSocket.release()->deleteLater();
disconnect(DependencyManager::get<AudioClient>().data(), &AudioClient::inputReceived, this,
&LimitlessConnection::audioInputReceived);
qCDebug(interfaceapp) << "Connection to Limitless Voice Server closed.";
}
void LimitlessConnection::audioInputReceived(const QByteArray& inputSamples) {
if (isConnected()) {
_transcribeServerSocket->write(inputSamples.data(), inputSamples.size());
_transcribeServerSocket->waitForBytesWritten();
}
}
void LimitlessConnection::transcriptionReceived() {
while (_transcribeServerSocket && _transcribeServerSocket->bytesAvailable() > 0) {
const QByteArray data = _transcribeServerSocket->readAll();
_serverDataBuffer.append(data);
int begin = _serverDataBuffer.indexOf('<');
int end = _serverDataBuffer.indexOf('>');
while (begin > -1 && end > -1) {
const int len = end - begin;
const QByteArray serverMessage = _serverDataBuffer.mid(begin+1, len-1);
if (serverMessage.contains("1407")) {
qCDebug(interfaceapp) << "Limitless Speech Server denied the request.";
// Don't spam the server with further false requests please.
DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->setListeningToVoice(true);
stopListening();
return;
} else if (serverMessage.contains("1408")) {
qCDebug(interfaceapp) << "Limitless Audio request authenticated!";
_serverDataBuffer.clear();
connect(DependencyManager::get<AudioClient>().data(), &AudioClient::inputReceived, this,
&LimitlessConnection::audioInputReceived);
return;
}
QJsonObject json = QJsonDocument::fromJson(serverMessage.data()).object();
_serverDataBuffer.remove(begin, len+1);
_currentTranscription = json["alternatives"].toArray()[0].toObject()["transcript"].toString();
emit onReceivedTranscription(_currentTranscription);
if (json["isFinal"] == true) {
qCDebug(interfaceapp) << "Final transcription: " << _currentTranscription;
stopListening();
return;
}
begin = _serverDataBuffer.indexOf('<');
end = _serverDataBuffer.indexOf('>');
}
}
}
bool LimitlessConnection::isConnected() const {
return _transcribeServerSocket.get() && _transcribeServerSocket->isWritable()
&& _transcribeServerSocket->state() != QAbstractSocket::SocketState::UnconnectedState;
}

View file

@ -0,0 +1,44 @@
//
// SpeechRecognitionScriptingInterface.h
// interface/src/scripting
//
// Created by Trevor Berninger on 3/24/17.
// Copyright 2017 Limitless ltd.
//
// 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_LimitlessConnection_h
#define hifi_LimitlessConnection_h
#include <AudioClient.h>
#include <QObject>
#include <QFuture>
class LimitlessConnection : public QObject {
Q_OBJECT
public:
LimitlessConnection();
Q_INVOKABLE void startListening(QString authCode);
Q_INVOKABLE void stopListening();
std::atomic<bool> _streamingAudioForTranscription;
signals:
void onReceivedTranscription(QString speech);
void onFinishedSpeaking(QString speech);
private:
void transcriptionReceived();
void audioInputReceived(const QByteArray& inputSamples);
bool isConnected() const;
std::unique_ptr<QTcpSocket> _transcribeServerSocket;
QByteArray _serverDataBuffer;
QString _currentTranscription;
};
#endif //hifi_LimitlessConnection_h

View file

@ -0,0 +1,64 @@
//
// SpeechRecognitionScriptingInterface.h
// interface/src/scripting
//
// Created by Trevor Berninger on 3/20/17.
// Copyright 2017 Limitless ltd.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <src/InterfaceLogging.h>
#include <src/ui/AvatarInputs.h>
#include <QtConcurrent/QtConcurrentRun>
#include "LimitlessVoiceRecognitionScriptingInterface.h"
const float LimitlessVoiceRecognitionScriptingInterface::_audioLevelThreshold = 0.33f;
const int LimitlessVoiceRecognitionScriptingInterface::_voiceTimeoutDuration = 2000;
LimitlessVoiceRecognitionScriptingInterface::LimitlessVoiceRecognitionScriptingInterface() :
_shouldStartListeningForVoice(false)
{
_voiceTimer.setSingleShot(true);
connect(&_voiceTimer, &QTimer::timeout, this, &LimitlessVoiceRecognitionScriptingInterface::voiceTimeout);
connect(&_connection, &LimitlessConnection::onReceivedTranscription, this, [this](QString transcription){emit onReceivedTranscription(transcription);});
connect(&_connection, &LimitlessConnection::onFinishedSpeaking, this, [this](QString transcription){emit onFinishedSpeaking(transcription);});
_connection.moveToThread(&_connectionThread);
_connectionThread.setObjectName("Limitless Connection");
_connectionThread.start();
}
void LimitlessVoiceRecognitionScriptingInterface::update() {
const float audioLevel = AvatarInputs::getInstance()->loudnessToAudioLevel(DependencyManager::get<AudioClient>()->getAudioAverageInputLoudness());
if (_shouldStartListeningForVoice) {
if (_connection._streamingAudioForTranscription) {
if (audioLevel > _audioLevelThreshold) {
if (_voiceTimer.isActive()) {
_voiceTimer.stop();
}
} else if (!_voiceTimer.isActive()){
_voiceTimer.start(_voiceTimeoutDuration);
}
} else if (audioLevel > _audioLevelThreshold) {
// to make sure invoke doesn't get called twice before the method actually gets called
_connection._streamingAudioForTranscription = true;
QMetaObject::invokeMethod(&_connection, "startListening", Q_ARG(QString, authCode));
}
}
}
void LimitlessVoiceRecognitionScriptingInterface::setListeningToVoice(bool listening) {
_shouldStartListeningForVoice = listening;
}
void LimitlessVoiceRecognitionScriptingInterface::setAuthKey(QString key) {
authCode = key;
}
void LimitlessVoiceRecognitionScriptingInterface::voiceTimeout() {
if (_connection._streamingAudioForTranscription) {
QMetaObject::invokeMethod(&_connection, "stopListening");
}
}

View file

@ -0,0 +1,50 @@
//
// SpeechRecognitionScriptingInterface.h
// interface/src/scripting
//
// Created by Trevor Berninger on 3/20/17.
// Copyright 2017 Limitless ltd.
//
// 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_SpeechRecognitionScriptingInterface_h
#define hifi_SpeechRecognitionScriptingInterface_h
#include <AudioClient.h>
#include <QObject>
#include <QFuture>
#include "LimitlessConnection.h"
class LimitlessVoiceRecognitionScriptingInterface : public QObject, public Dependency {
Q_OBJECT
public:
LimitlessVoiceRecognitionScriptingInterface();
void update();
QString authCode;
public slots:
void setListeningToVoice(bool listening);
void setAuthKey(QString key);
signals:
void onReceivedTranscription(QString speech);
void onFinishedSpeaking(QString speech);
private:
bool _shouldStartListeningForVoice;
static const float _audioLevelThreshold;
static const int _voiceTimeoutDuration;
QTimer _voiceTimer;
QThread _connectionThread;
LimitlessConnection _connection;
void voiceTimeout();
};
#endif //hifi_SpeechRecognitionScriptingInterface_h

View file

@ -235,6 +235,14 @@ void WindowScriptingInterface::shareSnapshot(const QString& path, const QUrl& hr
qApp->shareSnapshot(path, href);
}
void WindowScriptingInterface::makeConnection(bool success, const QString& userNameOrError) {
if (success) {
emit connectionAdded(userNameOrError);
} else {
emit connectionError(userNameOrError);
}
}
bool WindowScriptingInterface::isPhysicsEnabled() {
return qApp->isPhysicsEnabled();
}
@ -255,7 +263,7 @@ int WindowScriptingInterface::openMessageBox(QString title, QString text, int bu
}
int WindowScriptingInterface::createMessageBox(QString title, QString text, int buttons, int defaultButton) {
auto messageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text,
auto messageBox = DependencyManager::get<OffscreenUi>()->createMessageBox(OffscreenUi::ICON_INFORMATION, title, text,
static_cast<QFlags<QMessageBox::StandardButton>>(buttons), static_cast<QMessageBox::StandardButton>(defaultButton));
connect(messageBox, SIGNAL(selected(int)), this, SLOT(onMessageBoxSelected(int)));

View file

@ -56,6 +56,7 @@ public slots:
void showAssetServer(const QString& upload = "");
void copyToClipboard(const QString& text);
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f);
void makeConnection(bool success, const QString& userNameOrError);
void shareSnapshot(const QString& path, const QUrl& href = QUrl(""));
bool isPhysicsEnabled();
@ -70,9 +71,13 @@ signals:
void domainChanged(const QString& domainHostname);
void svoImportRequested(const QString& url);
void domainConnectionRefused(const QString& reasonMessage, int reasonCode, const QString& extraInfo);
void snapshotTaken(const QString& pathStillSnapshot, const QString& pathAnimatedSnapshot, bool notify);
void stillSnapshotTaken(const QString& pathStillSnapshot, bool notify);
void snapshotShared(const QString& error);
void processingGif();
void processingGifStarted(const QString& pathStillSnapshot);
void processingGifCompleted(const QString& pathAnimatedSnapshot);
void connectionAdded(const QString& connectionName);
void connectionError(const QString& errorString);
void messageBoxClosed(int id, int button);

View file

@ -85,7 +85,6 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
renderAudioScope(renderArgs); // audio scope in the very back - NOTE: this is the debug audio scope, not the VU meter
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts
renderStatsAndLogs(renderArgs); // currently renders nothing
});
renderArgs->_batch = nullptr; // so future users of renderArgs don't try to use our batch
@ -159,27 +158,6 @@ void ApplicationOverlay::renderOverlays(RenderArgs* renderArgs) {
qApp->getOverlays().renderHUD(renderArgs);
}
void ApplicationOverlay::renderStatsAndLogs(RenderArgs* renderArgs) {
// Display stats and log text onscreen
// Determine whether to compute timing details
/*
// Show on-screen msec timer
if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) {
auto canvasSize = qApp->getCanvasSize();
quint64 mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5);
QString frameTimer = QString("%1\n").arg((int)(mSecsNow % 1000));
int timerBottom =
(Menu::getInstance()->isOptionChecked(MenuOption::Stats))
? 80 : 20;
drawText(canvasSize.x - 100, canvasSize.y - timerBottom,
0.30f, 0.0f, 0, frameTimer.toUtf8().constData(), WHITE_TEXT);
}
*/
}
void ApplicationOverlay::renderDomainConnectionStatusBorder(RenderArgs* renderArgs) {
auto geometryCache = DependencyManager::get<GeometryCache>();
static std::once_flag once;
@ -229,13 +207,13 @@ void ApplicationOverlay::buildFramebufferObject() {
auto width = uiSize.x;
auto height = uiSize.y;
if (!_overlayFramebuffer->getDepthStencilBuffer()) {
auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, DEFAULT_SAMPLER));
auto overlayDepthTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(DEPTH_FORMAT, width, height, gpu::Texture::SINGLE_MIP, DEFAULT_SAMPLER));
_overlayFramebuffer->setDepthStencilBuffer(overlayDepthTexture, DEPTH_FORMAT);
}
if (!_overlayFramebuffer->getRenderBuffer(0)) {
const gpu::Sampler OVERLAY_SAMPLER(gpu::Sampler::FILTER_MIN_MAG_LINEAR, gpu::Sampler::WRAP_CLAMP);
auto colorBuffer = gpu::TexturePointer(gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, OVERLAY_SAMPLER));
auto colorBuffer = gpu::TexturePointer(gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, gpu::Texture::SINGLE_MIP, OVERLAY_SAMPLER));
_overlayFramebuffer->setRenderBuffer(0, colorBuffer);
}
}

View file

@ -62,24 +62,13 @@ AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) {
} \
}
void AvatarInputs::update() {
if (!Menu::getInstance()) {
return;
}
AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking));
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
AI_UPDATE(isHMD, qApp->isHMDMode());
AI_UPDATE_WRITABLE(showAudioTools, Menu::getInstance()->isOptionChecked(MenuOption::AudioTools));
auto audioIO = DependencyManager::get<AudioClient>();
float AvatarInputs::loudnessToAudioLevel(float loudness) {
const float AUDIO_METER_AVERAGING = 0.5;
const float LOG2 = log(2.0f);
const float METER_LOUDNESS_SCALE = 2.8f / 5.0f;
const float LOG2_LOUDNESS_FLOOR = 11.0f;
float audioLevel = 0.0f;
auto audio = DependencyManager::get<AudioClient>();
float loudness = audio->getLastInputLoudness() + 1.0f;
loudness += 1.0f;
_trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.0f - AUDIO_METER_AVERAGING) * loudness;
@ -93,6 +82,24 @@ void AvatarInputs::update() {
if (audioLevel > 1.0f) {
audioLevel = 1.0;
}
return audioLevel;
}
void AvatarInputs::update() {
if (!Menu::getInstance()) {
return;
}
AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking));
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
AI_UPDATE(isHMD, qApp->isHMDMode());
AI_UPDATE_WRITABLE(showAudioTools, Menu::getInstance()->isOptionChecked(MenuOption::AudioTools));
auto audioIO = DependencyManager::get<AudioClient>();
const float audioLevel = loudnessToAudioLevel(DependencyManager::get<AudioClient>()->getLastInputLoudness());
AI_UPDATE_FLOAT(audioLevel, audioLevel, 0.01f);
AI_UPDATE(audioClipping, ((audioIO->getTimeSinceLastClip() > 0.0f) && (audioIO->getTimeSinceLastClip() < 1.0f)));
AI_UPDATE(audioMuted, audioIO->isMuted());

View file

@ -34,6 +34,7 @@ class AvatarInputs : public QQuickItem {
public:
static AvatarInputs* getInstance();
float loudnessToAudioLevel(float loudness);
AvatarInputs(QQuickItem* parent = nullptr);
void update();
bool showAudioTools() const { return _showAudioTools; }

View file

@ -31,6 +31,7 @@
#include "TabletScriptingInterface.h"
#include "scripting/HMDScriptingInterface.h"
static const QVariant TABLET_ADDRESS_DIALOG = "TabletAddressDialog.qml";
template<typename T>
void DialogsManager::maybeCreateDialog(QPointer<T>& member) {
if (!member) {
@ -46,12 +47,48 @@ void DialogsManager::maybeCreateDialog(QPointer<T>& member) {
}
void DialogsManager::toggleAddressBar() {
AddressBarDialog::toggle();
emit addressBarToggled();
auto hmd = DependencyManager::get<HMDScriptingInterface>();
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
if (tablet->getToolbarMode()) {
if (tablet->isPathLoaded(TABLET_ADDRESS_DIALOG)) {
tablet->gotoHomeScreen();
emit addressBarToggled();
} else {
tablet->loadQMLSource(TABLET_ADDRESS_DIALOG);
emit addressBarToggled();
}
} else {
if (hmd->getShouldShowTablet()) {
if (tablet->isPathLoaded(TABLET_ADDRESS_DIALOG) && _closeAddressBar) {
tablet->gotoHomeScreen();
hmd->closeTablet();
_closeAddressBar = false;
emit addressBarToggled();
} else {
tablet->loadQMLSource(TABLET_ADDRESS_DIALOG);
_closeAddressBar = true;
emit addressBarToggled();
}
} else {
tablet->loadQMLSource(TABLET_ADDRESS_DIALOG);
hmd->openTablet();
_closeAddressBar = true;
emit addressBarToggled();
}
}
}
void DialogsManager::showAddressBar() {
AddressBarDialog::show();
auto hmd = DependencyManager::get<HMDScriptingInterface>();
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
tablet->loadQMLSource(TABLET_ADDRESS_DIALOG);
if (!hmd->getShouldShowTablet()) {
hmd->openTablet();
}
}
void DialogsManager::showFeed() {
@ -60,10 +97,25 @@ void DialogsManager::showFeed() {
}
void DialogsManager::setDomainConnectionFailureVisibility(bool visible) {
if (visible) {
ConnectionFailureDialog::show();
qDebug() << "DialogsManager::setDomainConnectionFailureVisibility: visible" << visible;
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
if (tablet->getToolbarMode()) {
if (visible) {
ConnectionFailureDialog::show();
} else {
ConnectionFailureDialog::hide();
}
} else {
ConnectionFailureDialog::hide();
static const QUrl url("../../dialogs/TabletConnectionFailureDialog.qml");
auto hmd = DependencyManager::get<HMDScriptingInterface>();
if (visible) {
tablet->initialScreen(url);
if (!hmd->getShouldShowTablet()) {
hmd->openTablet();
}
}
}
}

View file

@ -79,6 +79,7 @@ private:
QPointer<OctreeStatsDialog> _octreeStatsDialog;
QPointer<TestingDialog> _testingDialog;
QPointer<DomainConnectionDialog> _domainConnectionDialog;
bool _closeAddressBar { false };
};
#endif // hifi_DialogsManager_h

View file

@ -76,8 +76,8 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) {
return;
}
if (_scriptEngine != NULL) {
disconnect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
disconnect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&)));
disconnect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint);
disconnect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError);
if (_ownScriptEngine) {
_scriptEngine->deleteLater();
}
@ -87,8 +87,8 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) {
_ownScriptEngine = scriptEngine == NULL;
_scriptEngine = _ownScriptEngine ? DependencyManager::get<ScriptEngines>()->loadScript(QString(), false) : scriptEngine;
connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&)));
connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&)));
connect(_scriptEngine, &ScriptEngine::printedMessage, this, &JSConsole::handlePrint);
connect(_scriptEngine, &ScriptEngine::errorMessage, this, &JSConsole::handleError);
}
void JSConsole::executeCommand(const QString& command) {
@ -134,11 +134,13 @@ void JSConsole::commandFinished() {
resetCurrentCommandHistory();
}
void JSConsole::handleError(const QString& message) {
void JSConsole::handleError(const QString& scriptName, const QString& message) {
Q_UNUSED(scriptName);
appendMessage(GUTTER_ERROR, "<span style='" + RESULT_ERROR_STYLE + "'>" + message.toHtmlEscaped() + "</span>");
}
void JSConsole::handlePrint(const QString& message) {
void JSConsole::handlePrint(const QString& scriptName, const QString& message) {
Q_UNUSED(scriptName);
appendMessage("", message);
}

View file

@ -47,8 +47,8 @@ protected:
protected slots:
void scrollToBottom();
void resizeTextInput();
void handlePrint(const QString& message);
void handleError(const QString& message);
void handlePrint(const QString& scriptName, const QString& message);
void handleError(const QString& scriptName, const QString& message);
void commandFinished();
private:

View file

@ -102,13 +102,6 @@ void setupPreferences() {
auto setter = [](bool value) { qApp->setHmdTabletBecomesToolbarSetting(value); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "HMD Tablet Becomes Toolbar", getter, setter));
}
// TODO
// Note: If this code is added back, you must remove the line "setTabletVisibleToOthersSetting(false)" from Application::loadSettings()
/*{
auto getter = []()->bool { return qApp->getTabletVisibleToOthersSetting(); };
auto setter = [](bool value) { qApp->setTabletVisibleToOthersSetting(value); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Tablet Is Visible To Others", getter, setter));
}*/
{
auto getter = []()->bool { return qApp->getPreferAvatarFingerOverStylus(); };
auto setter = [](bool value) { qApp->setPreferAvatarFingerOverStylus(value); };

View file

@ -86,7 +86,8 @@ void SnapshotAnimated::captureFrames() {
SnapshotAnimated::snapshotAnimatedTimerRunning = false;
// Notify the user that we're processing the snapshot
emit SnapshotAnimated::snapshotAnimatedDM->processingGif();
// This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called.
emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath);
// Kick off the thread that'll pack the frames into the GIF
QtConcurrent::run(processFrames);
@ -132,7 +133,7 @@ void SnapshotAnimated::processFrames() {
// Reset the current frame timestamp
SnapshotAnimated::snapshotAnimatedTimestamp = 0;
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = 0;
// Let the window scripting interface know that the snapshots have been taken.
emit SnapshotAnimated::snapshotAnimatedDM->snapshotTaken(SnapshotAnimated::snapshotStillPath, SnapshotAnimated::snapshotAnimatedPath, false);
// Update the "Share" dialog with the processed GIF.
emit SnapshotAnimated::snapshotAnimatedDM->processingGifCompleted(SnapshotAnimated::snapshotAnimatedPath);
}

View file

@ -126,7 +126,7 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(updatedAvatarCount, avatarManager->getNumAvatarsUpdated());
STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated());
STAT_UPDATE(serverCount, (int)nodeList->size());
STAT_UPDATE(framerate, qApp->getFps());
STAT_UPDATE_FLOAT(framerate, qApp->getFps(), 0.1f);
if (qApp->getActiveDisplayPlugin()) {
auto displayPlugin = qApp->getActiveDisplayPlugin();
auto stats = displayPlugin->getHardwareStats();
@ -134,11 +134,11 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(longrenders, stats["long_render_count"].toInt());
STAT_UPDATE(longsubmits, stats["long_submit_count"].toInt());
STAT_UPDATE(longframes, stats["long_frame_count"].toInt());
STAT_UPDATE(renderrate, displayPlugin->renderRate());
STAT_UPDATE(presentrate, displayPlugin->presentRate());
STAT_UPDATE(presentnewrate, displayPlugin->newFramePresentRate());
STAT_UPDATE(presentdroprate, displayPlugin->droppedFrameRate());
STAT_UPDATE(stutterrate, displayPlugin->stutterRate());
STAT_UPDATE_FLOAT(renderrate, displayPlugin->renderRate(), 0.1f);
STAT_UPDATE_FLOAT(presentrate, displayPlugin->presentRate(), 0.1f);
STAT_UPDATE_FLOAT(presentnewrate, displayPlugin->newFramePresentRate(), 0.1f);
STAT_UPDATE_FLOAT(presentdroprate, displayPlugin->droppedFrameRate(), 0.1f);
STAT_UPDATE_FLOAT(stutterrate, displayPlugin->stutterRate(), 0.1f);
} else {
STAT_UPDATE(appdropped, -1);
STAT_UPDATE(longrenders, -1);
@ -151,8 +151,8 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate());
auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
STAT_UPDATE(packetOutCount, bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond());
STAT_UPDATE(packetInCount, (int)bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
STAT_UPDATE(packetOutCount, (int)bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond());
STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f);
STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f);
@ -164,7 +164,11 @@ void Stats::updateStats(bool force) {
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer);
SharedNodePointer messageMixerNode = nodeList->soloNodeOfType(NodeType::MessagesMixer);
STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1);
STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1);
const int mixerLossRate = (int)roundf(_audioStats->data()->getMixerStream()->lossRateWindow() * 100.0f);
const int clientLossRate = (int)roundf(_audioStats->data()->getClientStream()->lossRateWindow() * 100.0f);
const int largestLossRate = mixerLossRate > clientLossRate ? mixerLossRate : clientLossRate;
STAT_UPDATE(audioPacketLoss, audioMixerNode ? largestLossRate : -1);
STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1);
STAT_UPDATE(assetPing, assetServerNode ? assetServerNode->getPingMs() : -1);
STAT_UPDATE(messagePing, messageMixerNode ? messageMixerNode->getPingMs() : -1);
@ -196,36 +200,36 @@ void Stats::updateStats(bool force) {
if (_expanded || force) {
SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer);
if (avatarMixer) {
STAT_UPDATE(avatarMixerInKbps, roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(avatarMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(avatarMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(avatarMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(avatarMixerInKbps, (int)roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(avatarMixerInPps, (int)roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(avatarMixerOutKbps, (int)roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(avatarMixerOutPps, (int)roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer)));
} else {
STAT_UPDATE(avatarMixerInKbps, -1);
STAT_UPDATE(avatarMixerInPps, -1);
STAT_UPDATE(avatarMixerOutKbps, -1);
STAT_UPDATE(avatarMixerOutPps, -1);
}
STAT_UPDATE(myAvatarSendRate, avatarManager->getMyAvatarSendRate());
STAT_UPDATE_FLOAT(myAvatarSendRate, avatarManager->getMyAvatarSendRate(), 0.1f);
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
auto audioClient = DependencyManager::get<AudioClient>();
if (audioMixerNode || force) {
STAT_UPDATE(audioMixerKbps, roundf(
STAT_UPDATE(audioMixerKbps, (int)roundf(
bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerPps, roundf(
STAT_UPDATE(audioMixerPps, (int)roundf(
bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) +
bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerInKbps, roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioAudioInboundPPS, audioClient->getAudioInboundPPS());
STAT_UPDATE(audioSilentInboundPPS, audioClient->getSilentInboundPPS());
STAT_UPDATE(audioOutboundPPS, audioClient->getAudioOutboundPPS());
STAT_UPDATE(audioSilentOutboundPPS, audioClient->getSilentOutboundPPS());
STAT_UPDATE(audioMixerInKbps, (int)roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerInPps, (int)roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerOutKbps, (int)roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerOutPps, (int)roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioAudioInboundPPS, (int)audioClient->getAudioInboundPPS());
STAT_UPDATE(audioSilentInboundPPS, (int)audioClient->getSilentInboundPPS());
STAT_UPDATE(audioOutboundPPS, (int)audioClient->getAudioOutboundPPS());
STAT_UPDATE(audioSilentOutboundPPS, (int)audioClient->getSilentOutboundPPS());
} else {
STAT_UPDATE(audioMixerKbps, -1);
STAT_UPDATE(audioMixerPps, -1);

Some files were not shown because too many files have changed in this diff Show more