Merge branch 'master' into feature/head-standard-action

This commit is contained in:
Anthony J. Thibault 2017-04-27 17:51:13 -07:00
commit 2120e63e3a
616 changed files with 105745 additions and 7800 deletions

View file

@ -34,6 +34,7 @@ module.exports = {
"Quat": false,
"Rates": false,
"Recording": false,
"Resource": false,
"Reticle": false,
"Scene": false,
"Script": false,

View file

@ -1,7 +1,7 @@
###Dependencies
* [cmake](https://cmake.org/download/) ~> 3.3.2
* [Qt](https://www.qt.io/download-open-source) ~> 5.6.1
* [Qt](https://www.qt.io/download-open-source) ~> 5.6.2
* [OpenSSL](https://www.openssl.org/community/binaries.html)
* IMPORTANT: Use the latest available version of OpenSSL to avoid security vulnerabilities.
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
@ -46,8 +46,8 @@ This can either be entered directly into your shell session before you build or
The path it needs to be set to will depend on where and how Qt5 was installed. e.g.
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.1/clang_64/lib/cmake/
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.1-1/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.2/clang_64/lib/cmake/
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.2/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
####Generating build files
@ -64,7 +64,7 @@ Any variables that need to be set for CMake to find dependencies can be set as E
For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation:
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.1/lib/cmake
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.2/lib/cmake
####Finding Dependencies

View file

@ -5,7 +5,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies
You will need the following tools to build our Android targets.
* [cmake](http://www.cmake.org/download/) ~> 3.5.1
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.5.1
* [Qt](http://www.qt.io/download-open-source/#) ~> 5.6.2
* [ant](http://ant.apache.org/bindownload.cgi) ~> 1.9.4
* [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) ~> r10d
* [Android SDK](http://developer.android.com/sdk/installing/index.html) ~> 24.4.1.1

View file

@ -16,16 +16,12 @@ For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR:
Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change.
###Qt
You can use the online installer or the offline installer.
Download and install the [Qt 5.6.2 for macOS](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-mac-x64-clang-5.6.2.dmg).
* [Download the online installer](https://www.qt.io/download-open-source/#section-2)
* When it asks you to select components, select the following:
* Qt > Qt 5.6
* [Download the offline installer](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-mac-x64-clang-5.6.1-1.dmg)
Keep the default components checked when going through the installer.
Once Qt is installed, you need to manually configure the following:
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.1/5.6/clang_64/lib/cmake/` directory.
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.2/5.6/clang_64/lib/cmake/` directory.
###Xcode
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.

View file

@ -8,23 +8,23 @@ Note: Newer versions of Visual Studio are not yet compatible.
###Step 2. Installing CMake
Download and install the CMake 3.8.0-rc2 "win64-x64 Installer" from the [CMake Website](https://cmake.org/download/). Make sure "Add CMake to system PATH for all users" is checked when going through the installer.
Download and install the [CMake 3.8.0 win64-x64 Installer](https://cmake.org/files/v3.8/cmake-3.8.0-win64-x64.msi). Make sure "Add CMake to system PATH for all users" is checked when going through the installer.
###Step 3. Installing Qt
Download and install the [Qt 5.6.1 Installer](https://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe). Please note that the download file is large (850MB) and may take some time.
Download and install the [Qt 5.6.2 for Windows 64-bit (VS 2013)](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-windows-x86-msvc2013_64-5.6.2.exe).
Make sure to select all components when going through the installer.
Keep the default components checked when going through the installer.
###Step 4. Setting Qt Environment Variable
Go to "Control Panel > System > Advanced System Settings > Environment Variables > New..." (or search “Environment Variables” in Start Search).
* Set "Variable name": QT_CMAKE_PREFIX_PATH
* Set "Variable value": `C:\Qt\Qt5.6.1\5.6\msvc2013_64\lib\cmake`
* Set "Variable value": `%QT_DIR%\5.6\msvc2013_64\lib\cmake`
###Step 5. Installing OpenSSL
Download and install the "Win64 OpenSSL v1.0.2k" Installer from [this website](https://slproweb.com/products/Win32OpenSSL.html).
Download and install the [Win64 OpenSSL v1.0.2k Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2k.exe).
###Step 6. Running CMake to Generate Build Files
@ -77,5 +77,5 @@ If not, add the directory where nmake is located to the PATH environment variabl
####Qt is throwing an error
Make sure you have the correct version (5.6.1-1) installed and 'QT_CMAKE_PREFIX_PATH' environment variable is set correctly.
Make sure you have the correct version (5.6.2) installed and 'QT_CMAKE_PREFIX_PATH' environment variable is set correctly.

View file

@ -210,6 +210,7 @@ endforeach()
file(GLOB_RECURSE JS_SRC scripts/*.js)
add_custom_target(js SOURCES ${JS_SRC})
GroupSources("scripts")
if (UNIX)
install(

View file

@ -31,6 +31,7 @@
#include <ScriptCache.h>
#include <ScriptEngines.h>
#include <SoundCache.h>
#include <UsersScriptingInterface.h>
#include <UUID.h>
#include <recording/ClipCache.h>
@ -75,6 +76,8 @@ Agent::Agent(ReceivedMessage& message) :
DependencyManager::set<ScriptEngines>(ScriptEngine::AGENT_SCRIPT);
DependencyManager::set<RecordingScriptingInterface>();
DependencyManager::set<UsersScriptingInterface>();
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
@ -351,6 +354,10 @@ void Agent::executeScript() {
// give this AvatarData object to the script engine
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());
// give scripts access to the Users object
_scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
auto player = DependencyManager::get<recording::Deck>();
connect(player.data(), &recording::Deck::playbackStateChanged, [=] {
if (player->isPlaying()) {
@ -537,7 +544,7 @@ void Agent::setIsAvatar(bool isAvatar) {
connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket);
// start the timers
_avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);
_avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets
// tell the avatarAudioTimer to start ticking
emit startAvatarAudioTimer();

View file

@ -1,91 +0,0 @@
//
// AssignmentAction.cpp
// assignment-client/src/
//
// Created by Seth Alves 2015-6-19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntitySimulation.h"
#include "AssignmentAction.h"
AssignmentAction::AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) :
EntityActionInterface(type, id),
_data(QByteArray()),
_active(false),
_ownerEntity(ownerEntity) {
}
AssignmentAction::~AssignmentAction() {
}
void AssignmentAction::removeFromSimulation(EntitySimulationPointer simulation) const {
withReadLock([&]{
simulation->removeAction(_id);
simulation->applyActionChanges();
});
}
QByteArray AssignmentAction::serialize() const {
QByteArray result;
withReadLock([&]{
result = _data;
});
return result;
}
void AssignmentAction::deserialize(QByteArray serializedArguments) {
withWriteLock([&]{
_data = serializedArguments;
});
}
bool AssignmentAction::updateArguments(QVariantMap arguments) {
qDebug() << "UNEXPECTED -- AssignmentAction::updateArguments called in assignment-client.";
return false;
}
QVariantMap AssignmentAction::getArguments() {
qDebug() << "UNEXPECTED -- AssignmentAction::getArguments called in assignment-client.";
return QVariantMap();
}
glm::vec3 AssignmentAction::getPosition() {
qDebug() << "UNEXPECTED -- AssignmentAction::getPosition called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentAction::setPosition(glm::vec3 position) {
qDebug() << "UNEXPECTED -- AssignmentAction::setPosition called in assignment-client.";
}
glm::quat AssignmentAction::getRotation() {
qDebug() << "UNEXPECTED -- AssignmentAction::getRotation called in assignment-client.";
return glm::quat();
}
void AssignmentAction::setRotation(glm::quat rotation) {
qDebug() << "UNEXPECTED -- AssignmentAction::setRotation called in assignment-client.";
}
glm::vec3 AssignmentAction::getLinearVelocity() {
qDebug() << "UNEXPECTED -- AssignmentAction::getLinearVelocity called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentAction::setLinearVelocity(glm::vec3 linearVelocity) {
qDebug() << "UNEXPECTED -- AssignmentAction::setLinearVelocity called in assignment-client.";
}
glm::vec3 AssignmentAction::getAngularVelocity() {
qDebug() << "UNEXPECTED -- AssignmentAction::getAngularVelocity called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentAction::setAngularVelocity(glm::vec3 angularVelocity) {
qDebug() << "UNEXPECTED -- AssignmentAction::setAngularVelocity called in assignment-client.";
}

View file

@ -1,48 +0,0 @@
//
// AssignmentActionFactory.cpp
// assignment-client/src/
//
// Created by Seth Alves on 2015-6-19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AssignmentActionFactory.h"
EntityActionPointer assignmentActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
return EntityActionPointer(new AssignmentAction(type, id, ownerEntity));
}
EntityActionPointer AssignmentActionFactory::factory(EntityActionType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) {
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
if (action) {
bool ok = action->updateArguments(arguments);
if (ok) {
return action;
}
}
return nullptr;
}
EntityActionPointer AssignmentActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
QDataStream serializedActionDataStream(data);
EntityActionType type;
QUuid id;
serializedActionDataStream >> type;
serializedActionDataStream >> id;
EntityActionPointer action = assignmentActionFactory(type, id, ownerEntity);
if (action) {
action->deserialize(data);
}
return action;
}

View file

@ -1,29 +0,0 @@
//
// AssignmentActionFactory.cpp
// assignment-client/src/
//
// Created by Seth Alves on 2015-6-19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AssignmentActionFactory_h
#define hifi_AssignmentActionFactory_h
#include "EntityActionFactoryInterface.h"
#include "AssignmentAction.h"
class AssignmentActionFactory : public EntityActionFactoryInterface {
public:
AssignmentActionFactory() : EntityActionFactoryInterface() { }
virtual ~AssignmentActionFactory() { }
virtual EntityActionPointer factory(EntityActionType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) override;
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override;
};
#endif // hifi_AssignmentActionFactory_h

View file

@ -30,9 +30,10 @@
#include <ShutdownEventListener.h>
#include <SoundCache.h>
#include <ResourceScriptingInterface.h>
#include <UserActivityLoggerScriptingInterface.h>
#include "AssignmentFactory.h"
#include "AssignmentActionFactory.h"
#include "AssignmentDynamicFactory.h"
#include "AssignmentClient.h"
#include "AssignmentClientLogging.h"
@ -63,9 +64,10 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
auto animationCache = DependencyManager::set<AnimationCache>();
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(false);
DependencyManager::registerInheritance<EntityActionFactoryInterface, AssignmentActionFactory>();
auto actionFactory = DependencyManager::set<AssignmentActionFactory>();
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
auto dynamicFactory = DependencyManager::set<AssignmentDynamicFactory>();
DependencyManager::set<ResourceScriptingInterface>();
DependencyManager::set<UserActivityLoggerScriptingInterface>();
// setup a thread for the NodeList and its PacketReceiver
QThread* nodeThread = new QThread(this);

View file

@ -0,0 +1,83 @@
//
// AssignmentDynamic.cpp
// assignment-client/src/
//
// Created by Seth Alves 2015-6-19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntitySimulation.h"
#include "AssignmentDynamic.h"
AssignmentDynamic::AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) :
EntityDynamicInterface(type, id),
_data(QByteArray()),
_active(false),
_ownerEntity(ownerEntity) {
}
AssignmentDynamic::~AssignmentDynamic() {
}
void AssignmentDynamic::removeFromSimulation(EntitySimulationPointer simulation) const {
withReadLock([&]{
simulation->removeDynamic(_id);
simulation->applyDynamicChanges();
});
}
QByteArray AssignmentDynamic::serialize() const {
QByteArray result;
withReadLock([&]{
result = _data;
});
return result;
}
void AssignmentDynamic::deserialize(QByteArray serializedArguments) {
withWriteLock([&]{
_data = serializedArguments;
});
}
bool AssignmentDynamic::updateArguments(QVariantMap arguments) {
qDebug() << "UNEXPECTED -- AssignmentDynamic::updateArguments called in assignment-client.";
return false;
}
QVariantMap AssignmentDynamic::getArguments() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getArguments called in assignment-client.";
return QVariantMap();
}
glm::vec3 AssignmentDynamic::getPosition() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getPosition called in assignment-client.";
return glm::vec3(0.0f);
}
glm::quat AssignmentDynamic::getRotation() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getRotation called in assignment-client.";
return glm::quat();
}
glm::vec3 AssignmentDynamic::getLinearVelocity() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getLinearVelocity called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentDynamic::setLinearVelocity(glm::vec3 linearVelocity) {
qDebug() << "UNEXPECTED -- AssignmentDynamic::setLinearVelocity called in assignment-client.";
}
glm::vec3 AssignmentDynamic::getAngularVelocity() {
qDebug() << "UNEXPECTED -- AssignmentDynamic::getAngularVelocity called in assignment-client.";
return glm::vec3(0.0f);
}
void AssignmentDynamic::setAngularVelocity(glm::vec3 angularVelocity) {
qDebug() << "UNEXPECTED -- AssignmentDynamic::setAngularVelocity called in assignment-client.";
}

View file

@ -1,5 +1,5 @@
//
// AssignmentAction.h
// AssignmentDynamic.h
// assignment-client/src/
//
// Created by Seth Alves 2015-6-19
@ -8,21 +8,21 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// http://bulletphysics.org/Bullet/BulletFull/classbtActionInterface.html
// http://bulletphysics.org/Bullet/BulletFull/classbtDynamicInterface.html
#ifndef hifi_AssignmentAction_h
#define hifi_AssignmentAction_h
#ifndef hifi_AssignmentDynamic_h
#define hifi_AssignmentDynamic_h
#include <QUuid>
#include <EntityItem.h>
#include "EntityActionInterface.h"
#include "EntityDynamicInterface.h"
class AssignmentAction : public EntityActionInterface, public ReadWriteLockable {
class AssignmentDynamic : public EntityDynamicInterface, public ReadWriteLockable {
public:
AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AssignmentAction();
AssignmentDynamic(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AssignmentDynamic();
virtual void removeFromSimulation(EntitySimulationPointer simulation) const override;
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
@ -38,9 +38,7 @@ private:
protected:
virtual glm::vec3 getPosition() override;
virtual void setPosition(glm::vec3 position) override;
virtual glm::quat getRotation() override;
virtual void setRotation(glm::quat rotation) override;
virtual glm::vec3 getLinearVelocity() override;
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
virtual glm::vec3 getAngularVelocity() override;
@ -50,4 +48,4 @@ protected:
EntityItemWeakPointer _ownerEntity;
};
#endif // hifi_AssignmentAction_h
#endif // hifi_AssignmentDynamic_h

View file

@ -0,0 +1,48 @@
//
// AssignmentDynamcFactory.cpp
// assignment-client/src/
//
// Created by Seth Alves on 2015-6-19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AssignmentDynamicFactory.h"
EntityDynamicPointer assignmentDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) {
return EntityDynamicPointer(new AssignmentDynamic(type, id, ownerEntity));
}
EntityDynamicPointer AssignmentDynamicFactory::factory(EntityDynamicType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) {
EntityDynamicPointer dynamic = assignmentDynamicFactory(type, id, ownerEntity);
if (dynamic) {
bool ok = dynamic->updateArguments(arguments);
if (ok) {
return dynamic;
}
}
return nullptr;
}
EntityDynamicPointer AssignmentDynamicFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
QDataStream serializedDynamicDataStream(data);
EntityDynamicType type;
QUuid id;
serializedDynamicDataStream >> type;
serializedDynamicDataStream >> id;
EntityDynamicPointer dynamic = assignmentDynamicFactory(type, id, ownerEntity);
if (dynamic) {
dynamic->deserialize(data);
}
return dynamic;
}

View file

@ -0,0 +1,29 @@
//
// AssignmentDynamicFactory.cpp
// assignment-client/src/
//
// Created by Seth Alves on 2015-6-19
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AssignmentDynamicFactory_h
#define hifi_AssignmentDynamicFactory_h
#include "EntityDynamicFactoryInterface.h"
#include "AssignmentDynamic.h"
class AssignmentDynamicFactory : public EntityDynamicFactoryInterface {
public:
AssignmentDynamicFactory() : EntityDynamicFactoryInterface() { }
virtual ~AssignmentDynamicFactory() { }
virtual EntityDynamicPointer factory(EntityDynamicType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) override;
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data) override;
};
#endif // hifi_AssignmentDynamicFactory_h

View file

@ -11,6 +11,8 @@
#include "SendAssetTask.h"
#include <cmath>
#include <QFile>
#include <DependencyManager.h>
@ -21,6 +23,7 @@
#include <udt/Packet.h>
#include "AssetUtils.h"
#include "ByteRange.h"
#include "ClientServerUtils.h"
SendAssetTask::SendAssetTask(QSharedPointer<ReceivedMessage> message, const SharedNodePointer& sendToNode, const QDir& resourcesDir) :
@ -34,20 +37,21 @@ SendAssetTask::SendAssetTask(QSharedPointer<ReceivedMessage> message, const Shar
void SendAssetTask::run() {
MessageID messageID;
DataOffset start, end;
ByteRange byteRange;
_message->readPrimitive(&messageID);
QByteArray assetHash = _message->read(SHA256_HASH_LENGTH);
// `start` and `end` indicate the range of data to retrieve for the asset identified by `assetHash`.
// `start` is inclusive, `end` is exclusive. Requesting `start` = 1, `end` = 10 will retrieve 9 bytes of data,
// starting at index 1.
_message->readPrimitive(&start);
_message->readPrimitive(&end);
_message->readPrimitive(&byteRange.fromInclusive);
_message->readPrimitive(&byteRange.toExclusive);
QString hexHash = assetHash.toHex();
qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from " << start << " to " << end;
qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from "
<< byteRange.fromInclusive << " to " << byteRange.toExclusive;
qDebug() << "Starting task to send asset: " << hexHash << " for messageID " << messageID;
auto replyPacketList = NLPacketList::create(PacketType::AssetGetReply, QByteArray(), true, true);
@ -56,7 +60,7 @@ void SendAssetTask::run() {
replyPacketList->writePrimitive(messageID);
if (end <= start) {
if (!byteRange.isValid()) {
replyPacketList->writePrimitive(AssetServerError::InvalidByteRange);
} else {
QString filePath = _resourcesDir.filePath(QString(hexHash));
@ -64,15 +68,40 @@ void SendAssetTask::run() {
QFile file { filePath };
if (file.open(QIODevice::ReadOnly)) {
if (file.size() < end) {
// first fixup the range based on the now known file size
byteRange.fixupRange(file.size());
// check if we're being asked to read data that we just don't have
// because of the file size
if (file.size() < byteRange.fromInclusive || file.size() < byteRange.toExclusive) {
replyPacketList->writePrimitive(AssetServerError::InvalidByteRange);
qCDebug(networking) << "Bad byte range: " << hexHash << " " << start << ":" << end;
qCDebug(networking) << "Bad byte range: " << hexHash << " "
<< byteRange.fromInclusive << ":" << byteRange.toExclusive;
} else {
auto size = end - start;
file.seek(start);
replyPacketList->writePrimitive(AssetServerError::NoError);
replyPacketList->writePrimitive(size);
replyPacketList->write(file.read(size));
// we have a valid byte range, handle it and send the asset
auto size = byteRange.size();
if (byteRange.fromInclusive >= 0) {
// this range is positive, meaning we just need to seek into the file and then read from there
file.seek(byteRange.fromInclusive);
replyPacketList->writePrimitive(AssetServerError::NoError);
replyPacketList->writePrimitive(size);
replyPacketList->write(file.read(size));
} else {
// this range is negative, at least the first part of the read will be back into the end of the file
// seek to the part of the file where the negative range begins
file.seek(file.size() + byteRange.fromInclusive);
replyPacketList->writePrimitive(AssetServerError::NoError);
replyPacketList->writePrimitive(size);
// first write everything from the negative range to the end of the file
replyPacketList->write(file.read(size));
}
qCDebug(networking) << "Sending asset: " << hexHash;
}
file.close();

View file

@ -71,15 +71,10 @@ AvatarMixer::~AvatarMixer() {
void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
QByteArray individualData = nodeData->getAvatar().identityByteArray();
auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size());
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122());
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);
++_sumIdentityPackets;
}

View file

@ -263,16 +263,8 @@ 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) {
// 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;
}
++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;
@ -285,7 +277,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
int avatarRank = 0;
// this is overly conservative, because it includes some avatars we might not consider
int remainingAvatars = (int)sortedAvatars.size();
int remainingAvatars = (int)sortedAvatars.size();
while (!sortedAvatars.empty()) {
AvatarPriority sortData = sortedAvatars.top();

View file

@ -481,14 +481,14 @@ void EntityScriptServer::deletingEntity(const EntityItemID& entityID) {
}
}
void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, const bool reload) {
void EntityScriptServer::entityServerScriptChanging(const EntityItemID& entityID, bool reload) {
if (_entityViewer.getTree() && !_shuttingDown) {
_entitiesScriptEngine->unloadEntityScript(entityID, true);
checkAndCallPreload(entityID, reload);
}
}
void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, const bool reload) {
void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool reload) {
if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptEngine) {
EntityItemPointer entity = _entityViewer.getTree()->findEntityByEntityItemID(entityID);

View file

@ -67,8 +67,8 @@ private:
void addingEntity(const EntityItemID& entityID);
void deletingEntity(const EntityItemID& entityID);
void entityServerScriptChanging(const EntityItemID& entityID, const bool reload);
void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false);
void entityServerScriptChanging(const EntityItemID& entityID, bool reload);
void checkAndCallPreload(const EntityItemID& entityID, bool reload = false);
void cleanupOldKilledListeners();

87
cmake/externals/nvtt/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,87 @@
include(ExternalProject)
include(SelectLibraryConfigurations)
set(EXTERNAL_NAME nvtt)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://s3.amazonaws.com/hifi-public/dependencies/nvtt-win-2.1.0.zip
URL_MD5 3ea6eeadbcc69071acf9c49ba565760e
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD 1
)
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE PATH "Location of NVTT include directory")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${SOURCE_DIR}/Release/x64/nvtt.lib CACHE FILEPATH "Path to NVTT release library")
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/Release>/x64" CACHE PATH "Location of NVTT release DLL")
else ()
if (ANDROID)
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
endif ()
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/nvidia-texture-tools-2.1.0.zip
URL_MD5 81b8fa6a9ee3f986088eb6e2215d6a57
CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DNVTT_SHARED=1 -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "Location of NVTT include directory")
if (APPLE)
set(_LIB_EXT "dylib")
else ()
set(_LIB_EXT "so")
endif ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libnvtt.${_LIB_EXT} CACHE FILEPATH "Path to NVTT library")
if (APPLE)
# on OS X we have to use install_name_tool to fix the paths found in the NVTT shared libraries
# so that they can be found and linked during the linking phase
set(_NVTT_LIB_DIR "${INSTALL_DIR}/lib")
# first fix the install names of all present libraries
ExternalProject_Add_Step(
${EXTERNAL_NAME}
change-install-name
COMMENT "Calling install_name_tool on NVTT libraries to fix install name for dylib linking"
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_NVTT_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
DEPENDEES install
WORKING_DIRECTORY <INSTALL_DIR>
LOG 1
)
# then, for the main library (libnvtt) fix the paths to the dependency libraries (core, image, math)
ExternalProject_Add_Step(
${EXTERNAL_NAME}
change-dependency-paths
COMMENT "Calling install_name_tool on NVTT libraries to fix paths for dependency libraries"
COMMAND install_name_tool -change libnvimage.dylib ${INSTALL_DIR}/lib/libnvimage.dylib libnvtt.dylib
COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvtt.dylib
COMMAND install_name_tool -change libnvmath.dylib ${INSTALL_DIR}/lib/libnvmath.dylib libnvtt.dylib
COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvimage.dylib
COMMAND install_name_tool -change libnvmath.dylib ${INSTALL_DIR}/lib/libnvmath.dylib libnvimage.dylib
COMMAND install_name_tool -change libnvcore.dylib ${INSTALL_DIR}/lib/libnvcore.dylib libnvmath.dylib
DEPENDEES install
WORKING_DIRECTORY <INSTALL_DIR>/lib
LOG 1
)
endif ()
endif ()
# Hide this external target (for IDE users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")

View file

@ -0,0 +1,37 @@
#
# FindNVTT.cmake
#
# Try to find NVIDIA texture tools library and include path.
# Once done this will define
#
# NVTT_FOUND
# NVTT_INCLUDE_DIRS
# NVTT_LIBRARIES
# NVTT_DLL_PATH
#
# Created on 4/14/2017 by Stephen Birarda
# 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("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("nvtt")
find_path(NVTT_INCLUDE_DIRS nvtt/nvtt.h PATH_SUFFIXES include HINTS ${NVTT_SEARCH_DIRS})
include(FindPackageHandleStandardArgs)
find_library(NVTT_LIBRARY_RELEASE nvtt PATH_SUFFIXES "lib" "Release.x64/lib" HINTS ${NVTT_SEARCH_DIRS})
find_library(NVTT_LIBRARY_DEBUG nvtt PATH_SUFFIXES "lib" "Debug.x64/lib" HINTS ${NVTT_SEARCH_DIRS})
include(SelectLibraryConfigurations)
select_library_configurations(NVTT)
if (WIN32)
find_path(NVTT_DLL_PATH nvtt.dll PATH_SUFFIXES "Release.x64/bin" HINTS ${NVTT_SEARCH_DIRS})
find_package_handle_standard_args(NVTT DEFAULT_MSG NVTT_INCLUDE_DIRS NVTT_LIBRARIES NVTT_DLL_PATH)
else ()
find_package_handle_standard_args(NVTT DEFAULT_MSG NVTT_INCLUDE_DIRS NVTT_LIBRARIES)
endif ()

View file

@ -435,23 +435,8 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
return SharedNodePointer();
}
QUuid hintNodeID;
// in case this is a node that's failing to connect
// double check we don't have a node whose sockets match exactly already in the list
limitedNodeList->eachNodeBreakable([&nodeConnection, &hintNodeID](const SharedNodePointer& node){
if (node->getPublicSocket() == nodeConnection.publicSockAddr
&& node->getLocalSocket() == nodeConnection.localSockAddr) {
// we have a node that already has these exact sockets - this occurs if a node
// is unable to connect to the domain
hintNodeID = node->getUUID();
return false;
}
return true;
});
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID);
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
// set the edit rights for this user
newNode->setPermissions(userPerms);
@ -479,11 +464,12 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
return newNode;
}
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
QUuid nodeID) {
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) {
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
QUuid nodeID;
if (connectedPeer) {
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
nodeID = nodeConnection.connectUUID;
@ -493,10 +479,8 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
discoveredSocket = *connectedPeer->getActiveSocket();
}
} else {
// we got a connectUUID we didn't recognize, either use the hinted node ID or randomly generate a new one
if (nodeID.isNull()) {
nodeID = QUuid::createUuid();
}
// we got a connectUUID we didn't recognize, randomly generate a new one
nodeID = QUuid::createUuid();
}
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();

View file

@ -76,8 +76,7 @@ private:
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
const QString& username,
const QByteArray& usernameSignature);
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
QUuid nodeID = QUuid());
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr);

View file

@ -65,6 +65,7 @@ set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}")
file(GLOB_RECURSE QML_SRC resources/qml/*.qml resources/qml/*.js)
add_custom_target(qml SOURCES ${QML_SRC})
GroupSources("resources/qml")
if (UNIX)
install(
@ -193,7 +194,7 @@ link_hifi_libraries(
recording fbx networking model-networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer avatars-renderer ui auto-updater
controllers plugins
controllers plugins image
ui-plugins display-plugins input-plugins
${NON_ANDROID_LIBRARIES}
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
xml:space="preserve"
sodipodi:docname="avatar-record-a.svg"
inkscape:version="0.92.1 r15371"><metadata
id="metadata36"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs34" /><sodipodi:namedview
pagecolor="#ff0000"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1829"
inkscape:window-height="1057"
id="namedview32"
showgrid="false"
inkscape:zoom="4.72"
inkscape:cx="-9.4279661"
inkscape:cy="25"
inkscape:window-x="83"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><style
type="text/css"
id="style2">
.st0{fill:#FFFFFF;}
</style><g
id="Layer_2" /><g
id="g879"><path
class="st0"
d="m 23.2,20.5 c -1,0.8 -1.8,1.4 -2.7,2.1 -0.2,0.1 -0.2,0.4 -0.2,0.7 -0.3,1.7 -0.6,3.4 -0.9,5.1 -0.1,0.8 -0.6,1.2 -1.3,1.1 -0.7,-0.1 -1.2,-0.7 -1.1,-1.4 0.3,-2.2 0.6,-4.4 1,-6.6 0.1,-0.3 0.3,-0.7 0.6,-0.9 1.4,-1.3 2.8,-2.5 4.2,-3.7 0.7,-0.6 1.5,-1 2.4,-0.9 0.3,0 0.7,0 1,0 1,-0.1 1.7,0.4 2.1,1.3 0.7,1.4 1.4,2.8 1.9,4.3 0.5,1.3 1.2,2.1 2.4,2.6 1,0.4 2,1 3,1.5 0.2,0.1 0.5,0.3 0.7,0.5 0.4,0.4 0.5,1 0.3,1.4 C 36.4,28 36,28.1 35.5,28 35.1,27.9 34.7,27.8 34.3,27.6 33,27 31.8,26.4 30.6,25.8 29.8,25.5 29.2,25 28.8,24.2 c -0.2,-0.3 -0.4,-0.6 -0.7,-1 -0.1,0.3 -0.1,0.5 -0.2,0.7 -0.3,1.2 -0.5,2.4 -0.8,3.6 -0.1,0.4 0,0.7 0.2,1 2.2,3.7 4.4,7.4 6.6,11.1 0.3,0.4 0.4,1 0.5,1.5 0.1,0.7 -0.1,1.3 -0.7,1.6 C 33,43.1 32.3,43.1 31.8,42.6 31.4,42.2 31,41.8 30.7,41.3 28.2,37.4 25.7,33.4 23.2,29.5 22.8,28.8 22.4,28 22.1,27.3 22,26.9 22,26.4 22.1,26 c 0.4,-1.8 0.7,-3.6 1.1,-5.5 z"
id="path5"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M 23.2,33.9 C 23.1,33.8 23,33.7 23,33.6 c 0,0 0,0 0,0 -0.2,-0.2 -0.3,-0.5 -0.5,-0.7 -0.3,-0.4 -0.6,-0.8 -0.9,-1.1 -0.3,1 -0.5,2 -0.8,3 -0.1,0.3 -0.3,0.7 -0.4,1 -1,1.5 -2,3.1 -3,4.6 -0.2,0.4 -0.4,0.8 -0.6,1.3 -0.2,0.9 0.7,1.9 1.6,1.5 0.5,-0.2 1,-0.7 1.3,-1.1 0.9,-1.1 1.6,-2.3 2.5,-3.3 0.8,-1 1.4,-2.2 1.8,-3.4 -0.2,-0.7 -0.5,-1.1 -0.8,-1.5 z"
id="path7"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M 29,11.6 C 29,12.9 27.9,14 26.6,14 H 26.4 C 25.1,14 24,12.9 24,11.6 V 10.4 C 24,9.1 25.1,8 26.4,8 h 0.2 c 1.3,0 2.4,1.1 2.4,2.4 z"
id="path9"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="m 43.4,24.1 c -0.5,0.3 -0.9,0.5 -1.4,0.8 v 6.3 h 2.3 v -7.6 c -0.3,0.2 -0.6,0.3 -0.9,0.5 z"
id="path11"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M 42,38.6 V 39 c 0,1.2 -1,2.1 -2.1,2.1 h -0.8 v 2.3 h 0.8 c 2.5,0 4.5,-2 4.5,-4.5 V 38.5 H 42 Z"
id="path13"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="m 9.7,12.2 v -0.4 c 0,-1.2 1,-2.1 2.1,-2.1 h 2 V 7.3 h -2 c -2.5,0 -4.5,2 -4.5,4.5 v 0.4 z"
id="path15"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1" /><rect
x="7.4000001"
y="18.299999"
class="st0"
width="2.3"
height="12.9"
id="rect17"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M 9.7,38.9 V 38.5 H 7.4 v 0.4 c 0,2.5 2,4.5 4.5,4.5 h 2 v -2.3 h -2 c -1.2,0 -2.2,-1 -2.2,-2.2 z"
id="path19"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1" /><g
style="fill:#000000;fill-opacity:1"
id="g25"><circle
class="st0"
cx="38.599998"
cy="13.3"
r="2.2"
id="circle21"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="m 38.6,15.5 c -1.2,0 -2.2,-1 -2.2,-2.2 0,-1.2 1,-2.2 2.2,-2.2 1.2,0 2.2,1 2.2,2.2 0,1.2 -1,2.2 -2.2,2.2 z m 0,-4.3 c -1.1,0 -2.1,0.9 -2.1,2.1 0,1.2 0.9,2.1 2.1,2.1 1.1,0 2.1,-0.9 2.1,-2.1 0,-1.2 -1,-2.1 -2.1,-2.1 z"
id="path23"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1" /></g><path
class="st0"
d="m 38.6,19.7 c -3.6,0 -6.4,-2.9 -6.4,-6.4 0,-3.5 2.9,-6.4 6.4,-6.4 3.6,0 6.4,2.9 6.4,6.4 0,3.5 -2.9,6.4 -6.4,6.4 z m 0,-10.6 c -2.3,0 -4.2,1.9 -4.2,4.2 0,2.3 1.9,4.2 4.2,4.2 2.3,0 4.2,-1.9 4.2,-4.2 0,-2.3 -1.9,-4.2 -4.2,-4.2 z"
id="path27"
inkscape:connector-curvature="0"
style="fill:#000000;fill-opacity:1" /></g></svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -0,0 +1,36 @@
<?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 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g>
<path class="st0" d="M23.2,20.5c-1,0.8-1.8,1.4-2.7,2.1c-0.2,0.1-0.2,0.4-0.2,0.7c-0.3,1.7-0.6,3.4-0.9,5.1
c-0.1,0.8-0.6,1.2-1.3,1.1c-0.7-0.1-1.2-0.7-1.1-1.4c0.3-2.2,0.6-4.4,1-6.6c0.1-0.3,0.3-0.7,0.6-0.9c1.4-1.3,2.8-2.5,4.2-3.7
c0.7-0.6,1.5-1,2.4-0.9c0.3,0,0.7,0,1,0c1-0.1,1.7,0.4,2.1,1.3c0.7,1.4,1.4,2.8,1.9,4.3c0.5,1.3,1.2,2.1,2.4,2.6c1,0.4,2,1,3,1.5
c0.2,0.1,0.5,0.3,0.7,0.5c0.4,0.4,0.5,1,0.3,1.4C36.4,28,36,28.1,35.5,28c-0.4-0.1-0.8-0.2-1.2-0.4c-1.3-0.6-2.5-1.2-3.7-1.8
c-0.8-0.3-1.4-0.8-1.8-1.6c-0.2-0.3-0.4-0.6-0.7-1c-0.1,0.3-0.1,0.5-0.2,0.7c-0.3,1.2-0.5,2.4-0.8,3.6c-0.1,0.4,0,0.7,0.2,1
c2.2,3.7,4.4,7.4,6.6,11.1c0.3,0.4,0.4,1,0.5,1.5c0.1,0.7-0.1,1.3-0.7,1.6c-0.7,0.4-1.4,0.4-1.9-0.1c-0.4-0.4-0.8-0.8-1.1-1.3
c-2.5-3.9-5-7.9-7.5-11.8c-0.4-0.7-0.8-1.5-1.1-2.2c-0.1-0.4-0.1-0.9,0-1.3C22.5,24.2,22.8,22.4,23.2,20.5z"/>
<path class="st0" d="M23.2,33.9c-0.1-0.1-0.2-0.2-0.2-0.3c0,0,0,0,0,0c-0.2-0.2-0.3-0.5-0.5-0.7c-0.3-0.4-0.6-0.8-0.9-1.1
c-0.3,1-0.5,2-0.8,3c-0.1,0.3-0.3,0.7-0.4,1c-1,1.5-2,3.1-3,4.6c-0.2,0.4-0.4,0.8-0.6,1.3c-0.2,0.9,0.7,1.9,1.6,1.5
c0.5-0.2,1-0.7,1.3-1.1c0.9-1.1,1.6-2.3,2.5-3.3c0.8-1,1.4-2.2,1.8-3.4C23.8,34.7,23.5,34.3,23.2,33.9z"/>
<path class="st0" d="M29,11.6c0,1.3-1.1,2.4-2.4,2.4h-0.2c-1.3,0-2.4-1.1-2.4-2.4v-1.2C24,9.1,25.1,8,26.4,8h0.2
c1.3,0,2.4,1.1,2.4,2.4V11.6z"/>
<path class="st0" d="M43.4,24.1c-0.5,0.3-0.9,0.5-1.4,0.8v6.3h2.3v-7.6C44,23.8,43.7,23.9,43.4,24.1z"/>
<path class="st0" d="M42,38.6v0.4c0,1.2-1,2.1-2.1,2.1h-0.8v2.3h0.8c2.5,0,4.5-2,4.5-4.5v-0.4H42z"/>
<path class="st0" d="M9.7,12.2v-0.4c0-1.2,1-2.1,2.1-2.1h2V7.3h-2c-2.5,0-4.5,2-4.5,4.5v0.4H9.7z"/>
<rect x="7.4" y="18.3" class="st0" width="2.3" height="12.9"/>
<path class="st0" d="M9.7,38.9v-0.4H7.4v0.4c0,2.5,2,4.5,4.5,4.5h2v-2.3h-2C10.7,41.1,9.7,40.1,9.7,38.9z"/>
<g>
<circle class="st0" cx="38.6" cy="13.3" r="2.2"/>
<path class="st0" d="M38.6,15.5c-1.2,0-2.2-1-2.2-2.2s1-2.2,2.2-2.2c1.2,0,2.2,1,2.2,2.2S39.8,15.5,38.6,15.5z M38.6,11.2
c-1.1,0-2.1,0.9-2.1,2.1s0.9,2.1,2.1,2.1c1.1,0,2.1-0.9,2.1-2.1S39.7,11.2,38.6,11.2z"/>
</g>
<path class="st0" d="M38.6,19.7c-3.6,0-6.4-2.9-6.4-6.4s2.9-6.4,6.4-6.4c3.6,0,6.4,2.9,6.4,6.4S42.1,19.7,38.6,19.7z M38.6,9.1
c-2.3,0-4.2,1.9-4.2,4.2s1.9,4.2,4.2,4.2c2.3,0,4.2-1.9,4.2-4.2S40.9,9.1,38.6,9.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
xml:space="preserve"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="doppleganger-a.svg"><metadata
id="metadata36"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs34"><linearGradient
id="linearGradient8353"
osb:paint="solid"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop8355" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ff4900"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1004"
id="namedview32"
showgrid="false"
inkscape:zoom="9.44"
inkscape:cx="-3.2806499"
inkscape:cy="20.640561"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="g4308" /><style
type="text/css"
id="style4">
.st0{fill:#FFFFFF;}
</style><g
id="Layer_2" /><g
id="Layer_1"
style="fill:#000000;fill-opacity:1"><g
id="g8375"
transform="matrix(1.0667546,0,0,1.0667546,-2.1894733,-1.7818707)"><g
id="g4308"
transform="translate(1.0333645e-6,0)"><g
id="g8"
style="fill:#000000;fill-opacity:1"
transform="matrix(1.1059001,0,0,1.1059001,-17.342989,-7.9561147)"><path
class="st0"
d="m 23.2,24.1 c -0.8,0.9 -1.5,1.8 -2.2,2.6 -0.1,0.2 -0.1,0.5 -0.1,0.7 0.1,1.7 0.2,3.4 0.2,5.1 0,0.8 -0.4,1.2 -1.1,1.3 -0.7,0.1 -1.3,-0.4 -1.4,-1.1 -0.2,-2.2 -0.3,-4.3 -0.5,-6.5 0,-0.3 0.1,-0.7 0.4,-1 1.1,-1.5 2.3,-3 3.4,-4.5 0.6,-0.7 1.6,-1.6 2.6,-1.6 0.3,0 1.1,0 1.4,0 0.8,-0.1 1.3,0.1 1.9,0.9 1,1.2 1.5,2.3 2.4,3.6 0.7,1.1 1.4,1.6 2.9,1.9 1.1,0.2 2.2,0.5 3.3,0.8 0.3,0.1 0.6,0.2 0.8,0.3 0.5,0.3 0.7,0.8 0.6,1.3 -0.1,0.5 -0.5,0.7 -1,0.8 -0.4,0 -0.9,0 -1.3,-0.1 -1.4,-0.3 -2.7,-0.6 -4.1,-0.9 -0.8,-0.2 -1.5,-0.6 -2.1,-1.1 -0.3,-0.3 -0.6,-0.5 -0.9,-0.8 0,0.3 0,0.5 0,0.7 0,1.2 0,2.4 0,3.6 0,0.4 -0.3,12.6 -0.1,16.8 0,0.5 -0.1,1 -0.2,1.5 -0.2,0.7 -0.6,1 -1.4,1.1 -0.8,0 -1.4,-0.3 -1.7,-1 C 24.8,48 24.7,47.4 24.6,46.9 24.2,42.3 23.7,34 23.5,33.1 23.4,32.3 23.3,32 23.2,31 c -0.1,-0.5 -0.1,-0.9 -0.1,-1.3 0.2,-1.8 0.1,-3.6 0.1,-5.6 z"
id="path10"
style="fill:#000000;fill-opacity:1"
inkscape:connector-curvature="0" /><path
class="st0"
d="m 28.2,14.6 c 0,1.4 -1.1,2.6 -2.6,2.6 l 0,0 C 24.2,17.2 23,16.1 23,14.6 L 23,13 c 0,-1.4 1.1,-2.6 2.6,-2.6 l 0,0 c 1.4,0 2.6,1.1 2.6,2.6 l 0,1.6 z"
id="path12"
style="fill:#000000;fill-opacity:1"
inkscape:connector-curvature="0" /></g><g
id="g8-3"
style="opacity:0.5;fill:#808080;fill-opacity:1;stroke:#000000;stroke-width:0.59335912;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29667956,0.29667956;stroke-dashoffset:0;stroke-opacity:1"
transform="matrix(-1.1059001,0,0,1.1059001,67.821392,-7.9561147)"><path
class="st0"
d="m 23.2,24.1 c -0.8,0.9 -1.5,1.8 -2.2,2.6 -0.1,0.2 -0.1,0.5 -0.1,0.7 0.1,1.7 0.2,3.4 0.2,5.1 0,0.8 -0.4,1.2 -1.1,1.3 -0.7,0.1 -1.3,-0.4 -1.4,-1.1 -0.2,-2.2 -0.3,-4.3 -0.5,-6.5 0,-0.3 0.1,-0.7 0.4,-1 1.1,-1.5 2.3,-3 3.4,-4.5 0.6,-0.7 1.6,-1.6 2.6,-1.6 0.3,0 1.1,0 1.4,0 0.8,-0.1 1.3,0.1 1.9,0.9 1,1.2 1.5,2.3 2.4,3.6 0.7,1.1 1.4,1.6 2.9,1.9 1.1,0.2 2.2,0.5 3.3,0.8 0.3,0.1 0.6,0.2 0.8,0.3 0.5,0.3 0.7,0.8 0.6,1.3 -0.1,0.5 -0.5,0.7 -1,0.8 -0.4,0 -0.9,0 -1.3,-0.1 -1.4,-0.3 -2.7,-0.6 -4.1,-0.9 -0.8,-0.2 -1.5,-0.6 -2.1,-1.1 -0.3,-0.3 -0.6,-0.5 -0.9,-0.8 0,0.3 0,0.5 0,0.7 0,1.2 0,2.4 0,3.6 0,0.4 -0.3,12.6 -0.1,16.8 0,0.5 -0.1,1 -0.2,1.5 -0.2,0.7 -0.6,1 -1.4,1.1 -0.8,0 -1.4,-0.3 -1.7,-1 C 24.8,48 24.7,47.4 24.6,46.9 24.2,42.3 23.7,34 23.5,33.1 23.4,32.3 23.3,32 23.2,31 c -0.1,-0.5 -0.1,-0.9 -0.1,-1.3 0.2,-1.8 0.1,-3.6 0.1,-5.6 z"
id="path10-6"
style="fill:#808080;fill-opacity:1;stroke:#000000;stroke-width:0.59335912;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29667956,0.29667956;stroke-dashoffset:0;stroke-opacity:1"
inkscape:connector-curvature="0" /><path
class="st0"
d="m 28.2,14.6 c 0,1.4 -1.1,2.6 -2.6,2.6 l 0,0 C 24.2,17.2 23,16.1 23,14.6 L 23,13 c 0,-1.4 1.1,-2.6 2.6,-2.6 l 0,0 c 1.4,0 2.6,1.1 2.6,2.6 l 0,1.6 z"
id="path12-7"
style="fill:#808080;fill-opacity:1;stroke:#000000;stroke-width:0.59335912;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29667956,0.29667956;stroke-dashoffset:0;stroke-opacity:1"
inkscape:connector-curvature="0" /></g></g><rect
style="opacity:0.5;fill:#808080;fill-opacity:1;stroke:#000000;stroke-width:0.15729524;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.62918094, 1.25836187;stroke-dashoffset:0;stroke-opacity:1"
id="rect4306"
width="0.12393159"
height="46.498554"
x="25.227457"
y="1.8070068"
rx="0"
ry="0.9407174" /></g></g></svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
xml:space="preserve"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="doppleganger-i.svg"><metadata
id="metadata36"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs34"><linearGradient
id="linearGradient8353"
osb:paint="solid"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop8355" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ff4900"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1004"
id="namedview32"
showgrid="false"
inkscape:zoom="9.44"
inkscape:cx="-3.2806499"
inkscape:cy="20.640561"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="g4308" /><style
type="text/css"
id="style4">
.st0{fill:#FFFFFF;}
</style><g
id="Layer_2" /><g
id="Layer_1"
style="fill:#000000;fill-opacity:1"><g
id="g8375"
transform="matrix(1.0667546,0,0,1.0667546,-2.1894733,-1.7818707)"><g
id="g4308"
transform="translate(1.0333645e-6,0)"><g
id="g8"
style="fill:#ffffff;fill-opacity:1"
transform="matrix(1.1059001,0,0,1.1059001,-17.342989,-7.9561147)"><path
class="st0"
d="m 23.2,24.1 c -0.8,0.9 -1.5,1.8 -2.2,2.6 -0.1,0.2 -0.1,0.5 -0.1,0.7 0.1,1.7 0.2,3.4 0.2,5.1 0,0.8 -0.4,1.2 -1.1,1.3 -0.7,0.1 -1.3,-0.4 -1.4,-1.1 -0.2,-2.2 -0.3,-4.3 -0.5,-6.5 0,-0.3 0.1,-0.7 0.4,-1 1.1,-1.5 2.3,-3 3.4,-4.5 0.6,-0.7 1.6,-1.6 2.6,-1.6 0.3,0 1.1,0 1.4,0 0.8,-0.1 1.3,0.1 1.9,0.9 1,1.2 1.5,2.3 2.4,3.6 0.7,1.1 1.4,1.6 2.9,1.9 1.1,0.2 2.2,0.5 3.3,0.8 0.3,0.1 0.6,0.2 0.8,0.3 0.5,0.3 0.7,0.8 0.6,1.3 -0.1,0.5 -0.5,0.7 -1,0.8 -0.4,0 -0.9,0 -1.3,-0.1 -1.4,-0.3 -2.7,-0.6 -4.1,-0.9 -0.8,-0.2 -1.5,-0.6 -2.1,-1.1 -0.3,-0.3 -0.6,-0.5 -0.9,-0.8 0,0.3 0,0.5 0,0.7 0,1.2 0,2.4 0,3.6 0,0.4 -0.3,12.6 -0.1,16.8 0,0.5 -0.1,1 -0.2,1.5 -0.2,0.7 -0.6,1 -1.4,1.1 -0.8,0 -1.4,-0.3 -1.7,-1 C 24.8,48 24.7,47.4 24.6,46.9 24.2,42.3 23.7,34 23.5,33.1 23.4,32.3 23.3,32 23.2,31 c -0.1,-0.5 -0.1,-0.9 -0.1,-1.3 0.2,-1.8 0.1,-3.6 0.1,-5.6 z"
id="path10"
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0" /><path
class="st0"
d="m 28.2,14.6 c 0,1.4 -1.1,2.6 -2.6,2.6 l 0,0 C 24.2,17.2 23,16.1 23,14.6 L 23,13 c 0,-1.4 1.1,-2.6 2.6,-2.6 l 0,0 c 1.4,0 2.6,1.1 2.6,2.6 l 0,1.6 z"
id="path12"
style="fill:#ffffff;fill-opacity:1"
inkscape:connector-curvature="0" /></g><g
id="g8-3"
style="opacity:0.5;fill:#808080;fill-opacity:1;stroke:#ffffff;stroke-width:0.59335912;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29667956, 0.29667956000000001;stroke-dashoffset:0;stroke-opacity:1"
transform="matrix(-1.1059001,0,0,1.1059001,67.821392,-7.9561147)"><path
class="st0"
d="m 23.2,24.1 c -0.8,0.9 -1.5,1.8 -2.2,2.6 -0.1,0.2 -0.1,0.5 -0.1,0.7 0.1,1.7 0.2,3.4 0.2,5.1 0,0.8 -0.4,1.2 -1.1,1.3 -0.7,0.1 -1.3,-0.4 -1.4,-1.1 -0.2,-2.2 -0.3,-4.3 -0.5,-6.5 0,-0.3 0.1,-0.7 0.4,-1 1.1,-1.5 2.3,-3 3.4,-4.5 0.6,-0.7 1.6,-1.6 2.6,-1.6 0.3,0 1.1,0 1.4,0 0.8,-0.1 1.3,0.1 1.9,0.9 1,1.2 1.5,2.3 2.4,3.6 0.7,1.1 1.4,1.6 2.9,1.9 1.1,0.2 2.2,0.5 3.3,0.8 0.3,0.1 0.6,0.2 0.8,0.3 0.5,0.3 0.7,0.8 0.6,1.3 -0.1,0.5 -0.5,0.7 -1,0.8 -0.4,0 -0.9,0 -1.3,-0.1 -1.4,-0.3 -2.7,-0.6 -4.1,-0.9 -0.8,-0.2 -1.5,-0.6 -2.1,-1.1 -0.3,-0.3 -0.6,-0.5 -0.9,-0.8 0,0.3 0,0.5 0,0.7 0,1.2 0,2.4 0,3.6 0,0.4 -0.3,12.6 -0.1,16.8 0,0.5 -0.1,1 -0.2,1.5 -0.2,0.7 -0.6,1 -1.4,1.1 -0.8,0 -1.4,-0.3 -1.7,-1 C 24.8,48 24.7,47.4 24.6,46.9 24.2,42.3 23.7,34 23.5,33.1 23.4,32.3 23.3,32 23.2,31 c -0.1,-0.5 -0.1,-0.9 -0.1,-1.3 0.2,-1.8 0.1,-3.6 0.1,-5.6 z"
id="path10-6"
style="fill:#808080;fill-opacity:1;stroke:#ffffff;stroke-width:0.59335912;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29667956, 0.29667956000000001;stroke-dashoffset:0;stroke-opacity:1"
inkscape:connector-curvature="0" /><path
class="st0"
d="m 28.2,14.6 c 0,1.4 -1.1,2.6 -2.6,2.6 l 0,0 C 24.2,17.2 23,16.1 23,14.6 L 23,13 c 0,-1.4 1.1,-2.6 2.6,-2.6 l 0,0 c 1.4,0 2.6,1.1 2.6,2.6 l 0,1.6 z"
id="path12-7"
style="fill:#808080;fill-opacity:1;stroke:#ffffff;stroke-width:0.59335912;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.29667956, 0.29667956000000001;stroke-dashoffset:0;stroke-opacity:1"
inkscape:connector-curvature="0" /></g></g><rect
style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.15729524;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:0.62918094, 1.25836187000000010;stroke-dashoffset:0;stroke-opacity:1"
id="rect4306"
width="0.12393159"
height="46.498554"
x="25.227457"
y="1.8070068"
rx="0"
ry="0.9407174" /></g></g></svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

View file

@ -0,0 +1,18 @@
<?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 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#EF3B4E;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1_1_">
<path class="st0" d="M47.2,41.3l-9.1-9.1c-0.8-0.8-1.9-1.1-3-1l-2.4-2.4c1.8-2.6,2.8-5.7,2.8-9c0-8.9-7.2-16.1-16.1-16.1
S3.3,11,3.3,19.8c0,8.9,7.2,16.1,16.1,16.1c4.1,0,7.8-1.5,10.6-4l2.2,2.2c-0.2,1.1,0.1,2.2,1,3l9.1,9.1c1.4,1.4,3.6,1.4,4.9,0
C48.5,44.9,48.5,42.7,47.2,41.3z M19.4,32.2c-6.8,0-12.3-5.5-12.3-12.3S12.6,7.6,19.4,7.6s12.3,5.5,12.3,12.3
C31.8,26.6,26.2,32.2,19.4,32.2z"/>
</g>
<circle class="st1" cx="43.5" cy="6.5" r="5.9"/>
</svg>

After

Width:  |  Height:  |  Size: 911 B

View file

@ -1,6 +1,6 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
<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 50 150" style="enable-background:new 0 0 50 150;" xml:space="preserve">
<style type="text/css">
.st0{fill:#414042;}
@ -8,21 +8,16 @@
.st2{fill:#1398BB;}
.st3{fill:#31D8FF;}
</style>
<g id="Layer_1">
<path class="st0" d="M33.72,85.08l-9.15-9.15l-0.74-0.74l0.74-0.74l9.35-9.35c0.59-0.59,0.59-1.56,0-2.15
c-0.29-0.29-0.67-0.45-1.08-0.45c-0.41,0-0.79,0.16-1.08,0.45L19.52,75.19l12.04,12.04c0.29,0.29,0.67,0.45,1.08,0.45
c0.41,0,0.79-0.16,1.08-0.45C34.31,86.64,34.31,85.67,33.72,85.08z"/>
<path class="st1" d="M33.72,33.45l-9.15-9.15l-0.74-0.74l0.74-0.74l9.35-9.35c0.59-0.59,0.59-1.56,0-2.15
c-0.29-0.29-0.67-0.45-1.08-0.45c-0.41,0-0.79,0.16-1.08,0.45L19.52,23.56L31.56,35.6c0.29,0.29,0.67,0.45,1.08,0.45
c0.41,0,0.79-0.16,1.08-0.45C34.31,35.01,34.31,34.04,33.72,33.45z"/>
<path class="st2" d="M17.99,124.82l12.78,12.78c1,1,2.63,1,3.63,0c1-1,1-2.63,0-3.63l-9.15-9.15l9.35-9.35c1-1,1-2.63,0-3.63
c-1-1-2.63-1-3.63,0L17.99,124.82z"/>
<path class="st3" d="M32.79,112.13c0.41,0,0.79,0.16,1.08,0.45c0.59,0.59,0.59,1.56,0,2.15l-9.35,9.35l-0.74,0.74l0.74,0.74
l9.15,9.15c0.59,0.59,0.59,1.56,0,2.15c-0.29,0.29-0.67,0.45-1.08,0.45c-0.41,0-0.79-0.16-1.08-0.45l-12.04-12.04l12.24-12.24
C32,112.29,32.38,112.13,32.79,112.13 M32.79,111.08c-0.66,0-1.31,0.25-1.82,0.75l-12.98,12.98l12.78,12.78
c0.5,0.5,1.16,0.75,1.82,0.75c0.66,0,1.31-0.25,1.82-0.75c1-1,1-2.63,0-3.63l-9.15-9.15l9.35-9.35c1-1,1-2.63,0-3.63
C34.1,111.34,33.44,111.08,32.79,111.08L32.79,111.08z"/>
</g>
<path class="st0" d="M33.4,87.4L22.1,76.1l-0.9-0.9l0.9-0.9l11.5-11.5c0.7-0.7,0.7-1.9,0-2.7c-0.3-0.3-0.8-0.5-1.3-0.5
s-1,0.2-1.3,0.5L15.9,75.2l14.9,14.9c0.4,0.4,0.8,0.6,1.3,0.6c0.5,0,1-0.2,1.3-0.6C34.2,89.3,34.2,88.2,33.4,87.4z"/>
<path class="st1" d="M33.4,37.1L22.1,25.8l-0.9-0.9l0.9-0.9l11.5-11.5c0.7-0.7,0.7-1.9,0-2.7c-0.3-0.4-0.8-0.6-1.3-0.6
s-1,0.2-1.3,0.6L15.9,24.9l14.9,14.9c0.4,0.4,0.8,0.5,1.3,0.5c0.5,0,1-0.2,1.3-0.5C34.2,39,34.2,37.8,33.4,37.1z"/>
<path class="st2" d="M14.1,124.8l15.8,15.8c1.2,1.2,3.2,1.2,4.5,0c1.2-1.2,1.2-3.2,0-4.5L23,124.8l11.5-11.5c1.2-1.2,1.2-3.2,0-4.5
c-1.2-1.2-3.2-1.2-4.5,0L14.1,124.8z"/>
<path class="st3" d="M32.4,109.2c0.5,0,1,0.2,1.3,0.6c0.7,0.7,0.7,1.9,0,2.7l-11.5,11.5l-0.9,0.9l0.9,0.9l11.3,11.3
c0.7,0.7,0.7,1.9,0,2.7c-0.4,0.4-0.8,0.6-1.3,0.6s-1-0.2-1.3-0.6l-14.9-14.9L31,109.7C31.4,109.4,31.9,109.2,32.4,109.2 M32.4,107.9
c-0.8,0-1.6,0.3-2.2,0.9l-16,16l15.8,15.8c0.6,0.6,1.4,0.9,2.2,0.9s1.6-0.3,2.2-0.9c1.2-1.2,1.2-3.2,0-4.5l-11.3-11.3l11.5-11.6
c1.2-1.2,1.2-3.2,0-4.5C34,108.2,33.2,107.9,32.4,107.9L32.4,107.9z"/>
<g id="Layer_2">
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,6 +1,6 @@
<?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" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
<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 50 150" style="enable-background:new 0 0 50 150;" xml:space="preserve">
<style type="text/css">
.st0{fill:#414042;}
@ -8,21 +8,16 @@
.st2{fill:#1398BB;}
.st3{fill:#31D8FF;}
</style>
<g id="Layer_1">
<path class="st0" d="M21.12,62.95c-0.29-0.29-0.67-0.45-1.08-0.45c-0.41,0-0.79,0.16-1.08,0.45c-0.59,0.59-0.59,1.56,0,2.15
l9.35,9.35l0.74,0.74l-0.74,0.74l-9.15,9.15c-0.59,0.59-0.59,1.56,0,2.15c0.29,0.29,0.67,0.45,1.08,0.45
c0.41,0,0.79-0.16,1.08-0.45l12.04-12.04L21.12,62.95z"/>
<path class="st1" d="M21.12,11.32c-0.29-0.29-0.67-0.45-1.08-0.45c-0.41,0-0.79,0.16-1.08,0.45c-0.59,0.59-0.59,1.56,0,2.15
l9.35,9.35l0.74,0.74l-0.74,0.74l-9.15,9.15c-0.59,0.59-0.59,1.56,0,2.15c0.29,0.29,0.67,0.45,1.08,0.45
c0.41,0,0.79-0.16,1.08-0.45l12.04-12.04L21.12,11.32z"/>
<path class="st2" d="M34.9,124.82L22.11,137.6c-1,1-2.63,1-3.63,0c-1-1-1-2.63,0-3.63l9.15-9.15l-9.35-9.35c-1-1-1-2.63,0-3.63
c1-1,2.63-1,3.63,0L34.9,124.82z"/>
<path class="st3" d="M20.1,112.13c0.41,0,0.79,0.16,1.08,0.45l12.24,12.24l-12.04,12.04c-0.29,0.29-0.67,0.45-1.08,0.45
c-0.41,0-0.79-0.16-1.08-0.45c-0.59-0.59-0.59-1.56,0-2.15l9.15-9.15l0.74-0.74l-0.74-0.74l-9.35-9.35c-0.59-0.59-0.59-1.56,0-2.15
C19.31,112.29,19.69,112.13,20.1,112.13 M20.1,111.08c-0.66,0-1.31,0.25-1.82,0.75c-1,1-1,2.63,0,3.63l9.35,9.35l-9.15,9.15
c-1,1-1,2.63,0,3.63c0.5,0.5,1.16,0.75,1.82,0.75s1.31-0.25,1.82-0.75l12.78-12.78l-12.98-12.98
C21.41,111.34,20.76,111.08,20.1,111.08L20.1,111.08z"/>
</g>
<path class="st0" d="M21.1,60.1c-0.4-0.4-0.8-0.5-1.3-0.5s-1,0.2-1.3,0.5c-0.7,0.7-0.7,1.9,0,2.7l11.5,11.5l0.9,0.9l-0.9,0.9
L18.6,87.4c-0.7,0.7-0.7,1.9,0,2.7c0.4,0.3,0.8,0.6,1.3,0.6s1-0.2,1.3-0.6l14.9-14.9L21.1,60.1z"/>
<path class="st1" d="M21.1,9.8c-0.4-0.4-0.8-0.6-1.3-0.6s-1,0.2-1.3,0.6c-0.7,0.7-0.7,1.9,0,2.7L29.9,24l0.9,0.9l-0.9,0.9L18.6,37.1
c-0.7,0.7-0.7,1.9,0,2.7c0.4,0.4,0.8,0.5,1.3,0.5s1-0.2,1.3-0.5l14.9-14.9L21.1,9.8z"/>
<path class="st2" d="M38,124.8l-15.8,15.8c-1.2,1.2-3.2,1.2-4.5,0c-1.2-1.2-1.2-3.2,0-4.5L29,124.8l-11.5-11.5
c-1.2-1.2-1.2-3.2,0-4.5c1.2-1.2,3.2-1.2,4.5,0L38,124.8z"/>
<path class="st3" d="M19.7,109.2c0.5,0,1,0.2,1.3,0.6l15.1,15.1l-14.9,14.9c-0.4,0.4-0.8,0.6-1.3,0.6s-1-0.2-1.3-0.6
c-0.7-0.7-0.7-1.9,0-2.7l11.3-11.3l0.9-0.9l-0.9-0.9l-11.5-11.5c-0.7-0.7-0.7-1.9,0-2.7C18.8,109.4,19.2,109.2,19.7,109.2
M19.7,107.9c-0.8,0-1.6,0.3-2.2,0.9c-1.2,1.2-1.2,3.2,0,4.5L29,124.8l-11.3,11.3c-1.2,1.2-1.2,3.2,0,4.5c0.6,0.6,1.4,0.9,2.2,0.9
s1.6-0.3,2.2-0.9L38,124.8l-16-16C21.4,108.2,20.5,107.9,19.7,107.9L19.7,107.9z"/>
<g id="Layer_2">
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -542,7 +542,7 @@ ScrollingWindow {
Item {
height: parent.height
width: parent.width
HifiControls.Button {
HifiControls.QueuedButton {
id: uploadButton
anchors.right: parent.right
@ -552,22 +552,7 @@ ScrollingWindow {
height: 30
width: 155
onClicked: uploadClickedTimer.running = true
// For some reason trigginer an API that enters
// an internal event loop directly from the button clicked
// trigger below causes the appliction to behave oddly.
// Most likely because the button onClicked handling is never
// completed until the function returns.
// FIXME find a better way of handling the input dialogs that
// doesn't trigger this.
Timer {
id: uploadClickedTimer
interval: 5
repeat: false
running: false
onTriggered: uploadClicked();
}
onClickedQueued: uploadClicked()
}
Item {

View file

@ -211,6 +211,11 @@ Item {
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +
", Pending: " + root.downloadsPending;
}
StatText {
visible: root.expanded;
text: "Processing: " + root.processing +
", Pending: " + root.processingPending;
}
StatText {
visible: root.expanded && root.downloadUrls.length > 0;
text: "Download URLs:"
@ -244,16 +249,16 @@ Item {
id: octreeCol
spacing: 4; x: 4; y: 4;
StatText {
text: " Frame timing:"
text: "Engine: " + root.engineFrameTime.toFixed(1) + " ms"
}
StatText {
text: " Batch: " + root.batchFrameTime.toFixed(1) + " ms"
text: "Batch: " + root.batchFrameTime.toFixed(1) + " ms"
}
StatText {
text: " GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
}
StatText {
text: " Avatar: " + root.avatarSimulationTime.toFixed(1) + " ms"
text: "Avatar: " + root.avatarSimulationTime.toFixed(1) + " ms"
}
StatText {
text: "Triangles: " + root.triangles +

View file

@ -0,0 +1,43 @@
//
// QueuedButton.qml
// -- original Button.qml + signal timer workaround --ht
// Created by David Rowe on 16 Feb 2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4 as Original
import QtQuick.Controls.Styles 1.4
import "../styles-uit"
import "." as HifiControls
HifiControls.Button {
// FIXME: THIS WORKAROUND MIGRATED/CONSOLIDATED FROM RUNNINGSCRIPTS.QML
// For some reason trigginer an API that enters
// an internal event loop directly from the button clicked
// trigger below causes the appliction to behave oddly.
// Most likely because the button onClicked handling is never
// completed until the function returns.
// FIXME find a better way of handling the input dialogs that
// doesn't trigger this.
// NOTE: dialogs that need to use this workaround can connect via
// onQueuedClicked: ...
// instead of:
// onClicked: ...
signal clickedQueued()
Timer {
id: fromTimer
interval: 5
repeat: false
running: false
onTriggered: clickedQueued()
}
onClicked: fromTimer.running = true
}

View file

@ -0,0 +1,53 @@
//
// TabletWebButton.qml
//
// Created by Dante Ruiz on 2017/4/13
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import "../styles-uit"
Rectangle {
property alias text: label.text
property alias pixelSize: label.font.pixelSize;
property bool selected: false
property bool hovered: false
property bool enabled: false
property int spacing: 2
property var action: function () {}
property string enabledColor: hifi.colors.blueHighlight
property string disabledColor: hifi.colors.blueHighlight
property string highlightColor: hifi.colors.blueHighlight;
width: label.width + 64
height: 32
color: hifi.colors.white
HifiConstants { id: hifi }
RalewaySemiBold {
id: label;
color: enabledColor
font.pixelSize: 15;
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
}
Rectangle {
id: indicator
width: parent.width
height: selected ? 3 : 1
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
color: hifi.colors.blueHighlight
visible: parent.selected || hovered
}
}

View file

@ -2,97 +2,83 @@ import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebEngine 1.2
import QtWebChannel 1.0
import HFTabletWebEngineProfile 1.0
import "../controls-uit" as HiFiControls
import "../styles" as HifiStyles
import "../styles-uit"
import HFWebEngineProfile 1.0
import HFTabletWebEngineProfile 1.0
import "../"
import "."
Item {
id: web
HifiConstants { id: hifi }
width: parent.width
height: parent.height
property var parentStackItem: null
property int headerHeight: 38
property int headerHeight: 70
property string url
property string address: url //for compatibility
property alias address: displayUrl.text //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 bool removingPage: false
property bool loadingPage: false
property int currentPage: -1 // used as a model for repeater
property alias pagesModel: pagesModel
Row {
Rectangle {
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(); }
}
}
width: parent.width
height: parent.headerHeight
color: hifi.colors.white
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")
Row {
id: nav
anchors {
top: parent.top
topMargin: 10
horizontalCenter: parent.horizontalCenter
}
spacing: 120
TabletWebButton {
id: back
enabledColor: hifi.colors.baseGray
enabled: false
text: "BACK"
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;
MouseArea {
anchors.fill: parent
onClicked: goBack()
hoverEnabled: true
}
}
TabletWebButton {
id: close
enabledColor: hifi.colors.darkGray
text: "CLOSE"
MouseArea {
anchors.fill: parent
onClicked: closeWebEngine()
}
}
}
RalewaySemiBold {
id: displayUrl
color: hifi.colors.baseGray
font.pixelSize: 12
anchors {
top: nav.bottom
horizontalCenter: parent.horizontalCenter;
}
}
}
@ -100,15 +86,34 @@ Item {
ListModel {
id: pagesModel
onCountChanged: {
currentPage = count - 1
currentPage = count - 1;
if (currentPage > 0) {
back.enabledColor = hifi.colors.darkGray;
} else {
back.enabledColor = hifi.colors.baseGray;
}
}
}
function goBack() {
if (currentPage > 0) {
currentPage--;
} else if (parentStackItem) {
if (webview.canGoBack && !isUrlLoaded(webview.url)) {
if (currentPage > 0) {
removingPage = true;
pagesModel.remove(currentPage);
}
webview.goBack();
} else if (currentPage > 0) {
removingPage = true;
pagesModel.remove(currentPage);
}
}
function closeWebEngine() {
if (parentStackItem) {
parentStackItem.pop();
} else {
web.visible = false;
}
}
@ -122,6 +127,10 @@ Item {
urlAppend(url)
}
function isUrlLoaded(url) {
return (pagesModel.get(currentPage).webUrl === url);
}
function reloadPage() {
view.reloadAndBypassCache()
view.setActiveFocusOnPress(true);
@ -129,19 +138,26 @@ Item {
}
function urlAppend(url) {
if (removingPage) {
removingPage = false;
return;
}
var lurl = decodeURIComponent(url)
if (lurl[lurl.length - 1] !== "/")
if (lurl[lurl.length - 1] !== "/") {
lurl = lurl + "/"
if (currentPage === -1 || pagesModel.get(currentPage).webUrl !== lurl) {
pagesModel.append({webUrl: lurl})
}
if (currentPage === -1 || (pagesModel.get(currentPage).webUrl !== lurl && !timer.running)) {
timer.start();
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
if (currentPage >= 0 && currentPage < pagesModel.count) {
timer.start();
webview.url = pagesModel.get(currentPage).webUrl;
web.url = webview.url;
web.address = webview.url;
}
}
@ -155,45 +171,125 @@ Item {
property var eventBridge;
}
Loader {
id: loader
Timer {
id: timer
interval: 200
running: false
repeat: false
onTriggered: timer.stop();
}
property WebEngineView currentView: null
WebEngineView {
id: webview
objectName: "webEngineView"
x: 0
y: 0
width: parent.width
height: parent.height - web.headerHeight
asynchronous: true
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - web.headerHeight : parent.height - web.headerHeight
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
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());
loadingPage = true;
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
}
}
}
if (WebEngineView.LoadFailedStatus == loadRequest.status) {
console.log(" Tablet WebEngineView failed to laod url: " + loadRequest.url.toString());
}
}
onNewViewRequested: {
request.openIn(webview);
}
}
HiFiControls.Keyboard {
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
}
Component.onCompleted: {
web.isDesktop = (typeof desktop !== "undefined");
address = url;
loader.active = true
}
Keys.onPressed: {
Keys.onPressed: {
switch(event.key) {
case Qt.Key_L:
if (event.modifiers == Qt.ControlModifier) {
event.accepted = true
addressBar.selectAll()
addressBar.forceActiveFocus()
}
break;
case Qt.Key_L:
if (event.modifiers == Qt.ControlModifier) {
event.accepted = true
}
break;
}
}
}

View file

@ -466,6 +466,11 @@ FocusScope {
return fileDialogBuilder.createObject(desktop, properties);
}
Component { id: assetDialogBuilder; AssetDialog { } }
function assetDialog(properties) {
return assetDialogBuilder.createObject(desktop, properties);
}
function unfocusWindows() {
// First find the active focus item, and unfocus it, all the way
// up the parent chain to the window

View file

@ -0,0 +1,58 @@
//
// AssetDialog.qml
//
// Created by David Rowe on 18 Apr 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 Qt.labs.settings 1.0
import "../styles-uit"
import "../windows"
import "assetDialog"
ModalWindow {
id: root
resizable: true
implicitWidth: 480
implicitHeight: 360
minSize: Qt.vector2d(360, 240)
draggable: true
Settings {
category: "AssetDialog"
property alias width: root.width
property alias height: root.height
property alias x: root.x
property alias y: root.y
}
// Set from OffscreenUi::assetDialog().
property alias caption: root.title
property alias dir: assetDialogContent.dir
property alias filter: assetDialogContent.filter
property alias options: assetDialogContent.options
// Dialog results.
signal selectedAsset(var asset);
signal canceled();
property int titleWidth: 0 // For ModalFrame.
HifiConstants { id: hifi }
AssetDialogContent {
id: assetDialogContent
width: pane.width
height: pane.height
anchors.margins: 0
}
}

View file

@ -0,0 +1,53 @@
//
// TabletAssetDialog.qml
//
// Created by David Rowe on 18 Apr 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 "../windows"
import "assetDialog"
TabletModalWindow {
id: root
anchors.fill: parent
width: parent.width
height: parent.height
// Set from OffscreenUi::assetDialog().
property alias caption: root.title
property alias dir: assetDialogContent.dir
property alias filter: assetDialogContent.filter
property alias options: assetDialogContent.options
// Dialog results.
signal selectedAsset(var asset);
signal canceled();
property int titleWidth: 0 // For TabletModalFrame.
TabletModalFrame {
id: frame
anchors.fill: parent
AssetDialogContent {
id: assetDialogContent
singleClickNavigate: true
width: parent.width - 12
height: parent.height - frame.frameMarginTop - 12
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: parent.height - height - 6
}
}
}
}

View file

@ -0,0 +1,536 @@
//
// AssetDialogContent.qml
//
// Created by David Rowe on 19 Apr 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 "../../controls-uit"
import "../../styles-uit"
import "../fileDialog"
Item {
// Set from OffscreenUi::assetDialog()
property alias dir: assetTableModel.folder
property alias filter: selectionType.filtersString // FIXME: Currently only supports simple filters, "*.xxx".
property int options // Not used.
property bool selectDirectory: false
// Not implemented.
//property bool saveDialog: false;
//property bool multiSelect: false;
property bool singleClickNavigate: false
HifiConstants { id: hifi }
Component.onCompleted: {
homeButton.destination = dir;
if (selectDirectory) {
d.currentSelectionIsFolder = true;
d.currentSelectionPath = assetTableModel.folder;
}
assetTableView.forceActiveFocus();
}
Item {
id: assetDialogItem
anchors.fill: parent
clip: true
MouseArea {
// Clear selection when click on internal unused area.
anchors.fill: parent
drag.target: root
onClicked: {
d.clearSelection();
frame.forceActiveFocus();
assetTableView.forceActiveFocus();
}
}
Row {
id: navControls
anchors {
top: parent.top
topMargin: hifi.dimensions.contentMargin.y
left: parent.left
}
spacing: hifi.dimensions.contentSpacing.x
GlyphButton {
id: upButton
glyph: hifi.glyphs.levelUp
width: height
size: 30
enabled: assetTableModel.parentFolder !== ""
onClicked: d.navigateUp();
}
GlyphButton {
id: homeButton
property string destination: ""
glyph: hifi.glyphs.home
size: 28
width: height
enabled: destination !== ""
//onClicked: d.navigateHome();
onClicked: assetTableModel.folder = destination;
}
}
ComboBox {
id: pathSelector
anchors {
top: parent.top
topMargin: hifi.dimensions.contentMargin.y
left: navControls.right
leftMargin: hifi.dimensions.contentSpacing.x
right: parent.right
}
z: 10
property string lastValidFolder: assetTableModel.folder
function calculatePathChoices(folder) {
var folders = folder.split("/"),
choices = [],
i, length;
if (folders[folders.length - 1] === "") {
folders.pop();
}
choices.push(folders[0]);
for (i = 1, length = folders.length; i < length; i++) {
choices.push(choices[i - 1] + "/" + folders[i]);
}
if (folders[0] === "") {
choices[0] = "/";
}
choices.reverse();
if (choices.length > 0) {
pathSelector.model = choices;
}
}
onLastValidFolderChanged: {
var folder = lastValidFolder;
calculatePathChoices(folder);
}
onCurrentTextChanged: {
var folder = currentText;
if (folder !== "/") {
folder += "/";
}
if (folder !== assetTableModel.folder) {
if (root.selectDirectory) {
currentSelection.text = currentText;
d.currentSelectionPath = currentText;
}
assetTableModel.folder = folder;
assetTableView.forceActiveFocus();
}
}
}
QtObject {
id: d
property string currentSelectionPath
property bool currentSelectionIsFolder
property var tableViewConnection: Connections { target: assetTableView; onCurrentRowChanged: d.update(); }
function update() {
var row = assetTableView.currentRow;
if (row === -1) {
if (!root.selectDirectory) {
currentSelection.text = "";
currentSelectionIsFolder = false;
}
return;
}
var rowInfo = assetTableModel.get(row);
currentSelectionPath = rowInfo.filePath;
currentSelectionIsFolder = rowInfo.fileIsDir;
if (root.selectDirectory || !currentSelectionIsFolder) {
currentSelection.text = currentSelectionPath;
} else {
currentSelection.text = "";
}
}
function navigateUp() {
if (assetTableModel.parentFolder !== "") {
assetTableModel.folder = assetTableModel.parentFolder;
return true;
}
return false;
}
function navigateHome() {
assetTableModel.folder = homeButton.destination;
return true;
}
function clearSelection() {
assetTableView.selection.clear();
assetTableView.currentRow = -1;
update();
}
}
ListModel {
id: assetTableModel
property string folder
property string parentFolder: ""
readonly property string rootFolder: "/"
onFolderChanged: {
parentFolder = calculateParentFolder();
update();
}
function calculateParentFolder() {
if (folder !== "/") {
return folder.slice(0, folder.slice(0, -1).lastIndexOf("/") + 1);
}
return "";
}
function isFolder(row) {
if (row === -1) {
return false;
}
return get(row).fileIsDir;
}
function onGetAllMappings(error, map) {
var mappings,
fileTypeFilter,
index,
path,
fileName,
fileType,
fileIsDir,
isValid,
subDirectory,
subDirectories = [],
fileNameSort,
rows = 0,
lower,
middle,
upper,
i,
length;
clear();
if (error === "") {
mappings = Object.keys(map);
fileTypeFilter = filter.replace("*", "").toLowerCase();
for (i = 0, length = mappings.length; i < length; i++) {
index = mappings[i].lastIndexOf("/");
path = mappings[i].slice(0, mappings[i].lastIndexOf("/") + 1);
fileName = mappings[i].slice(path.length);
fileType = fileName.slice(fileName.lastIndexOf("."));
fileIsDir = false;
isValid = false;
if (fileType.toLowerCase() === fileTypeFilter) {
if (path === folder) {
isValid = !selectDirectory;
} else if (path.length > folder.length) {
subDirectory = path.slice(folder.length);
index = subDirectory.indexOf("/");
if (index === subDirectory.lastIndexOf("/")) {
fileName = subDirectory.slice(0, index);
if (subDirectories.indexOf(fileName) === -1) {
fileIsDir = true;
isValid = true;
subDirectories.push(fileName);
}
}
}
}
if (isValid) {
fileNameSort = (fileIsDir ? "*" : "") + fileName.toLowerCase();
lower = 0;
upper = rows;
while (lower < upper) {
middle = Math.floor((lower + upper) / 2);
var lessThan;
if (fileNameSort < get(middle)["fileNameSort"]) {
lessThan = true;
upper = middle;
} else {
lessThan = false;
lower = middle + 1;
}
}
insert(lower, {
fileName: fileName,
filePath: path + (fileIsDir ? "" : fileName),
fileIsDir: fileIsDir,
fileNameSort: fileNameSort
});
rows++;
}
}
} else {
console.log("Error getting mappings from Asset Server");
}
}
function update() {
d.clearSelection();
clear();
Assets.getAllMappings(onGetAllMappings);
}
}
Table {
id: assetTableView
colorScheme: hifi.colorSchemes.light
anchors {
top: navControls.bottom
topMargin: hifi.dimensions.contentSpacing.y
left: parent.left
right: parent.right
bottom: currentSelection.top
bottomMargin: hifi.dimensions.contentSpacing.y + currentSelection.controlHeight - currentSelection.height
}
model: assetTableModel
focus: true
onClicked: {
if (singleClickNavigate) {
navigateToRow(row);
}
}
onDoubleClicked: navigateToRow(row);
Keys.onReturnPressed: navigateToCurrentRow();
Keys.onEnterPressed: navigateToCurrentRow();
itemDelegate: Item {
clip: true
FontLoader { id: firaSansSemiBold; source: "../../../fonts/FiraSans-SemiBold.ttf"; }
FontLoader { id: firaSansRegular; source: "../../../fonts/FiraSans-Regular.ttf"; }
FiraSansSemiBold {
text: styleData.value
elide: styleData.elideMode
anchors {
left: parent.left
leftMargin: hifi.dimensions.tablePadding
right: parent.right
rightMargin: hifi.dimensions.tablePadding
verticalCenter: parent.verticalCenter
}
size: hifi.fontSizes.tableText
color: hifi.colors.baseGrayHighlight
font.family: (styleData.row !== -1 && assetTableView.model.get(styleData.row).fileIsDir)
? firaSansSemiBold.name : firaSansRegular.name
}
}
TableViewColumn {
id: fileNameColumn
role: "fileName"
title: "Name"
width: assetTableView.width
movable: false
resizable: false
}
function navigateToRow(row) {
currentRow = row;
navigateToCurrentRow();
}
function navigateToCurrentRow() {
if (model.isFolder(currentRow)) {
model.folder = model.get(currentRow).filePath;
} else {
okAction.trigger();
}
}
Timer {
id: prefixClearTimer
interval: 1000
repeat: false
running: false
onTriggered: assetTableView.prefix = "";
}
property string prefix: ""
function addToPrefix(event) {
if (!event.text || event.text === "") {
return false;
}
var newPrefix = prefix + event.text.toLowerCase();
var matchedIndex = -1;
for (var i = 0; i < model.count; ++i) {
var name = model.get(i).fileName.toLowerCase();
if (0 === name.indexOf(newPrefix)) {
matchedIndex = i;
break;
}
}
if (matchedIndex !== -1) {
assetTableView.selection.clear();
assetTableView.selection.select(matchedIndex);
assetTableView.currentRow = matchedIndex;
assetTableView.prefix = newPrefix;
}
prefixClearTimer.restart();
return true;
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Backspace:
case Qt.Key_Tab:
case Qt.Key_Backtab:
event.accepted = false;
break;
default:
if (addToPrefix(event)) {
event.accepted = true
} else {
event.accepted = false;
}
break;
}
}
}
TextField {
id: currentSelection
label: selectDirectory ? "Directory:" : "File name:"
anchors {
left: parent.left
right: selectionType.visible ? selectionType.left: parent.right
rightMargin: selectionType.visible ? hifi.dimensions.contentSpacing.x : 0
bottom: buttonRow.top
bottomMargin: hifi.dimensions.contentSpacing.y
}
readOnly: true
activeFocusOnTab: !readOnly
onActiveFocusChanged: if (activeFocus) { selectAll(); }
onAccepted: okAction.trigger();
}
FileTypeSelection {
id: selectionType
anchors {
top: currentSelection.top
left: buttonRow.left
right: parent.right
}
visible: !selectDirectory && filtersCount > 1
KeyNavigation.left: assetTableView
KeyNavigation.right: openButton
}
Action {
id: okAction
text: currentSelection.text && root.selectDirectory && assetTableView.currentRow === -1 ? "Choose" : "Open"
enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false
onTriggered: {
if (!root.selectDirectory && !d.currentSelectionIsFolder
|| root.selectDirectory && assetTableView.currentRow === -1) {
selectedAsset(d.currentSelectionPath);
root.destroy();
} else {
assetTableView.navigateToCurrentRow();
}
}
}
Action {
id: cancelAction
text: "Cancel"
onTriggered: {
canceled();
root.destroy();
}
}
Row {
id: buttonRow
anchors {
right: parent.right
bottom: parent.bottom
}
spacing: hifi.dimensions.contentSpacing.y
Button {
id: openButton
color: hifi.buttons.blue
action: okAction
Keys.onReturnPressed: okAction.trigger()
KeyNavigation.up: selectionType
KeyNavigation.left: selectionType
KeyNavigation.right: cancelButton
}
Button {
id: cancelButton
action: cancelAction
KeyNavigation.up: selectionType
KeyNavigation.left: openButton
KeyNavigation.right: assetTableView.contentItem
Keys.onReturnPressed: { canceled(); root.enabled = false }
}
}
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Backspace:
event.accepted = d.navigateUp();
break;
case Qt.Key_Home:
event.accepted = d.navigateHome();
break;
}
}
}

View file

@ -16,7 +16,6 @@ import "../../hifi/tablet/tabletWindows/preferences"
Preference {
id: root
property alias text: dataTextField.text
property alias buttonText: button.text
property alias placeholderText: dataTextField.placeholderText
property var browser;
height: control.height + hifi.dimensions.controlInterlineHeight
@ -58,28 +57,46 @@ Preference {
right: parent.right
bottom: parent.bottom
}
height: Math.max(dataTextField.controlHeight, button.height)
height: dataTextField.controlHeight + bookmarkAvatarButton.height + hifi.dimensions.contentSpacing.y
TextField {
id: dataTextField
label: root.label
placeholderText: root.placeholderText
text: preference.value
label: root.label
colorScheme: dataTextField.acceptableInput ? hifi.colorSchemes.dark : hifi.colorSchemes.light
validator: RegExpValidator {
regExp: /.*\.(?:fst).*\?*/ig
}
anchors {
left: parent.left
right: button.left
rightMargin: hifi.dimensions.contentSpacing.x
bottom: parent.bottom
right: parent.right
bottom: bookmarkAvatarButton.top
bottomMargin: hifi.dimensions.contentSpacing.y
}
colorScheme: hifi.colorSchemes.dark
}
QueuedButton {
id: bookmarkAvatarButton
text: "Bookmark Avatar"
width: 140
visible: dataTextField.acceptableInput
anchors {
left: parent.left
bottom: parent.bottom
rightMargin: hifi.dimensions.contentSpacing.x
}
onClickedQueued: ApplicationInterface.loadAddAvatarBookmarkDialog()
}
Button {
id: button
text: "Browse"
id: browseAvatarsButton
text: "Browse Avatars"
width: 140
anchors {
right: parent.right
verticalCenter: dataTextField.verticalCenter
left: dataTextField.acceptableInput ? bookmarkAvatarButton.right : parent.left
bottom: parent.bottom
leftMargin: dataTextField.acceptableInput ? hifi.dimensions.contentSpacing.x : 0
}
onClicked: {
if (typeof desktop !== "undefined") {
@ -103,5 +120,6 @@ Preference {
eventBridge: tabletRoot.eventBridge
}
}
}
}

View file

@ -35,6 +35,7 @@ Rectangle {
property string timePhrase: pastTime(timestamp);
property int onlineUsers: 0;
property bool isConcurrency: action === 'concurrency';
property bool isAnnouncement: action === 'announcement';
property bool isStacked: !isConcurrency && drillDownToPlace;
property int textPadding: 10;
@ -80,7 +81,7 @@ Rectangle {
id: lobby;
visible: !hasGif || (animation.status !== Image.Ready);
width: parent.width - (isConcurrency ? 0 : (2 * smallMargin));
height: parent.height - (isConcurrency ? 0 : smallMargin);
height: parent.height - messageHeight - (isConcurrency ? 0 : smallMargin);
source: thumbnail || defaultThumbnail;
fillMode: Image.PreserveAspectCrop;
anchors {
@ -129,7 +130,7 @@ Rectangle {
property int dropSamples: 9;
property int dropSpread: 0;
DropShadow {
visible: true;
visible: showPlace; // Do we have to check for whatever the modern equivalent is for desktop.gradientsSupported?
source: place;
anchors.fill: place;
horizontalOffset: dropHorizontalOffset;
@ -139,12 +140,12 @@ Rectangle {
color: hifi.colors.black;
spread: dropSpread;
}
RalewayLight {
RalewaySemiBold {
id: place;
visible: showPlace;
text: placeName;
color: hifi.colors.white;
size: 38;
size: textSize;
elide: Text.ElideRight; // requires constrained width
anchors {
top: parent.top;
@ -153,57 +154,44 @@ Rectangle {
margins: textPadding;
}
}
Rectangle {
id: rectRow
z: 1
width: message.width + (users.visible ? users.width + bottomRow.spacing : 0)
+ (icon.visible ? icon.width + bottomRow.spacing: 0) + bottomRow.spacing;
height: messageHeight + 1;
radius: 25
anchors {
bottom: parent.bottom
left: parent.left
leftMargin: textPadding
bottomMargin: textPadding
Row {
FiraSansRegular {
id: users;
visible: isConcurrency || isAnnouncement;
text: onlineUsers;
size: textSize;
color: messageColor;
anchors.verticalCenter: message.verticalCenter;
}
Row {
id: bottomRow
FiraSansRegular {
id: users;
visible: isConcurrency;
text: onlineUsers;
size: textSize;
color: messageColor;
anchors.verticalCenter: message.verticalCenter;
}
Image {
id: icon;
source: "../../images/snap-icon.svg"
width: 40;
height: 40;
visible: action === 'snapshot';
}
RalewayRegular {
id: message;
text: isConcurrency ? ((onlineUsers === 1) ? "person" : "people") : (drillDownToPlace ? "snapshots" : ("by " + userName));
size: textSizeSmall;
color: messageColor;
elide: Text.ElideRight; // requires a width to be specified`
anchors {
bottom: parent.bottom;
bottomMargin: parent.spacing;
}
}
spacing: textPadding;
height: messageHeight;
Image {
id: icon;
source: "../../images/snap-icon.svg"
width: 40;
height: 40;
visible: action === 'snapshot';
}
RalewayRegular {
id: message;
text: isConcurrency ? ((onlineUsers === 1) ? "person" : "people") : (isAnnouncement ? "connections" : (drillDownToPlace ? "snapshots" : ("by " + userName)));
size: textSizeSmall;
color: messageColor;
elide: Text.ElideRight; // requires a width to be specified`
width: root.width - textPadding
- (users.visible ? users.width + parent.spacing : 0)
- (icon.visible ? icon.width + parent.spacing : 0)
- (actionIcon.width + (2 * smallMargin));
anchors {
bottom: parent.bottom;
left: parent.left;
leftMargin: 4
bottomMargin: parent.spacing;
}
}
spacing: textPadding;
height: messageHeight;
anchors {
bottom: parent.bottom;
left: parent.left;
leftMargin: textPadding;
}
}
// These two can be supplied to provide hover behavior.
// For example, AddressBarDialog provides functions that set the current list view item
@ -218,37 +206,22 @@ Rectangle {
onEntered: hoverThunk();
onExited: unhoverThunk();
}
Rectangle {
id: rectIcon
z: 1
width: 32
height: 32
radius: 15
StateImage {
id: actionIcon;
imageURL: "../../images/info-icon-2-state.svg";
size: 32;
buttonState: messageArea.containsMouse ? 1 : 0;
anchors {
bottom: parent.bottom;
right: parent.right;
bottomMargin: textPadding;
rightMargin: textPadding;
}
StateImage {
id: actionIcon;
imageURL: "../../images/info-icon-2-state.svg";
size: 32;
buttonState: messageArea.containsMouse ? 1 : 0;
anchors {
bottom: parent.bottom;
right: parent.right;
//margins: smallMargin;
}
margins: smallMargin;
}
}
MouseArea {
id: messageArea;
width: rectIcon.width;
height: rectIcon.height;
anchors.fill: rectIcon
width: parent.width;
height: messageHeight;
anchors.top: lobby.bottom;
acceptedButtons: Qt.LeftButton;
onClicked: goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId));
hoverEnabled: true;

View file

@ -0,0 +1,201 @@
//
// Feed.qml
// qml/hifi
//
// Displays a particular type of feed
//
// Created by Howard Stearns on 4/18/2017
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.5
import QtGraphicalEffects 1.0
import "toolbars"
import "../styles-uit"
Column {
id: root;
visible: false;
property int cardWidth: 212;
property int cardHeight: 152;
property int stackedCardShadowHeight: 10;
property string metaverseServerUrl: '';
property string actions: 'snapshot';
onActionsChanged: fillDestinations();
Component.onCompleted: fillDestinations();
property string labelText: actions;
property string filter: '';
onFilterChanged: filterChoicesByText();
property var goFunction: null;
HifiConstants { id: hifi }
ListModel { id: suggestions; }
function resolveUrl(url) {
return (url.indexOf('/') === 0) ? (metaverseServerUrl + url) : url;
}
function makeModelData(data) { // create a new obj from data
// ListModel elements will only ever have those properties that are defined by the first obj that is added.
// So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story.
var name = data.place_name,
tags = data.tags || [data.action, data.username],
description = data.description || "",
thumbnail_url = data.thumbnail_url || "";
if (actions === 'concurrency,snapshot') {
// A temporary hack for simulating announcements. We won't use this in production, but if requested, we'll use this data like announcements.
data.details.connections = 4;
data.action = 'announcement';
}
return {
place_name: name,
username: data.username || "",
path: data.path || "",
created_at: data.created_at || "",
action: data.action || "",
thumbnail_url: resolveUrl(thumbnail_url),
image_url: resolveUrl(data.details && data.details.image_url),
metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
tags: tags,
description: description,
online_users: data.details.connections || data.details.concurrency || 0,
drillDownToPlace: false,
searchText: [name].concat(tags, description || []).join(' ').toUpperCase()
}
}
property var allStories: [];
property var placeMap: ({}); // Used for making stacks.
property int requestId: 0;
function getUserStoryPage(pageNumber, cb, cb1) { // cb(error) after all pages of domain data have been added to model
// If supplied, cb1 will be run after the first page IFF it is not the last, for responsiveness.
var options = [
'now=' + new Date().toISOString(),
'include_actions=' + actions,
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
'require_online=true',
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
'page=' + pageNumber
];
var url = metaverseBase + 'user_stories?' + options.join('&');
var thisRequestId = ++requestId;
getRequest(url, function (error, data) {
if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) {
return; // abandon stale requests
}
allStories = allStories.concat(data.user_stories.map(makeModelData));
if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now
if ((pageNumber === 1) && cb1) {
cb1();
}
return getUserStoryPage(pageNumber + 1, cb);
}
cb();
});
}
function fillDestinations() { // Public
var filter = makeFilteredStoryProcessor(), counter = 0;
allStories = [];
suggestions.clear();
placeMap = {};
getUserStoryPage(1, function (error) {
allStories.slice(counter).forEach(filter);
console.log('user stories query', actions, error || 'ok', allStories.length, 'filtered to', suggestions.count);
root.visible = !!suggestions.count;
}, function () { // If there's more than a page, put what we have in the model right away, keeping track of how many are processed.
allStories.forEach(function (story) {
counter++;
filter(story);
root.visible = !!suggestions.count;
});
});
}
function makeFilteredStoryProcessor() { // answer a function(storyData) that adds it to suggestions if it matches
var words = filter.toUpperCase().split(/\s+/).filter(identity);
function suggestable(story) { // fixme add to makeFilteredStoryProcessor
if (story.action === 'snapshot') {
return true;
}
return (story.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain.
}
function matches(story) {
if (!words.length) {
return suggestable(story);
}
return words.every(function (word) {
return story.searchText.indexOf(word) >= 0;
});
}
function addToSuggestions(place) {
var collapse = ((actions === 'concurrency,snapshot') && (place.action !== 'concurrency')) || (place.action === 'announcement');
if (collapse) {
var existing = placeMap[place.place_name];
if (existing) {
existing.drillDownToPlace = true;
return;
}
}
suggestions.append(place);
if (collapse) {
placeMap[place.place_name] = suggestions.get(suggestions.count - 1);
} else if (place.action === 'concurrency') {
suggestions.get(suggestions.count - 1).drillDownToPlace = true; // Don't change raw place object (in allStories).
}
}
return function (story) {
if (matches(story)) {
addToSuggestions(story);
}
};
}
function filterChoicesByText() {
suggestions.clear();
placeMap = {};
allStories.forEach(makeFilteredStoryProcessor());
root.visible = !!suggestions.count;
}
RalewayLight {
id: label;
text: labelText;
color: hifi.colors.white;
size: 28;
}
ListView {
id: scroll;
clip: true;
model: suggestions;
orientation: ListView.Horizontal;
highlightMoveDuration: -1;
highlightMoveVelocity: -1;
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.blueHighlight; z: 1; }
spacing: 14;
width: parent.width;
height: cardHeight + stackedCardShadowHeight;
delegate: Card {
width: cardWidth;
height: cardHeight;
goFunction: root.goFunction;
userName: model.username;
placeName: model.place_name;
hifiUrl: model.place_name + model.path;
thumbnail: model.thumbnail_url;
imageUrl: model.image_url;
action: model.action;
timestamp: model.created_at;
onlineUsers: model.online_users;
storyId: model.metaverseId;
drillDownToPlace: model.drillDownToPlace;
shadowHeight: stackedCardShadowHeight;
hoverThunk: function () { scroll.currentIndex = index; }
unhoverThunk: function () { scroll.currentIndex = -1; }
}
}
}

View file

@ -219,41 +219,18 @@ ScrollingWindow {
Row {
spacing: hifi.dimensions.contentSpacing.x
HifiControls.Button {
HifiControls.QueuedButton {
text: "from URL"
color: hifi.buttons.black
height: 26
onClicked: fromUrlTimer.running = true
// For some reason trigginer an API that enters
// an internal event loop directly from the button clicked
// trigger below causes the appliction to behave oddly.
// Most likely because the button onClicked handling is never
// completed until the function returns.
// FIXME find a better way of handling the input dialogs that
// doesn't trigger this.
Timer {
id: fromUrlTimer
interval: 5
repeat: false
running: false
onTriggered: ApplicationInterface.loadScriptURLDialog();
}
onClickedQueued: ApplicationInterface.loadScriptURLDialog()
}
HifiControls.Button {
HifiControls.QueuedButton {
text: "from Disk"
color: hifi.buttons.black
height: 26
onClicked: fromDiskTimer.running = true
Timer {
id: fromDiskTimer
interval: 5
repeat: false
running: false
onTriggered: ApplicationInterface.loadDialog();
}
onClickedQueued: ApplicationInterface.loadDialog()
}
HifiControls.Button {

View file

@ -1,19 +1,11 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebEngine 1.1
import QtWebChannel 1.0
import QtQuick.Controls.Styles 1.4
import "../../controls"
import "../toolbars"
import HFWebEngineProfile 1.0
import QtGraphicalEffects 1.0
import "../../controls-uit" as HifiControls
import "../../styles-uit"
StackView {
id: editRoot
objectName: "stack"
initialItem: editBasePage
initialItem: Qt.resolvedUrl('EditTabView.qml')
property var eventBridge;
signal sendToScript(var message);
@ -30,270 +22,10 @@ StackView {
editRoot.pop();
}
Component {
id: editBasePage
TabView {
id: editTabView
// anchors.fill: parent
height: 60
Tab {
title: "CREATE"
active: true
enabled: true
property string originalUrl: ""
Rectangle {
color: "#404040"
Text {
color: "#ffffff"
text: "Choose an Entity Type to Create:"
font.pixelSize: 14
font.bold: true
anchors.top: parent.top
anchors.topMargin: 28
anchors.left: parent.left
anchors.leftMargin: 28
}
Flow {
id: createEntitiesFlow
spacing: 35
anchors.right: parent.right
anchors.rightMargin: 55
anchors.left: parent.left
anchors.leftMargin: 55
anchors.top: parent.top
anchors.topMargin: 70
NewEntityButton {
icon: "icons/create-icons/94-model-01.svg"
text: "MODEL"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newModelButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/21-cube-01.svg"
text: "CUBE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newCubeButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/22-sphere-01.svg"
text: "SPHERE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newSphereButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/24-light-01.svg"
text: "LIGHT"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newLightButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/20-text-01.svg"
text: "TEXT"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newTextButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/25-web-1-01.svg"
text: "WEB"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newWebButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/23-zone-01.svg"
text: "ZONE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newZoneButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/90-particles-01.svg"
text: "PARTICLE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" }
});
editTabView.currentIndex = 2
}
}
}
HifiControls.Button {
id: assetServerButton
text: "Open This Domain's Asset Server"
color: hifi.buttons.black
colorScheme: hifi.colorSchemes.dark
anchors.right: parent.right
anchors.rightMargin: 55
anchors.left: parent.left
anchors.leftMargin: 55
anchors.top: createEntitiesFlow.bottom
anchors.topMargin: 35
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "openAssetBrowserButton" }
});
}
}
HifiControls.Button {
text: "Import Entities (.json)"
color: hifi.buttons.black
colorScheme: hifi.colorSchemes.dark
anchors.right: parent.right
anchors.rightMargin: 55
anchors.left: parent.left
anchors.leftMargin: 55
anchors.top: assetServerButton.bottom
anchors.topMargin: 20
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "importEntitiesButton" }
});
}
}
}
}
Tab {
title: "LIST"
active: true
enabled: true
property string originalUrl: ""
WebView {
id: entityListToolWebView
url: "../../../../../scripts/system/html/entityList.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent
enabled: true
}
}
Tab {
title: "PROPERTIES"
active: true
enabled: true
property string originalUrl: ""
WebView {
id: entityPropertiesWebView
url: "../../../../../scripts/system/html/entityProperties.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent
enabled: true
}
}
Tab {
title: "GRID"
active: true
enabled: true
property string originalUrl: ""
WebView {
id: gridControlsWebView
url: "../../../../../scripts/system/html/gridControls.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent
enabled: true
}
}
Tab {
title: "P"
active: true
enabled: true
property string originalUrl: ""
WebView {
id: particleExplorerWebView
url: "../../../../../scripts/system/particle_explorer/particleExplorer.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent
enabled: true
}
}
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {
color: styleData.selected ? "#404040" :"black"
implicitWidth: text.width + 42
implicitHeight: 40
Text {
id: text
anchors.centerIn: parent
text: styleData.title
font.pixelSize: 16
font.bold: true
color: styleData.selected ? "white" : "white"
property string glyphtext: ""
HiFiGlyphs {
anchors.centerIn: parent
size: 30
color: "#ffffff"
text: text.glyphtext
}
Component.onCompleted: if (styleData.title == "P") {
text.text = " ";
text.glyphtext = "\ue004";
}
}
}
tabBar: Rectangle {
color: "black"
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
}
}
}
// Passes script messages to the item on the top of the stack
function fromScript(message) {
var currentItem = editRoot.currentItem;
if (currentItem && currentItem.fromScript)
currentItem.fromScript(message);
}
}

View file

@ -0,0 +1,318 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtWebEngine 1.1
import QtWebChannel 1.0
import QtQuick.Controls.Styles 1.4
import "../../controls"
import "../toolbars"
import HFWebEngineProfile 1.0
import QtGraphicalEffects 1.0
import "../../controls-uit" as HifiControls
import "../../styles-uit"
TabView {
id: editTabView
// anchors.fill: parent
height: 60
Tab {
title: "CREATE"
active: true
enabled: true
property string originalUrl: ""
Rectangle {
color: "#404040"
Text {
color: "#ffffff"
text: "Choose an Entity Type to Create:"
font.pixelSize: 14
font.bold: true
anchors.top: parent.top
anchors.topMargin: 28
anchors.left: parent.left
anchors.leftMargin: 28
}
Flow {
id: createEntitiesFlow
spacing: 35
anchors.right: parent.right
anchors.rightMargin: 55
anchors.left: parent.left
anchors.leftMargin: 55
anchors.top: parent.top
anchors.topMargin: 70
NewEntityButton {
icon: "icons/create-icons/94-model-01.svg"
text: "MODEL"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newModelButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/21-cube-01.svg"
text: "CUBE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newCubeButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/22-sphere-01.svg"
text: "SPHERE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newSphereButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/24-light-01.svg"
text: "LIGHT"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newLightButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/20-text-01.svg"
text: "TEXT"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newTextButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/25-web-1-01.svg"
text: "WEB"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newWebButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/23-zone-01.svg"
text: "ZONE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newZoneButton" }
});
editTabView.currentIndex = 2
}
}
NewEntityButton {
icon: "icons/create-icons/90-particles-01.svg"
text: "PARTICLE"
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" }
});
editTabView.currentIndex = 4
}
}
}
HifiControls.Button {
id: assetServerButton
text: "Open This Domain's Asset Server"
color: hifi.buttons.black
colorScheme: hifi.colorSchemes.dark
anchors.right: parent.right
anchors.rightMargin: 55
anchors.left: parent.left
anchors.leftMargin: 55
anchors.top: createEntitiesFlow.bottom
anchors.topMargin: 35
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "openAssetBrowserButton" }
});
}
}
HifiControls.Button {
text: "Import Entities (.json)"
color: hifi.buttons.black
colorScheme: hifi.colorSchemes.dark
anchors.right: parent.right
anchors.rightMargin: 55
anchors.left: parent.left
anchors.leftMargin: 55
anchors.top: assetServerButton.bottom
anchors.topMargin: 20
onClicked: {
editRoot.sendToScript({
method: "newEntityButtonClicked", params: { buttonName: "importEntitiesButton" }
});
}
}
}
}
Tab {
title: "LIST"
active: true
enabled: true
property string originalUrl: ""
WebView {
id: entityListToolWebView
url: "../../../../../scripts/system/html/entityList.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent
enabled: true
}
}
Tab {
title: "PROPERTIES"
active: true
enabled: true
property string originalUrl: ""
WebView {
id: entityPropertiesWebView
url: "../../../../../scripts/system/html/entityProperties.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent
enabled: true
}
}
Tab {
title: "GRID"
active: true
enabled: true
property string originalUrl: ""
WebView {
id: gridControlsWebView
url: "../../../../../scripts/system/html/gridControls.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent
enabled: true
}
}
Tab {
title: "P"
active: true
enabled: true
property string originalUrl: ""
WebView {
id: particleExplorerWebView
url: "../../../../../scripts/system/particle_explorer/particleExplorer.html"
eventBridge: editRoot.eventBridge
anchors.fill: parent
enabled: true
}
}
style: TabViewStyle {
frameOverlap: 1
tab: Rectangle {
color: styleData.selected ? "#404040" :"black"
implicitWidth: text.width + 42
implicitHeight: 40
Text {
id: text
anchors.centerIn: parent
text: styleData.title
font.pixelSize: 16
font.bold: true
color: styleData.selected ? "white" : "white"
property string glyphtext: ""
HiFiGlyphs {
anchors.centerIn: parent
size: 30
color: "#ffffff"
text: text.glyphtext
}
Component.onCompleted: if (styleData.title == "P") {
text.text = " ";
text.glyphtext = "\ue004";
}
}
}
tabBar: Rectangle {
color: "black"
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: parent.top
anchors.topMargin: 0
}
}
function fromScript(message) {
switch (message.method) {
case 'selectTab':
selectTab(message.params.id);
break;
default:
console.warn('Unrecognized message:', JSON.stringify(message));
}
}
// Changes the current tab based on tab index or title as input
function selectTab(id) {
if (typeof id === 'number') {
if (id >= 0 && id <= 4) {
editTabView.currentIndex = id;
} else {
console.warn('Attempt to switch to invalid tab:', id);
}
} else if (typeof id === 'string'){
switch (id.toLowerCase()) {
case 'create':
editTabView.currentIndex = 0;
break;
case 'list':
editTabView.currentIndex = 1;
break;
case 'properties':
editTabView.currentIndex = 2;
break;
case 'grid':
editTabView.currentIndex = 3;
break;
case 'particle':
editTabView.currentIndex = 4;
break;
default:
console.warn('Attempt to switch to invalid tab:', id);
}
} else {
console.warn('Attempt to switch tabs with invalid input:', JSON.stringify(id));
}
}
}

View file

@ -0,0 +1,170 @@
//
// Created by Dante Ruiz 2017/04/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
//
import QtQuick 2.5
import Hifi 1.0
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2 as OriginalDialogs
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows"
import "../../dialogs"
Rectangle {
id: inputRecorder
property var eventBridge;
HifiConstants { id: hifi }
signal sendToScript(var message);
color: hifi.colors.baseGray;
property string path: ""
property string dir: ""
property var dialog: null;
property bool recording: false;
Component { id: fileDialog; TabletFileDialog { } }
Row {
id: topButtons
width: parent.width
height: 40
spacing: 40
anchors {
left: parent.left
right: parent.right
top: parent.top
topMargin: 10
}
HifiControls.Button {
id: start
text: "Start Recoring"
color: hifi.buttons.black
enabled: true
onClicked: {
if (inputRecorder.recording) {
sendToScript({method: "Stop"});
inputRecorder.recording = false;
start.text = "Start Recording";
selectedFile.text = "Current recording is not saved";
} else {
sendToScript({method: "Start"});
inputRecorder.recording = true;
start.text = "Stop Recording";
}
}
}
HifiControls.Button {
id: save
text: "Save Recording"
color: hifi.buttons.black
enabled: true
onClicked: {
sendToScript({method: "Save"});
selectedFile.text = "";
}
}
HifiControls.Button {
id: playBack
anchors.right: browse.left
anchors.top: selectedFile.bottom
anchors.topMargin: 10
text: "Play Recording"
color: hifi.buttons.black
enabled: true
onClicked: {
sendToScript({method: "playback"});
HMD.closeTablet();
}
}
}
HifiControls.VerticalSpacer {}
HifiControls.TextField {
id: selectedFile
anchors.left: parent.left
anchors.right: parent.right
anchors.top: topButtons.top
anchors.topMargin: 40
colorScheme: hifi.colorSchemes.dark
readOnly: true
}
HifiControls.Button {
id: browse
anchors.right: parent.right
anchors.top: selectedFile.bottom
anchors.topMargin: 10
text: "Load..."
color: hifi.buttons.black
enabled: true
onClicked: {
dialog = fileDialog.createObject(inputRecorder);
dialog.caption = "InputRecorder";
console.log(dialog.dir);
dialog.dir = "file:///" + inputRecorder.dir;
dialog.selectedFile.connect(getFileSelected);
}
}
Column {
id: notes
anchors.centerIn: parent;
spacing: 20
Text {
text: "All files are saved under the folder 'hifi-input-recording' in AppData directory";
color: "white"
font.pointSize: 10
}
Text {
text: "To cancel a recording playback press Alt-B"
color: "white"
font.pointSize: 10
}
}
function getFileSelected(file) {
selectedFile.text = file;
inputRecorder.path = file;
sendToScript({method: "Load", params: {file: path }});
}
function fromScript(message) {
switch (message.method) {
case "update":
updateButtonStatus(message.params);
break;
case "path":
console.log(message.params);
inputRecorder.dir = message.params;
break;
}
}
function updateButtonStatus(status) {
inputRecorder.recording = status;
if (inputRecorder.recording) {
start.text = "Stop Recording";
} else {
start.text = "Start Recording";
}
}
}

View file

@ -20,38 +20,29 @@ import "../toolbars"
import "../../styles-uit" as HifiStyles
import "../../controls-uit" as HifiControls
// references HMD, AddressManager, AddressBarDialog from root context
StackView {
id: root
id: root;
HifiConstants { id: hifi }
HifiStyles.HifiConstants { id: hifiStyleConstants }
initialItem: addressBarDialog
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 int cardWidth: 212;
property int cardHeight: 152;
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
property var tablet: null;
property bool isDesktop: false;
Component { id: tabletStoryCard; TabletStoryCard {} }
Component { id: tabletWebView; TabletWebView {} }
Component.onCompleted: {
root.currentItem.focus = true;
root.currentItem.forceActiveFocus();
addressLine.focus = true;
addressLine.forceActiveFocus();
fillDestinations();
updateLocationText(false);
addressLine.focus = !HMD.active;
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);
@ -68,8 +59,9 @@ StackView {
}
function goCard(targetString) {
if (0 !== targetString.indexOf('hifi://')) {
var card = tabletStoryCard.createObject();
card.setUrl(addressBarDialog.metaverseServerUrl + targetString);
var card = tabletWebView.createObject();
card.url = addressBarDialog.metaverseServerUrl + targetString;
card.parentStackItem = root;
card.eventBridge = root.eventBridge;
root.push(card);
return;
@ -78,8 +70,6 @@ StackView {
toggleOrGo(true, targetString);
clearAddressLineTimer.start();
}
property bool isCursorVisible: false // Override default cursor visibility.
AddressBarDialog {
@ -102,16 +92,11 @@ StackView {
onMetaverseServerUrlChanged: updateLocationTextTimer.start();
Rectangle {
id: navBar
width: 480
height: 70
width: parent.width
height: 50;
color: hifiStyleConstants.colors.white
anchors {
top: parent.top
right: parent.right
rightMargin: 0
left: parent.left
leftMargin: 0
}
anchors.top: parent.top;
anchors.left: parent.left;
ToolbarButton {
id: homeButton
@ -129,8 +114,14 @@ StackView {
}
ToolbarButton {
id: backArrow;
buttonState: addressBarDialog.backEnabled;
imageURL: "../../../images/backward.svg";
onClicked: addressBarDialog.loadBack();
buttonEnabled: addressBarDialog.backEnabled;
onClicked: {
if (buttonEnabled) {
addressBarDialog.loadBack();
}
}
anchors {
left: homeButton.right
verticalCenter: parent.verticalCenter
@ -138,8 +129,14 @@ StackView {
}
ToolbarButton {
id: forwardArrow;
buttonState: addressBarDialog.forwardEnabled;
imageURL: "../../../images/forward.svg";
onClicked: addressBarDialog.loadForward();
buttonEnabled: addressBarDialog.forwardEnabled;
onClicked: {
if (buttonEnabled) {
addressBarDialog.loadForward();
}
}
anchors {
left: backArrow.right
verticalCenter: parent.verticalCenter
@ -149,190 +146,143 @@ StackView {
Rectangle {
id: addressBar
width: 480
width: parent.width
height: 70
color: hifiStyleConstants.colors.white
anchors {
top: navBar.bottom
right: parent.right
rightMargin: 16
left: parent.left
leftMargin: 16
top: navBar.bottom;
left: parent.left;
}
property int inputAreaHeight: 70
property int inputAreaStep: (height - inputAreaHeight) / 2
HifiStyles.RalewayLight {
id: notice;
font.pixelSize: hifi.fonts.pixelSize * 0.50;
font.pixelSize: hifi.fonts.pixelSize * 0.7;
anchors {
top: parent.top
topMargin: parent.inputAreaStep + 12
left: addressLine.left
right: addressLine.right
top: parent.top;
left: addressLineContainer.left;
right: addressLineContainer.right;
}
}
HifiStyles.FiraSansRegular {
id: location;
anchors {
left: addressLineContainer.left;
leftMargin: 8;
verticalCenter: addressLineContainer.verticalCenter;
}
font.pixelSize: addressLine.font.pixelSize;
color: "gray";
clip: true;
anchors.fill: addressLine;
visible: addressLine.text.length === 0
}
TextInput {
id: addressLine
focus: true
width: addressLineContainer.width - addressLineContainer.anchors.leftMargin - addressLineContainer.anchors.rightMargin;
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
leftMargin: 16
rightMargin: 16
topMargin: parent.inputAreaStep + (2 * hifi.layout.spacing)
bottomMargin: parent.inputAreaStep
left: addressLineContainer.left;
leftMargin: 8;
verticalCenter: addressLineContainer.verticalCenter;
}
font.pixelSize: hifi.fonts.pixelSize * 0.75
cursorVisible: false
onTextChanged: {
filterChoicesByText();
updateLocationText(text.length > 0);
if (!isCursorVisible && text.length > 0) {
isCursorVisible = true;
cursorVisible = true;
}
}
onAccepted: {
addressBarDialog.keyboardEnabled = false;
}
onActiveFocusChanged: {
cursorVisible = isCursorVisible && focus;
}
MouseArea {
// If user clicks in address bar show cursor to indicate ability to enter address.
anchors.fill: parent
onClicked: {
isCursorVisible = true;
parent.cursorVisible = true;
parent.focus = true;
parent.forceActiveFocus();
addressBarDialog.keyboardEnabled = HMD.active
tabletRoot.playButtonClickSound();
}
toggleOrGo();
}
}
Rectangle {
anchors.fill: addressLine
id: addressLineContainer;
height: 40;
anchors {
top: notice.bottom;
topMargin: 2;
left: parent.left;
leftMargin: 16;
right: parent.right;
rightMargin: 16;
}
color: hifiStyleConstants.colors.lightGray
opacity: 0.1
}
}
Rectangle {
id: topBar
height: 37
color: hifiStyleConstants.colors.white
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.topMargin: 0
anchors.top: addressBar.bottom
Row {
id: thing
spacing: 5 * hifi.layout.spacing
anchors {
top: parent.top;
left: parent.left
leftMargin: 25
}
TabletTextButton {
id: allTab;
text: "ALL";
property string includeActions: 'snapshot,concurrency';
selected: allTab === selectedTab;
action: tabSelect;
}
TabletTextButton {
id: placeTab;
text: "PLACES";
property string includeActions: 'concurrency';
selected: placeTab === selectedTab;
action: tabSelect;
}
TabletTextButton {
id: snapTab;
text: "SNAP";
property string includeActions: 'snapshot';
selected: snapTab === selectedTab;
action: tabSelect;
MouseArea {
anchors.fill: parent;
onClicked: {
if (!addressLine.focus || !HMD.active) {
addressLine.focus = true;
addressLine.forceActiveFocus();
addressBarDialog.keyboardEnabled = HMD.active;
}
tabletRoot.playButtonClickSound();
}
}
}
}
Rectangle {
id: bgMain
color: hifiStyleConstants.colors.white
anchors.bottom: parent.keyboardEnabled ? keyboard.top : parent.bottom
anchors.bottomMargin: 0
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.top: topBar.bottom
anchors.topMargin: 0
ListModel { id: suggestions }
ListView {
id: scroll
property int stackedCardShadowHeight: 0;
clip: true
spacing: 14
anchors {
bottom: parent.bottom
top: parent.top
left: parent.left
right: parent.right
leftMargin: 10
id: bgMain;
color: hifiStyleConstants.colors.faintGray50;
anchors {
top: addressBar.bottom;
bottom: parent.keyboardEnabled ? keyboard.top : parent.bottom;
left: parent.left;
right: parent.right;
}
ScrollView {
anchors.fill: bgMain;
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff;
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded;
Rectangle { // Column margins require QtQuick 2.7, which we don't use yet.
id: column;
property real pad: 10;
width: bgMain.width - column.pad;
height: stack.height;
color: "transparent";
anchors {
left: parent.left;
leftMargin: column.pad;
topMargin: column.pad;
}
Column {
id: stack;
width: column.width;
spacing: column.pad;
Feed {
id: happeningNow;
width: parent.width;
property real cardScale: 1.5;
cardWidth: places.cardWidth * happeningNow.cardScale;
cardHeight: places.cardHeight * happeningNow.cardScale;
metaverseServerUrl: addressBarDialog.metaverseServerUrl;
labelText: 'Happening Now';
//actions: 'concurrency,snapshot'; // uncomment this line instead of next to produce fake announcement data for testing.
actions: 'announcement';
filter: addressLine.text;
goFunction: goCard;
}
Feed {
id: places;
width: parent.width;
metaverseServerUrl: addressBarDialog.metaverseServerUrl;
labelText: 'Places';
actions: 'concurrency';
filter: addressLine.text;
goFunction: goCard;
}
Feed {
id: snapshots;
width: parent.width;
metaverseServerUrl: addressBarDialog.metaverseServerUrl;
labelText: 'Recent Activity';
actions: 'snapshot';
filter: addressLine.text;
goFunction: goCard;
}
}
}
model: suggestions
orientation: ListView.Vertical
delegate: Card {
width: cardWidth;
height: cardHeight;
goFunction: goCard;
userName: model.username;
placeName: model.place_name;
hifiUrl: model.place_name + model.path;
thumbnail: model.thumbnail_url;
imageUrl: model.image_url;
action: model.action;
timestamp: model.created_at;
onlineUsers: model.online_users;
storyId: model.metaverseId;
drillDownToPlace: model.drillDownToPlace;
shadowHeight: scroll.stackedCardShadowHeight;
hoverThunk: function () { scroll.currentIndex = index; }
unhoverThunk: function () { scroll.currentIndex = -1; }
}
highlightMoveDuration: -1;
highlightMoveVelocity: -1;
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.blueHighlight; z: 1; }
}
}
@ -347,13 +297,12 @@ StackView {
Timer {
// Delay clearing address line so as to avoid flicker of "not connected" being displayed after entering an address.
id: clearAddressLineTimer
running: false
interval: 100 // ms
repeat: false
id: clearAddressLineTimer;
running: false;
interval: 100; // ms
repeat: false;
onTriggered: {
addressLine.text = "";
isCursorVisible = false;
}
}
@ -415,127 +364,9 @@ StackView {
return true;
}
function resolveUrl(url) {
return (url.indexOf('/') === 0) ? (addressBarDialog.metaverseServerUrl + url) : url;
}
function makeModelData(data) { // create a new obj from data
// ListModel elements will only ever have those properties that are defined by the first obj that is added.
// So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story.
var name = data.place_name,
tags = data.tags || [data.action, data.username],
description = data.description || "",
thumbnail_url = data.thumbnail_url || "";
return {
place_name: name,
username: data.username || "",
path: data.path || "",
created_at: data.created_at || "",
action: data.action || "",
thumbnail_url: resolveUrl(thumbnail_url),
image_url: resolveUrl(data.details.image_url),
metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
tags: tags,
description: description,
online_users: data.details.concurrency || 0,
drillDownToPlace: false,
searchText: [name].concat(tags, description || []).join(' ').toUpperCase()
}
}
function suggestable(place) {
if (place.action === 'snapshot') {
return true;
}
return (place.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain.
}
property var selectedTab: allTab;
function tabSelect(textButton) {
selectedTab = textButton;
fillDestinations();
}
property var placeMap: ({});
function addToSuggestions(place) {
var collapse = allTab.selected && (place.action !== 'concurrency');
if (collapse) {
var existing = placeMap[place.place_name];
if (existing) {
existing.drillDownToPlace = true;
return;
}
}
suggestions.append(place);
if (collapse) {
placeMap[place.place_name] = suggestions.get(suggestions.count - 1);
} else if (place.action === 'concurrency') {
suggestions.get(suggestions.count - 1).drillDownToPlace = true; // Don't change raw place object (in allStories).
}
}
property int requestId: 0;
function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model
var options = [
'now=' + new Date().toISOString(),
'include_actions=' + selectedTab.includeActions,
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
'require_online=true',
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
'page=' + pageNumber
];
var url = metaverseBase + 'user_stories?' + options.join('&');
var thisRequestId = ++requestId;
getRequest(url, function (error, data) {
if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) {
return;
}
var stories = data.user_stories.map(function (story) { // explicit single-argument function
return makeModelData(story, url);
});
allStories = allStories.concat(stories);
stories.forEach(makeFilteredPlaceProcessor());
if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now
return getUserStoryPage(pageNumber + 1, cb);
}
cb();
});
}
function makeFilteredPlaceProcessor() { // answer a function(placeData) that adds it to suggestions if it matches
var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity),
data = allStories;
function matches(place) {
if (!words.length) {
return suggestable(place);
}
return words.every(function (word) {
return place.searchText.indexOf(word) >= 0;
});
}
return function (place) {
if (matches(place)) {
addToSuggestions(place);
}
};
}
function filterChoicesByText() {
suggestions.clear();
placeMap = {};
allStories.forEach(makeFilteredPlaceProcessor());
}
function fillDestinations() {
allStories = [];
suggestions.clear();
placeMap = {};
getUserStoryPage(1, function (error) {
console.log('user stories query', error || 'ok', allStories.length);
});
}
function updateLocationText(enteringAddress) {
if (enteringAddress) {
notice.text = "Go to a place, @user, path or network address";
notice.text = "Go To a place, @user, path, or network address:";
notice.color = hifiStyleConstants.colors.baseGrayHighlight;
} else {
notice.text = AddressManager.isConnected ? "Your location:" : "Not Connected";
@ -545,46 +376,14 @@ StackView {
}
}
onVisibleChanged: {
updateLocationText(false);
if (visible) {
addressLine.forceActiveFocus();
fillDestinations();
}
}
function toggleOrGo(fromSuggestions, address) {
if (address !== undefined && address !== "") {
addressBarDialog.loadAddress(address, fromSuggestions)
}
if (addressLine.text !== "") {
addressBarDialog.loadAddress(addressLine.text, fromSuggestions)
}
if (isDesktop) {
tablet.gotoHomeScreen();
} else {
HMD.closeTablet();
}
tabletRoot.shown = false;
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
tabletRoot.shown = false
clearAddressLineTimer.start();
event.accepted = true
break
case Qt.Key_Enter:
case Qt.Key_Return:
toggleOrGo()
clearAddressLineTimer.start();
event.accepted = true
break
addressBarDialog.loadAddress(address, fromSuggestions);
clearAddressLineTimer.start();
} else if (addressLine.text !== "") {
addressBarDialog.loadAddress(addressLine.text, fromSuggestions);
clearAddressLineTimer.start();
}
DialogsManager.hideAddressBar();
}
}

View file

@ -23,7 +23,7 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.resolvedUrl(path));
}
function popSource() {

View file

@ -23,7 +23,7 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.resolvedUrl(path));
}
function popSource() {

View file

@ -23,7 +23,7 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.resolvedUrl(path));
}
function popSource() {

View file

@ -23,7 +23,7 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.resolvedUrl(path));
}
function popSource() {

View file

@ -23,7 +23,7 @@ StackView {
signal sendToScript(var message);
function pushSource(path) {
profileRoot.push(Qt.reslovedUrl(path));
profileRoot.push(Qt.resolvedUrl(path));
}
function popSource() {

View file

@ -44,6 +44,12 @@ Item {
return openModal;
}
Component { id: assetDialogBuilder; TabletAssetDialog { } }
function assetDialog(properties) {
openModal = assetDialogBuilder.createObject(tabletRoot, properties);
return openModal;
}
function setMenuProperties(rootMenu, subMenu) {
tabletRoot.rootMenu = rootMenu;
tabletRoot.subMenu = subMenu;

View file

@ -1,46 +0,0 @@
//
// TabletAddressDialog.qml
//
// Created by Dante Ruiz on 2017/04/24
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import QtGraphicalEffects 1.0
import "../../controls"
import "../../styles"
import "../../windows"
import "../"
import "../toolbars"
import "../../styles-uit" as HifiStyles
import "../../controls-uit" as HifiControlsUit
import "../../controls" as HifiControls
Rectangle {
id: cardRoot
HifiStyles.HifiConstants { id: hifi }
width: parent.width
height: parent.height
property string address: ""
property alias eventBridge: webview.eventBridge
function setUrl(url) {
cardRoot.address = url;
webview.url = url;
}
HifiControls.TabletWebView {
id: webview
parentStackItem: root
anchors {
top: parent.top
right: parent.right
left: parent.left
bottom: parent.bottom
}
}
}

View file

@ -3,6 +3,8 @@ import QtQuick.Controls 1.4
StateImage {
id: button
property bool buttonEnabled: true
property bool isActive: false
property bool isEntered: false
@ -39,30 +41,37 @@ StateImage {
}
function updateState() {
if (!button.isEntered && !button.isActive) {
buttonState = imageOffOut;
} else if (!button.isEntered && button.isActive) {
buttonState = imageOnOut;
} else if (button.isEntered && !button.isActive) {
buttonState = imageOffIn;
if (buttonEnabled) {
if (!button.isEntered && !button.isActive) {
buttonState = imageOffOut;
} else if (!button.isEntered && button.isActive) {
buttonState = imageOnOut;
} else if (button.isEntered && !button.isActive) {
buttonState = imageOffIn;
} else {
buttonState = imageOnIn;
}
} else {
buttonState = imageOnIn;
buttonState = 0;
}
}
onIsActiveChanged: updateState();
onButtonEnabledChanged: updateState();
Timer {
id: asyncClickSender
interval: 10
repeat: false
running: false
onTriggered: button.clicked();
onTriggered: {
button.clicked();
}
}
MouseArea {
id: mouseArea
hoverEnabled: true
hoverEnabled: buttonEnabled
anchors.fill: parent
onClicked: asyncClickSender.start();
onEntered: {

Binary file not shown.

View file

@ -59,6 +59,7 @@
#include <AssetUpload.h>
#include <AutoUpdater.h>
#include <AudioInjectorManager.h>
#include <AvatarBookmarks.h>
#include <CursorManager.h>
#include <DebugDraw.h>
#include <DeferredLightingEffect.h>
@ -77,10 +78,12 @@
#include <InfoView.h>
#include <input-plugins/InputPlugin.h>
#include <controllers/UserInputMapper.h>
#include <controllers/InputRecorder.h>
#include <controllers/ScriptingInterface.h>
#include <controllers/StateController.h>
#include <UserActivityLoggerScriptingInterface.h>
#include <LogHandler.h>
#include "LocationBookmarks.h"
#include <MainWindow.h>
#include <MappingRequest.h>
#include <MessagesClient.h>
@ -138,7 +141,7 @@
#include "devices/Leapmotion.h"
#include "DiscoverabilityManager.h"
#include "GLCanvas.h"
#include "InterfaceActionFactory.h"
#include "InterfaceDynamicFactory.h"
#include "InterfaceLogging.h"
#include "LODManager.h"
#include "ModelPackager.h"
@ -460,7 +463,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, InterfaceDynamicFactory>();
DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>();
// Set dependencies
@ -514,7 +517,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<OffscreenUi>();
DependencyManager::set<AutoUpdater>();
DependencyManager::set<PathUtils>();
DependencyManager::set<InterfaceActionFactory>();
DependencyManager::set<InterfaceDynamicFactory>();
DependencyManager::set<AudioInjectorManager>();
DependencyManager::set<MessagesClient>();
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
@ -530,6 +533,8 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<EntityScriptServerLogClient>();
DependencyManager::set<LimitlessVoiceRecognitionScriptingInterface>();
DependencyManager::set<OctreeStatsProvider>(nullptr, qApp->getOcteeSceneStats());
DependencyManager::set<AvatarBookmarks>();
DependencyManager::set<LocationBookmarks>();
return previousSessionCrashed;
}
@ -590,7 +595,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_aboutToQuit(false),
_notifiedPacketVersionMismatchThisDomain(false),
_maxOctreePPS(maxOctreePacketsPerSecond.get()),
_lastFaceTrackerUpdate(0)
_lastFaceTrackerUpdate(0),
_snapshotSound(nullptr)
{
auto steamClient = PluginManager::getInstance()->getSteamClientPlugin();
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
@ -703,8 +709,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
usleep(USECS_PER_MSEC * 50); // 20hz
}
_bookmarks = new Bookmarks(); // Before setting up the menu
// start the nodeThread so its event loop is running
QThread* nodeThread = new QThread(this);
nodeThread->setObjectName("NodeList Thread");
@ -1122,19 +1126,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
return qApp->isHMDMode() ? 1 : 0;
});
_applicationStateDevice->setInputVariant(STATE_CAMERA_FULL_SCREEN_MIRROR, []() -> float {
return qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR ? 1 : 0;
return qApp->getCamera().getMode() == CAMERA_MODE_MIRROR ? 1 : 0;
});
_applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON, []() -> float {
return qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0;
return qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0;
});
_applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float {
return qApp->getCamera()->getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0;
return qApp->getCamera().getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0;
});
_applicationStateDevice->setInputVariant(STATE_CAMERA_ENTITY, []() -> float {
return qApp->getCamera()->getMode() == CAMERA_MODE_ENTITY ? 1 : 0;
return qApp->getCamera().getMode() == CAMERA_MODE_ENTITY ? 1 : 0;
});
_applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float {
return qApp->getCamera()->getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0;
return qApp->getCamera().getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0;
});
_applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float {
return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0;
@ -1420,11 +1424,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(DependencyManager::get<AudioClient>().data(), &AudioClient::mutedByMixer, this, onMutedByMixer);
// Track when the address bar is opened
auto onAddressBarToggled = [this]() {
auto onAddressBarShown = [this]() {
// Record time
UserActivityLogger::getInstance().logAction("opened_address_bar", { { "uptime_ms", _sessionRunTimer.elapsed() } });
};
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::addressBarToggled, this, onAddressBarToggled);
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::addressBarShown, this, onAddressBarShown);
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
@ -1434,6 +1438,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
entityPacketSender->setMyAvatar(myAvatar.get());
connect(this, &Application::applicationStateChanged, this, &Application::activeChanged);
connect(_window, SIGNAL(windowMinimizedChanged(bool)), this, SLOT(windowMinimizedChanged(bool)));
qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0);
auto textureCache = DependencyManager::get<TextureCache>();
@ -1441,8 +1446,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
QString skyboxUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.jpg" };
QString skyboxAmbientUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-ambient.jpg" };
_defaultSkyboxTexture = textureCache->getImageTexture(skyboxUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", false } });
_defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", true } });
_defaultSkyboxTexture = textureCache->getImageTexture(skyboxUrl, image::TextureUsage::CUBE_TEXTURE, { { "generateIrradiance", false } });
_defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, image::TextureUsage::CUBE_TEXTURE, { { "generateIrradiance", true } });
_defaultSkybox->setCubemap(_defaultSkyboxTexture);
@ -1451,6 +1456,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
return entityServerNode && !isPhysicsEnabled();
});
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
_snapshotSound = DependencyManager::get<SoundCache>()->getSound(QUrl::fromLocalFile(inf.absoluteFilePath()));
QVariant testProperty = property(hifi::properties::TEST);
qDebug() << testProperty;
if (testProperty.isValid()) {
@ -1458,46 +1466,53 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
const auto testScript = property(hifi::properties::TEST).toUrl();
scriptEngines->loadScript(testScript, false);
} else {
// Get sandbox content set version, if available
enum HandControllerType {
Vive,
Oculus
};
static const std::map<HandControllerType, int> MIN_CONTENT_VERSION = {
{ Vive, 1 },
{ Oculus, 27 }
};
// Get sandbox content set version
auto acDirPath = PathUtils::getAppDataPath() + "../../" + BuildInfo::MODIFIED_ORGANIZATION + "/assignment-client/";
auto contentVersionPath = acDirPath + "content-version.txt";
qCDebug(interfaceapp) << "Checking " << contentVersionPath << " for content version";
auto contentVersion = 0;
int contentVersion = 0;
QFile contentVersionFile(contentVersionPath);
if (contentVersionFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
QString line = contentVersionFile.readAll();
// toInt() returns 0 if the conversion fails, so we don't need to specifically check for failure
contentVersion = line.toInt();
contentVersion = line.toInt(); // returns 0 if conversion fails
}
qCDebug(interfaceapp) << "Server content version: " << contentVersion;
static const int MIN_VIVE_CONTENT_VERSION = 1;
static const int MIN_OCULUS_TOUCH_CONTENT_VERSION = 27;
bool hasSufficientTutorialContent = false;
// Get controller availability
bool hasHandControllers = false;
// Only specific hand controllers are currently supported, so only send users to the tutorial
// if they have one of those hand controllers.
HandControllerType handControllerType = Vive;
if (PluginUtils::isViveControllerAvailable()) {
hasHandControllers = true;
hasSufficientTutorialContent = contentVersion >= MIN_VIVE_CONTENT_VERSION;
handControllerType = Vive;
} else if (PluginUtils::isOculusTouchControllerAvailable()) {
hasHandControllers = true;
hasSufficientTutorialContent = contentVersion >= MIN_OCULUS_TOUCH_CONTENT_VERSION;
handControllerType = Oculus;
}
// Check tutorial content versioning
bool hasTutorialContent = contentVersion >= MIN_CONTENT_VERSION.at(handControllerType);
// Check HMD use (may be technically available without being in use)
bool hasHMD = PluginUtils::isHMDAvailable();
bool isUsingHMD = hasHMD && hasHandControllers && _displayPlugin->isHmd();
Setting::Handle<bool> tutorialComplete { "tutorialComplete", false };
Setting::Handle<bool> firstRun { Settings::firstRun, true };
bool hasHMDAndHandControllers = PluginUtils::isHMDAvailable() && hasHandControllers;
Setting::Handle<bool> tutorialComplete { "tutorialComplete", false };
bool isTutorialComplete = tutorialComplete.get();
bool shouldGoToTutorial = isUsingHMD && hasTutorialContent && !isTutorialComplete;
bool shouldGoToTutorial = hasHMDAndHandControllers && hasSufficientTutorialContent && !tutorialComplete.get();
qCDebug(interfaceapp) << "Has HMD + Hand Controllers: " << hasHMDAndHandControllers << ", current plugin: " << _displayPlugin->getName();
qCDebug(interfaceapp) << "Has sufficient tutorial content (" << contentVersion << ") : " << hasSufficientTutorialContent;
qCDebug(interfaceapp) << "Tutorial complete: " << tutorialComplete.get();
qCDebug(interfaceapp) << "Should go to tutorial: " << shouldGoToTutorial;
qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMD;
qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent <<
", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial;
// when --url in command line, teleport to location
const QString HIFI_URL_COMMAND_LINE_KEY = "--url";
@ -1534,7 +1549,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// If this is a first run we short-circuit the address passed in
if (isFirstRun) {
if (hasHMDAndHandControllers) {
if (isUsingHMD) {
if (sandboxIsRunning) {
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
DependencyManager::get<AddressManager>()->goToLocalSandbox();
@ -1773,24 +1788,38 @@ void Application::cleanupBeforeQuit() {
// stop QML
DependencyManager::destroy<OffscreenUi>();
if (_snapshotSoundInjector != nullptr) {
_snapshotSoundInjector->stop();
}
// stop audio after QML, as there are unexplained audio crashes originating in qtwebengine
// stop the AudioClient, synchronously
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(),
"stop", Qt::BlockingQueuedConnection);
// destroy Audio so it and its threads have a chance to go down safely
DependencyManager::destroy<AudioClient>();
DependencyManager::destroy<AudioInjectorManager>();
// shutdown render engine
_main3DScene = nullptr;
_renderEngine = nullptr;
qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete";
}
Application::~Application() {
// remove avatars from physics engine
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
VectorOfMotionStates motionStates;
DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
_physicsEngine->removeObjects(motionStates);
DependencyManager::get<AvatarManager>()->deleteAllAvatars();
_physicsEngine->setCharacterController(nullptr);
// shutdown render engine
_main3DScene = nullptr;
_renderEngine = nullptr;
DependencyManager::destroy<Preferences>();
_entityClipboard->eraseAllOctreeElements();
@ -1802,14 +1831,6 @@ Application::~Application() {
_octreeProcessor.terminate();
_entityEditSender.terminate();
_physicsEngine->setCharacterController(nullptr);
// remove avatars from physics engine
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
VectorOfMotionStates motionStates;
DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
_physicsEngine->removeObjects(motionStates);
DependencyManager::get<AvatarManager>()->deleteAllAvatars();
DependencyManager::destroy<AvatarManager>();
DependencyManager::destroy<AnimationCache>();
@ -2009,6 +2030,8 @@ void Application::initializeUi() {
rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
rootContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance());
rootContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
rootContext->setContextProperty("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
// Caches
rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
@ -2079,7 +2102,7 @@ void Application::initializeUi() {
void Application::paintGL() {
// Some plugins process message events, allowing paintGL to be called reentrantly.
if (_inPaint || _aboutToQuit) {
if (_inPaint || _aboutToQuit || _window->isMinimized()) {
return;
}
@ -2436,7 +2459,7 @@ void Application::resizeGL() {
// Possible change in aspect ratio
{
QMutexLocker viewLocker(&_viewMutex);
loadViewFrustum(_myCamera, _viewFrustum);
_myCamera.loadViewFrustum(_viewFrustum);
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
@ -2731,6 +2754,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
if (isMeta) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->load("Browser.qml");
} else if (isOption) {
controller::InputRecorder* inputRecorder = controller::InputRecorder::getInstance();
inputRecorder->stopPlayback();
}
break;
@ -4438,7 +4464,7 @@ void Application::update(float deltaTime) {
_entitySimulation->setObjectsToChange(stillNeedChange);
});
_entitySimulation->applyActionChanges();
_entitySimulation->applyDynamicChanges();
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
_physicsEngine->removeObjects(motionStates);
@ -4448,8 +4474,8 @@ void Application::update(float deltaTime) {
_physicsEngine->changeObjects(motionStates);
myAvatar->prepareForPhysicsSimulation();
_physicsEngine->forEachAction([&](EntityActionPointer action) {
action->prepareForPhysicsSimulation();
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
dynamic->prepareForPhysicsSimulation();
});
}
{
@ -4534,7 +4560,7 @@ void Application::update(float deltaTime) {
// to the server.
{
QMutexLocker viewLocker(&_viewMutex);
loadViewFrustum(_myCamera, _viewFrustum);
_myCamera.loadViewFrustum(_viewFrustum);
}
quint64 now = usecTimestampNow();
@ -4862,24 +4888,6 @@ QRect Application::getDesirableApplicationGeometry() const {
return applicationGeometry;
}
/////////////////////////////////////////////////////////////////////////////////////
// loadViewFrustum()
//
// Description: this will load the view frustum bounds for EITHER the head
// or the "myCamera".
//
void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
// We will use these below, from either the camera or head vectors calculated above
viewFrustum.setProjection(camera.getProjection());
// Set the viewFrustum up with the correct position and orientation of the camera
viewFrustum.setPosition(camera.getPosition());
viewFrustum.setOrientation(camera.getOrientation());
// Ask the ViewFrustum class to calculate our corners
viewFrustum.calculate();
}
glm::vec3 Application::getSunDirection() const {
// Sun direction is in fact just the location of the sun relative to the origin
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
@ -5051,7 +5059,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
// load the view frustum
{
QMutexLocker viewLocker(&_viewMutex);
loadViewFrustum(theCamera, _displayViewFrustum);
theCamera.loadViewFrustum(_displayViewFrustum);
}
// TODO fix shadows and make them use the GPU library
@ -5068,7 +5076,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
transaction.resetItem(BackgroundRenderData::_item, backgroundRenderPayload);
}
// Assuming nothing get's rendered through that
// Assuming nothing gets rendered through that
if (!selfAvatarOnly) {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
// render models...
@ -5124,6 +5132,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
QMutexLocker viewLocker(&_viewMutex);
renderArgs->setViewFrustum(_displayViewFrustum);
}
renderArgs->_cameraMode = (int8_t)theCamera.getMode(); // HACK
renderArgs->_scene = getMain3DScene();
_renderEngine->getRenderContext()->args = renderArgs;
// Before the deferred pass, let's try to use the render engine
@ -5219,11 +5229,7 @@ void Application::resettingDomain() {
}
void Application::nodeAdded(SharedNodePointer node) const {
if (node->getType() == NodeType::AvatarMixer) {
// new avatar mixer, send off our identity packet right away
getMyAvatar()->sendIdentityPacket();
getMyAvatar()->resetLastSent();
}
// nothing to do here
}
void Application::nodeActivated(SharedNodePointer node) {
@ -5259,6 +5265,13 @@ void Application::nodeActivated(SharedNodePointer node) {
if (node->getType() == NodeType::AudioMixer) {
DependencyManager::get<AudioClient>()->negotiateAudioFormat();
}
if (node->getType() == NodeType::AvatarMixer) {
// new avatar mixer, send off our identity packet right away
getMyAvatar()->markIdentityDataChanged();
getMyAvatar()->sendIdentityPacket();
getMyAvatar()->resetLastSent();
}
}
void Application::nodeKilled(SharedNodePointer node) {
@ -5507,6 +5520,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get<AudioScope>().data());
scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
scriptEngine->registerGlobalObject("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
// Caches
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
@ -6372,7 +6387,6 @@ void Application::loadLODToolsDialog() {
} else {
tablet->pushOntoStack("../../hifi/dialogs/TabletLODTools.qml");
}
}
@ -6422,16 +6436,30 @@ void Application::toggleEntityScriptServerLogDialog() {
}
}
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
QMediaPlayer* player = new QMediaPlayer();
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
player->play();
void Application::loadAddAvatarBookmarkDialog() const {
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
avatarBookmarks->addBookmark();
}
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
//keep sound thread out of event loop scope
AudioInjectorOptions options;
options.localOnly = true;
options.stereo = true;
if (_snapshotSoundInjector) {
_snapshotSoundInjector->setOptions(options);
_snapshotSoundInjector->restart();
} else {
QByteArray samples = _snapshotSound->getByteArray();
_snapshotSoundInjector = AudioInjector::playSound(samples, options);
}
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
// Get a screenshot and save it
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));
// 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.
@ -6442,6 +6470,7 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
}
});
}
void Application::shareSnapshot(const QString& path, const QUrl& href) {
postLambdaEvent([path, href] {
// not much to do here, everything is done in snapshot code...
@ -6504,6 +6533,14 @@ void Application::activeChanged(Qt::ApplicationState state) {
}
}
void Application::windowMinimizedChanged(bool minimized) {
if (!minimized && !getActiveDisplayPlugin()->isActive()) {
getActiveDisplayPlugin()->activate();
} else if (minimized && getActiveDisplayPlugin()->isActive()) {
getActiveDisplayPlugin()->deactivate();
}
}
void Application::postLambdaEvent(std::function<void()> f) {
if (this->thread() == QThread::currentThread()) {
f();
@ -6749,11 +6786,6 @@ void Application::updateDisplayMode() {
return;
}
UserActivityLogger::getInstance().logAction("changed_display_mode", {
{ "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" },
{ "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" }
});
auto offscreenUi = DependencyManager::get<OffscreenUi>();
// Make the switch atomic from the perspective of other threads
@ -6791,6 +6823,13 @@ void Application::updateDisplayMode() {
if (!active) {
qFatal("Failed to activate fallback plugin");
}
// We've changed the selection - it should be reflected in the menu
QAction* action = menu->getActionForOption(newDisplayPlugin->getName());
if (!action) {
qFatal("Failed to find activated plugin");
}
action->setChecked(true);
}
_offscreenContext->makeCurrent();
@ -6801,13 +6840,16 @@ void Application::updateDisplayMode() {
offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked);
}
bool isHmd = _displayPlugin->isHmd();
qCDebug(interfaceapp) << "Entering into" << (isHmd ? "HMD" : "Desktop") << "Mode";
// Only log/emit after a successful change
UserActivityLogger::getInstance().logAction("changed_display_mode", {
{ "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" },
{ "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" },
{ "hmd", isHmd }
});
emit activeDisplayPluginChanged();
if (_displayPlugin->isHmd()) {
qCDebug(interfaceapp) << "Entering into HMD Mode";
} else {
qCDebug(interfaceapp) << "Entering into Desktop Mode";
}
// reset the avatar, to set head and hand palms back to a reasonable default pose.
getMyAvatar()->reset(false);

View file

@ -52,8 +52,7 @@
#include "avatar/MyAvatar.h"
#include "BandwidthRecorder.h"
#include "Bookmarks.h"
#include "Camera.h"
#include "FancyCamera.h"
#include "ConnectionMonitor.h"
#include "gpu/Context.h"
#include "Menu.h"
@ -74,6 +73,7 @@
#include <model/Skybox.h>
#include <ModelScriptingInterface.h>
#include "Sound.h"
class OffscreenGLCanvas;
class GLCanvas;
@ -81,6 +81,7 @@ class FaceTracker;
class MainWindow;
class AssetUpload;
class CompositorHelper;
class AudioInjector;
namespace controller {
class StateController;
@ -175,8 +176,8 @@ public:
bool isThrottleRendering() const;
Camera* getCamera() { return &_myCamera; }
const Camera* getCamera() const { return &_myCamera; }
Camera& getCamera() { return _myCamera; }
const Camera& getCamera() const { return _myCamera; }
// Represents the current view frustum of the avatar.
void copyViewFrustum(ViewFrustum& viewOut) const;
// Represents the view frustum of the current rendering pass,
@ -261,7 +262,6 @@ public:
glm::mat4 getEyeProjection(int eye) const;
QRect getDesirableApplicationGeometry() const;
Bookmarks* getBookmarks() const { return _bookmarks; }
virtual bool canAcceptURL(const QString& url) const override;
virtual bool acceptURL(const QString& url, bool defaultUpload = false) override;
@ -270,7 +270,7 @@ public:
int getMaxOctreePacketsPerSecond() const;
render::ScenePointer getMain3DScene() override { return _main3DScene; }
render::ScenePointer getMain3DScene() const { return _main3DScene; }
const render::ScenePointer& getMain3DScene() const { return _main3DScene; }
render::EnginePointer getRenderEngine() override { return _renderEngine; }
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
@ -280,7 +280,7 @@ public:
float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f);
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
@ -330,6 +330,7 @@ public slots:
void toggleEntityScriptServerLogDialog();
void toggleRunningScriptsWidget() const;
Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
Q_INVOKABLE void loadAddAvatarBookmarkDialog() const;
void showDialog(const QString& desktopURL, const QString& tabletURL, const QString& name) const;
@ -416,6 +417,7 @@ private slots:
void faceTrackerMuteToggled();
void activeChanged(Qt::ApplicationState state);
void windowMinimizedChanged(bool minimized);
void notifyPacketVersionMismatch();
@ -463,7 +465,6 @@ private:
void updateDialogs(float deltaTime) const;
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions, bool forceResend = false);
static void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum);
glm::vec3 getSunDirection() const;
@ -559,7 +560,7 @@ private:
SimpleMovingAverage _avatarSimsPerSecond {10};
int _avatarSimsPerSecondReport {0};
quint64 _lastAvatarSimsPerSecondUpdate {0};
Camera _myCamera; // My view onto the world
FancyCamera _myCamera; // My view onto the world
Setting::Handle<QString> _previousScriptLocation;
Setting::Handle<float> _fieldOfView;
@ -599,8 +600,6 @@ private:
bool _aboutToQuit;
Bookmarks* _bookmarks;
bool _notifiedPacketVersionMismatchThisDomain;
QThread _settingsThread;
@ -686,6 +685,8 @@ private:
QTimer _addAssetToWorldErrorTimer;
FileScriptingInterface* _fileDownload;
AudioInjector* _snapshotSoundInjector { nullptr };
SharedSoundPointer _snapshotSound;
};

View file

@ -0,0 +1,81 @@
//
// AvatarBookmarks.cpp
// interface/src
//
// Created by Triplelexx on 23/03/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 <QAction>
#include <QInputDialog>
#include <QMessageBox>
#include <QStandardPaths>
#include <QQmlContext>
#include <Application.h>
#include <OffscreenUi.h>
#include <avatar/AvatarManager.h>
#include "MainWindow.h"
#include "Menu.h"
#include "AvatarBookmarks.h"
#include <QtQuick/QQuickWindow>
AvatarBookmarks::AvatarBookmarks() {
_bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME;
readFromFile();
}
void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
// Add menus/actions
auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkAvatar);
QObject::connect(bookmarkAction, SIGNAL(triggered()), this, SLOT(addBookmark()), Qt::QueuedConnection);
_bookmarksMenu = menu->addMenu(MenuOption::AvatarBookmarks);
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteAvatarBookmark);
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
Bookmarks::setupMenus(menubar, menu);
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
void AvatarBookmarks::changeToBookmarkedAvatar() {
QAction* action = qobject_cast<QAction*>(sender());
const QString& address = action->data().toString();
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
myAvatar->useFullAvatarURL(address);
}
void AvatarBookmarks::addBookmark() {
bool ok = false;
auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString(), &ok);
if (!ok) {
return;
}
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
if (bookmarkName.length() == 0) {
return;
}
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const QString& bookmarkAddress = myAvatar->getSkeletonModelURL().toString();
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
}
void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) {
QAction* changeAction = _bookmarksMenu->newAction();
changeAction->setData(address);
connect(changeAction, SIGNAL(triggered()), this, SLOT(changeToBookmarkedAvatar()));
if (!_isMenuSorted) {
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);
} else {
// TODO: this is aggressive but other alternatives have proved less fruitful so far.
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole);
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
}

View file

@ -0,0 +1,40 @@
//
// AvatarBookmarks.h
// interface/src
//
// Created by Triplelexx on 23/03/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_AvatarBookmarks_h
#define hifi_AvatarBookmarks_h
#include <DependencyManager.h>
#include "Bookmarks.h"
class AvatarBookmarks: public Bookmarks, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
AvatarBookmarks();
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
public slots:
void addBookmark();
protected:
void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;
private:
const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json";
private slots:
void changeToBookmarkedAvatar();
};
#endif // hifi_AvatarBookmarks_h

View file

@ -11,14 +11,10 @@
#include <QAction>
#include <QDebug>
#include <QJsonObject>
#include <QFile>
#include <QInputDialog>
#include <QJsonDocument>
#include <QMessageBox>
#include <QStandardPaths>
#include <AddressManager.h>
#include <Application.h>
#include <OffscreenUi.h>
@ -27,15 +23,69 @@
#include "InterfaceLogging.h"
#include "Bookmarks.h"
#include <QtQuick/QQuickWindow>
Bookmarks::Bookmarks() :
_isMenuSorted(false)
{
}
const QString Bookmarks::HOME_BOOKMARK = "Home";
void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
// Enable/Disable menus as needed
enableMenuItems(_bookmarks.count() > 0);
// Load Bookmarks
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) {
QString bookmarkName = it.key();
QString bookmarkAddress = it.value().toString();
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
}
}
Bookmarks::Bookmarks() {
_bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + BOOKMARKS_FILENAME;
readFromFile();
void Bookmarks::deleteBookmark() {
QStringList bookmarkList;
QList<QAction*> menuItems = _bookmarksMenu->actions();
for (int i = 0; i < menuItems.count(); ++i) {
bookmarkList.append(menuItems[i]->text());
}
bool ok = false;
auto bookmarkName = OffscreenUi::getItem(OffscreenUi::ICON_PLACEMARK, "Delete Bookmark", "Select the bookmark to delete", bookmarkList, 0, false, &ok);
if (!ok) {
return;
}
bookmarkName = bookmarkName.trimmed();
if (bookmarkName.length() == 0) {
return;
}
removeBookmarkFromMenu(Menu::getInstance(), bookmarkName);
remove(bookmarkName);
if (_bookmarksMenu->actions().count() == 0) {
enableMenuItems(false);
}
}
void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress) {
Menu* menubar = Menu::getInstance();
if (contains(bookmarkName)) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto duplicateBookmarkMessage = offscreenUi->createMessageBox(OffscreenUi::ICON_WARNING, "Duplicate Bookmark",
"The bookmark name you entered already exists in your list.",
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?");
auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage);
if (result != QMessageBox::Yes) {
return;
}
removeBookmarkFromMenu(menubar, bookmarkName);
}
addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress);
insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName.
enableMenuItems(true);
}
void Bookmarks::insert(const QString& name, const QString& address) {
@ -64,6 +114,33 @@ bool Bookmarks::contains(const QString& name) const {
return _bookmarks.contains(name);
}
bool Bookmarks::sortOrder(QAction* a, QAction* b) {
return a->text().toLower().localeAwareCompare(b->text().toLower()) < 0;
}
void Bookmarks::sortActions(Menu* menubar, MenuWrapper* menu) {
QList<QAction*> actions = menu->actions();
qSort(actions.begin(), actions.end(), sortOrder);
for (QAction* action : menu->actions()) {
menu->removeAction(action);
}
for (QAction* action : actions) {
menu->addAction(action);
}
_isMenuSorted = true;
}
int Bookmarks::getMenuItemLocation(QList<QAction*> actions, const QString& name) const {
int menuItemLocation = 0;
for (QAction* action : actions) {
if (name.toLower().localeAwareCompare(action->text().toLower()) < 0) {
menuItemLocation = actions.indexOf(action);
break;
}
}
return menuItemLocation;
}
QString Bookmarks::addressForBookmark(const QString& name) const {
return _bookmarks.value(name).toString();
}
@ -99,108 +176,6 @@ void Bookmarks::persistToFile() {
saveFile.write(data);
}
void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
// Add menus/actions
auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation);
QObject::connect(bookmarkAction, SIGNAL(triggered()), this, SLOT(bookmarkLocation()), Qt::QueuedConnection);
auto setHomeAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::SetHomeLocation);
QObject::connect(setHomeAction, SIGNAL(triggered()), this, SLOT(setHomeLocation()), Qt::QueuedConnection);
_bookmarksMenu = menu->addMenu(MenuOption::Bookmarks);
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark);
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
// Enable/Disable menus as needed
enableMenuItems(_bookmarks.count() > 0);
// Load bookmarks
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it ) {
QString bookmarkName = it.key();
QString bookmarkAddress = it.value().toString();
addLocationToMenu(menubar, bookmarkName, bookmarkAddress);
}
}
void Bookmarks::bookmarkLocation() {
bool ok = false;
auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString(), &ok);
if (!ok) {
return;
}
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
if (bookmarkName.length() == 0) {
return;
}
auto addressManager = DependencyManager::get<AddressManager>();
QString bookmarkAddress = addressManager->currentAddress().toString();
Menu* menubar = Menu::getInstance();
if (contains(bookmarkName)) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto duplicateBookmarkMessage = offscreenUi->createMessageBox(OffscreenUi::ICON_WARNING, "Duplicate Bookmark",
"The bookmark name you entered already exists in your list.",
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?");
auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage);
if (result != QMessageBox::Yes) {
return;
}
removeLocationFromMenu(menubar, bookmarkName);
}
addLocationToMenu(menubar, bookmarkName, bookmarkAddress);
insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName.
enableMenuItems(true);
}
void Bookmarks::setHomeLocation() {
Menu* menubar = Menu::getInstance();
QString bookmarkName = HOME_BOOKMARK;
auto addressManager = DependencyManager::get<AddressManager>();
QString bookmarkAddress = addressManager->currentAddress().toString();
addLocationToMenu(menubar, bookmarkName, bookmarkAddress);
insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName.
enableMenuItems(true);
}
void Bookmarks::teleportToBookmark() {
QAction* action = qobject_cast<QAction*>(sender());
QString address = action->data().toString();
DependencyManager::get<AddressManager>()->handleLookupString(address);
}
void Bookmarks::deleteBookmark() {
QStringList bookmarkList;
QList<QAction*> menuItems = _bookmarksMenu->actions();
for (int i = 0; i < menuItems.count(); i += 1) {
bookmarkList.append(menuItems[i]->text());
}
bool ok = false;
auto bookmarkName = OffscreenUi::getItem(OffscreenUi::ICON_PLACEMARK, "Delete Bookmark", "Select the bookmark to delete", bookmarkList, 0, false, &ok);
if (!ok) {
return;
}
bookmarkName = bookmarkName.trimmed();
if (bookmarkName.length() == 0) {
return;
}
removeLocationFromMenu(Menu::getInstance(), bookmarkName);
remove(bookmarkName);
if (_bookmarksMenu->actions().count() == 0) {
enableMenuItems(false);
}
}
void Bookmarks::enableMenuItems(bool enabled) {
if (_bookmarksMenu) {
_bookmarksMenu->setEnabled(enabled);
@ -210,17 +185,6 @@ void Bookmarks::enableMenuItems(bool enabled) {
}
}
void Bookmarks::addLocationToMenu(Menu* menubar, QString& name, QString& address) {
QAction* teleportAction = _bookmarksMenu->newAction();
teleportAction->setData(address);
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction,
name, 0, QAction::NoRole);
}
void Bookmarks::removeLocationFromMenu(Menu* menubar, QString& name) {
void Bookmarks::removeBookmarkFromMenu(Menu* menubar, const QString& name) {
menubar->removeAction(_bookmarksMenu, name);
}

View file

@ -27,37 +27,35 @@ class Bookmarks: public QObject {
public:
Bookmarks();
void setupMenus(Menu* menubar, MenuWrapper* menu);
virtual void setupMenus(Menu* menubar, MenuWrapper* menu);
QString addressForBookmark(const QString& name) const;
static const QString HOME_BOOKMARK;
protected:
virtual void addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress);
virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) = 0;
void enableMenuItems(bool enabled);
void readFromFile();
void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name.
void sortActions(Menu* menubar, MenuWrapper* menu);
int getMenuItemLocation(QList<QAction*> actions, const QString& name) const;
private slots:
void bookmarkLocation();
void setHomeLocation();
void teleportToBookmark();
void deleteBookmark();
private:
QVariantMap _bookmarks; // { name: address, ... }
QVariantMap _bookmarks; // { name: url, ... }
QPointer<MenuWrapper> _bookmarksMenu;
QPointer<QAction> _deleteBookmarksAction;
const QString BOOKMARKS_FILENAME = "bookmarks.json";
QString _bookmarksFilename;
void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name.
bool _isMenuSorted;
protected slots:
void deleteBookmark();
private:
void remove(const QString& name);
bool contains(const QString& name) const;
static bool sortOrder(QAction* a, QAction* b);
void readFromFile();
void persistToFile();
void enableMenuItems(bool enabled);
void addLocationToMenu(Menu* menubar, QString& name, QString& address);
void removeLocationFromMenu(Menu* menubar, QString& name);
void removeBookmarkFromMenu(Menu* menubar, const QString& name);
};
#endif // hifi_Bookmarks_h
#endif // hifi_Bookmarks_h

View file

@ -98,18 +98,7 @@ void Camera::setMode(CameraMode mode) {
emit modeUpdated(modeToString(mode));
}
QUuid Camera::getCameraEntity() const {
if (_cameraEntity != nullptr) {
return _cameraEntity->getID();
}
return QUuid();
};
void Camera::setCameraEntity(QUuid entityID) {
_cameraEntity = qApp->getEntities()->getTree()->findEntityByID(entityID);
}
void Camera::setProjection(const glm::mat4& projection) {
void Camera::setProjection(const glm::mat4& projection) {
_projection = projection;
}
@ -119,7 +108,7 @@ PickRay Camera::computePickRay(float x, float y) {
void Camera::setModeString(const QString& mode) {
CameraMode targetMode = stringToMode(mode);
switch (targetMode) {
case CAMERA_MODE_FIRST_PERSON:
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
@ -139,7 +128,7 @@ void Camera::setModeString(const QString& mode) {
default:
break;
}
qApp->cameraMenuChanged();
if (_mode != targetMode) {
@ -177,12 +166,6 @@ void Camera::loadViewFrustum(ViewFrustum& frustum) const {
frustum.calculate();
}
ViewFrustum Camera::toViewFrustum() const {
ViewFrustum result;
loadViewFrustum(result);
return result;
}
QVariantMap Camera::getViewFrustum() {
ViewFrustum frustum;
loadViewFrustum(frustum);

View file

@ -43,15 +43,11 @@ class Camera : public QObject {
* @property position {Vec3} The position of the camera.
* @property orientation {Quat} The orientation of the camera.
* @property mode {string} The current camera mode.
* @property cameraEntity {EntityID} The position and rotation properties of
* the entity specified by this ID are then used as the camera's position and
* orientation. Only works when <code>mode</code> is "entity".
* @property frustum {Object} The frustum of the camera.
*/
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
Q_PROPERTY(QString mode READ getModeString WRITE setModeString)
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
Q_PROPERTY(QVariantMap frustum READ getViewFrustum CONSTANT)
public:
@ -65,9 +61,6 @@ public:
void setMode(CameraMode m);
void loadViewFrustum(ViewFrustum& frustum) const;
ViewFrustum toViewFrustum() const;
EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; }
const glm::mat4& getTransform() const { return _transform; }
void setTransform(const glm::mat4& transform);
@ -87,9 +80,6 @@ public slots:
glm::quat getOrientation() const { return _orientation; }
void setOrientation(const glm::quat& orientation);
QUuid getCameraEntity() const;
void setCameraEntity(QUuid entityID);
/**jsdoc
* Compute a {PickRay} based on the current camera configuration and the position x,y on the screen.
* @function Camera.computePickRay
@ -143,7 +133,6 @@ private:
glm::quat _orientation;
bool _isKeepLookingAt{ false };
glm::vec3 _lookingAt;
EntityItemPointer _cameraEntity;
};
#endif // hifi_Camera_h

View file

@ -23,6 +23,8 @@
#include "DiscoverabilityManager.h"
#include "Menu.h"
#include <QThread>
const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::Friends;
DiscoverabilityManager::DiscoverabilityManager() :
@ -37,6 +39,13 @@ const QString API_USER_HEARTBEAT_PATH = "/api/v1/user/heartbeat";
const QString SESSION_ID_KEY = "session_id";
void DiscoverabilityManager::updateLocation() {
// since we store the last location and compare it to
// the current one in this function, we need to do this in
// the object's main thread (or use a mutex)
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "updateLocation");
return;
}
auto accountManager = DependencyManager::get<AccountManager>();
auto addressManager = DependencyManager::get<AddressManager>();
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
@ -143,7 +152,7 @@ void DiscoverabilityManager::removeLocation() {
void DiscoverabilityManager::setDiscoverabilityMode(Discoverability::Mode discoverabilityMode) {
if (static_cast<Discoverability::Mode>(_mode.get()) != discoverabilityMode) {
// update the setting to the new value
_mode.set(static_cast<int>(discoverabilityMode));
updateLocation(); // update right away

View file

@ -0,0 +1,25 @@
//
// FancyCamera.cpp
// interface/src
//
// 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 "FancyCamera.h"
#include "Application.h"
QUuid FancyCamera::getCameraEntity() const {
if (_cameraEntity != nullptr) {
return _cameraEntity->getID();
}
return QUuid();
};
void FancyCamera::setCameraEntity(QUuid entityID) {
_cameraEntity = qApp->getEntities()->getTree()->findEntityByID(entityID);
}

View file

@ -0,0 +1,43 @@
//
// FancyCamera.h
// interface/src
//
// 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_FancyCamera_h
#define hifi_FancyCamera_h
#include "Camera.h"
#include <EntityItem.h>
// TODO: come up with a better name than "FancyCamera"
class FancyCamera : public Camera {
Q_OBJECT
/**jsdoc
* @namespace Camera
* @property cameraEntity {EntityID} The position and rotation properties of
* the entity specified by this ID are then used as the camera's position and
* orientation. Only works when <code>mode</code> is "entity".
*/
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
public:
FancyCamera() : Camera() {}
EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; }
public slots:
QUuid getCameraEntity() const;
void setCameraEntity(QUuid entityID);
private:
EntityItemPointer _cameraEntity;
};
#endif // hifi_FancyCamera_h

View file

@ -1,82 +0,0 @@
//
// InterfaceActionFactory.cpp
// libraries/entities/src
//
// Created by Seth Alves on 2015-6-2
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <avatar/AvatarActionHold.h>
#include <ObjectActionOffset.h>
#include <ObjectActionSpring.h>
#include <ObjectActionTravelOriented.h>
#include <LogHandler.h>
#include "InterfaceActionFactory.h"
EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity) {
switch (type) {
case ACTION_TYPE_NONE:
return EntityActionPointer();
case ACTION_TYPE_OFFSET:
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
case ACTION_TYPE_SPRING:
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
case ACTION_TYPE_HOLD:
return std::make_shared<AvatarActionHold>(id, ownerEntity);
case ACTION_TYPE_TRAVEL_ORIENTED:
return std::make_shared<ObjectActionTravelOriented>(id, ownerEntity);
}
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity action type");
return EntityActionPointer();
}
EntityActionPointer InterfaceActionFactory::factory(EntityActionType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) {
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
if (action) {
bool ok = action->updateArguments(arguments);
if (ok) {
if (action->lifetimeIsOver()) {
return nullptr;
}
return action;
}
}
return nullptr;
}
EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
QDataStream serializedArgumentStream(data);
EntityActionType type;
QUuid id;
serializedArgumentStream >> type;
serializedArgumentStream >> id;
EntityActionPointer action = interfaceActionFactory(type, id, ownerEntity);
if (action) {
action->deserialize(data);
if (action->lifetimeIsOver()) {
static QString repeatedMessage =
LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during action creation.*");
qDebug() << "InterfaceActionFactory::factoryBA lifetimeIsOver during action creation --"
<< action->getExpires() << "<" << usecTimestampNow();
return nullptr;
}
}
return action;
}

View file

@ -0,0 +1,88 @@
//
// InterfaceDynamicFactory.cpp
// libraries/entities/src
//
// Created by Seth Alves on 2015-6-2
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <avatar/AvatarActionHold.h>
#include <avatar/AvatarActionFarGrab.h>
#include <ObjectActionOffset.h>
#include <ObjectActionSpring.h>
#include <ObjectActionTravelOriented.h>
#include <ObjectConstraintHinge.h>
#include <LogHandler.h>
#include "InterfaceDynamicFactory.h"
EntityDynamicPointer interfaceDynamicFactory(EntityDynamicType type, const QUuid& id, EntityItemPointer ownerEntity) {
switch (type) {
case DYNAMIC_TYPE_NONE:
return EntityDynamicPointer();
case DYNAMIC_TYPE_OFFSET:
return std::make_shared<ObjectActionOffset>(id, ownerEntity);
case DYNAMIC_TYPE_SPRING:
return std::make_shared<ObjectActionSpring>(id, ownerEntity);
case DYNAMIC_TYPE_HOLD:
return std::make_shared<AvatarActionHold>(id, ownerEntity);
case DYNAMIC_TYPE_TRAVEL_ORIENTED:
return std::make_shared<ObjectActionTravelOriented>(id, ownerEntity);
case DYNAMIC_TYPE_HINGE:
return std::make_shared<ObjectConstraintHinge>(id, ownerEntity);
case DYNAMIC_TYPE_FAR_GRAB:
return std::make_shared<AvatarActionFarGrab>(id, ownerEntity);
}
Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown entity dynamic type");
return EntityDynamicPointer();
}
EntityDynamicPointer InterfaceDynamicFactory::factory(EntityDynamicType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) {
EntityDynamicPointer dynamic = interfaceDynamicFactory(type, id, ownerEntity);
if (dynamic) {
bool ok = dynamic->updateArguments(arguments);
if (ok) {
if (dynamic->lifetimeIsOver()) {
return nullptr;
}
return dynamic;
}
}
return nullptr;
}
EntityDynamicPointer InterfaceDynamicFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
QDataStream serializedArgumentStream(data);
EntityDynamicType type;
QUuid id;
serializedArgumentStream >> type;
serializedArgumentStream >> id;
EntityDynamicPointer dynamic = interfaceDynamicFactory(type, id, ownerEntity);
if (dynamic) {
dynamic->deserialize(data);
if (dynamic->lifetimeIsOver()) {
static QString repeatedMessage =
LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during dynamic creation.*");
qDebug() << "InterfaceDynamicFactory::factoryBA lifetimeIsOver during dynamic creation --"
<< dynamic->getExpires() << "<" << usecTimestampNow();
return nullptr;
}
}
return dynamic;
}

View file

@ -1,5 +1,5 @@
//
// InterfaceActionFactory.cpp
// InterfaceDynamicFactory.cpp
// interface/src/
//
// Created by Seth Alves on 2015-6-10
@ -9,21 +9,21 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_InterfaceActionFactory_h
#define hifi_InterfaceActionFactory_h
#ifndef hifi_InterfaceDynamicFactory_h
#define hifi_InterfaceDynamicFactory_h
#include "EntityActionFactoryInterface.h"
#include "EntityDynamicFactoryInterface.h"
class InterfaceActionFactory : public EntityActionFactoryInterface {
class InterfaceDynamicFactory : public EntityDynamicFactoryInterface {
public:
InterfaceActionFactory() : EntityActionFactoryInterface() { }
virtual ~InterfaceActionFactory() { }
virtual EntityActionPointer factory(EntityActionType type,
InterfaceDynamicFactory() : EntityDynamicFactoryInterface() { }
virtual ~InterfaceDynamicFactory() { }
virtual EntityDynamicPointer factory(EntityDynamicType type,
const QUuid& id,
EntityItemPointer ownerEntity,
QVariantMap arguments) override;
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
virtual EntityDynamicPointer factoryBA(EntityItemPointer ownerEntity,
QByteArray data) override;
};
#endif // hifi_InterfaceActionFactory_h
#endif // hifi_InterfaceDynamicFactory_h

View file

@ -0,0 +1,88 @@
//
// LocationBookmarks.cpp
// interface/src
//
// Created by Triplelexx on 23/03/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 <QAction>
#include <QInputDialog>
#include <QMessageBox>
#include <QStandardPaths>
#include <AddressManager.h>
#include <Application.h>
#include <OffscreenUi.h>
#include "MainWindow.h"
#include "Menu.h"
#include "LocationBookmarks.h"
#include <QtQuick/QQuickWindow>
const QString LocationBookmarks::HOME_BOOKMARK = "Home";
LocationBookmarks::LocationBookmarks() {
_bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + LOCATIONBOOKMARKS_FILENAME;
readFromFile();
}
void LocationBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
// Add menus/actions
auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation);
QObject::connect(bookmarkAction, SIGNAL(triggered()), this, SLOT(addBookmark()), Qt::QueuedConnection);
auto setHomeAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::SetHomeLocation);
QObject::connect(setHomeAction, SIGNAL(triggered()), this, SLOT(setHomeLocation()), Qt::QueuedConnection);
_bookmarksMenu = menu->addMenu(MenuOption::LocationBookmarks);
_deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark);
QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection);
Bookmarks::setupMenus(menubar, menu);
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
void LocationBookmarks::setHomeLocation() {
auto addressManager = DependencyManager::get<AddressManager>();
QString bookmarkAddress = addressManager->currentAddress().toString();
Bookmarks::addBookmarkToFile(HOME_BOOKMARK, bookmarkAddress);
}
void LocationBookmarks::teleportToBookmark() {
QAction* action = qobject_cast<QAction*>(sender());
QString address = action->data().toString();
DependencyManager::get<AddressManager>()->handleLookupString(address);
}
void LocationBookmarks::addBookmark() {
bool ok = false;
auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString(), &ok);
if (!ok) {
return;
}
bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " ");
if (bookmarkName.length() == 0) {
return;
}
auto addressManager = DependencyManager::get<AddressManager>();
QString bookmarkAddress = addressManager->currentAddress().toString();
Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress);
}
void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) {
QAction* teleportAction = _bookmarksMenu->newAction();
teleportAction->setData(address);
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
if (!_isMenuSorted) {
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole);
} else {
// TODO: this is aggressive but other alternatives have proved less fruitful so far.
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole);
Bookmarks::sortActions(menubar, _bookmarksMenu);
}
}

View file

@ -0,0 +1,42 @@
//
// LocationBookmarks.h
// interface/src
//
// Created by Triplelexx on 23/03/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_LocationBookmarks_h
#define hifi_LocationBookmarks_h
#include <DependencyManager.h>
#include "Bookmarks.h"
class LocationBookmarks : public Bookmarks, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
LocationBookmarks();
void setupMenus(Menu* menubar, MenuWrapper* menu) override;
static const QString HOME_BOOKMARK;
public slots:
void addBookmark();
protected:
void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override;
private:
const QString LOCATIONBOOKMARKS_FILENAME = "bookmarks.json";
private slots:
void setHomeLocation();
void teleportToBookmark();
};
#endif // hifi_LocationBookmarks_h

View file

@ -32,6 +32,7 @@
#include "assets/ATPAssetMigrator.h"
#include "audio/AudioScope.h"
#include "avatar/AvatarManager.h"
#include "AvatarBookmarks.h"
#include "devices/DdeFaceTracker.h"
#include "devices/Faceshift.h"
#include "MainWindow.h"
@ -40,6 +41,7 @@
#include "ui/DialogsManager.h"
#include "ui/StandAloneJSConsole.h"
#include "InterfaceLogging.h"
#include "LocationBookmarks.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h"
@ -154,6 +156,8 @@ Menu::Menu() {
// Audio > Show Level Meter
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioTools, 0, false);
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioNoiseReduction, 0, true,
audioIO.data(), SLOT(toggleAudioNoiseReduction()));
// Avatar menu ----------------------------------
MenuWrapper* avatarMenu = addMenu("Avatar");
@ -194,6 +198,12 @@ Menu::Menu() {
0, // QML Qt::Key_Apostrophe,
qApp, SLOT(resetSensors()));
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableCharacterController, 0, true,
avatar.get(), SLOT(updateMotionBehaviorFromMenu()));
// Avatar > AvatarBookmarks related menus -- Note: the AvatarBookmarks class adds its own submenus here.
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
avatarBookmarks->setupMenus(this, avatarMenu);
// Display menu ----------------------------------
// FIXME - this is not yet matching Alan's spec because it doesn't have
@ -254,10 +264,11 @@ Menu::Menu() {
// Navigate > Show Address Bar
addActionToQMenuAndActionHash(navigateMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L,
dialogsManager.data(), SLOT(toggleAddressBar()));
dialogsManager.data(), SLOT(showAddressBar()));
// Navigate > Bookmark related menus -- Note: the Bookmark class adds its own submenus here.
qApp->getBookmarks()->setupMenus(this, navigateMenu);
// Navigate > LocationBookmarks related menus -- Note: the LocationBookmarks class adds its own submenus here.
auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
locationBookmarks->setupMenus(this, navigateMenu);
// Navigate > Copy Address [advanced]
auto addressManager = DependencyManager::get<AddressManager>();
@ -526,10 +537,6 @@ Menu::Menu() {
avatar.get(), SLOT(updateMotionBehaviorFromMenu()),
UNSPECIFIED_POSITION, "Developer");
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableCharacterController, 0, true,
avatar.get(), SLOT(updateMotionBehaviorFromMenu()),
UNSPECIFIED_POSITION, "Developer");
// Developer > Hands >>>
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false,
@ -616,8 +623,6 @@ Menu::Menu() {
QString("../../hifi/tablet/TabletAudioPreferences.qml"), "AudioPreferencesDialog");
});
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true,
audioIO.data(), SLOT(toggleAudioNoiseReduction()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false,
audioIO.data(), SLOT(toggleServerEcho()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false,

View file

@ -36,7 +36,7 @@ namespace MenuOption {
const QString AssetMigration = "ATP Asset Migration";
const QString AssetServer = "Asset Browser";
const QString Attachments = "Attachments...";
const QString AudioNoiseReduction = "Audio Noise Reduction";
const QString AudioNoiseReduction = "Noise Reduction";
const QString AudioScope = "Show Scope";
const QString AudioScopeFiftyFrames = "Fifty";
const QString AudioScopeFiveFrames = "Five";
@ -47,10 +47,11 @@ namespace MenuOption {
const QString AudioTools = "Show Level Meter";
const QString AutoMuteAudio = "Auto Mute Microphone";
const QString AvatarReceiveStats = "Show Receive Stats";
const QString AvatarBookmarks = "Avatar Bookmarks";
const QString Back = "Back";
const QString BinaryEyelidControl = "Binary Eyelid Control";
const QString BookmarkAvatar = "Bookmark Avatar";
const QString BookmarkLocation = "Bookmark Location";
const QString Bookmarks = "Bookmarks";
const QString CalibrateCamera = "Calibrate Camera";
const QString CameraEntityMode = "Entity Mode";
const QString CenterPlayerInView = "Center Player In View";
@ -78,6 +79,7 @@ namespace MenuOption {
const QString DeadlockInterface = "Deadlock Interface";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
const QString DefaultSkybox = "Default Skybox";
const QString DeleteAvatarBookmark = "Delete Avatar Bookmark...";
const QString DeleteBookmark = "Delete Bookmark...";
const QString DisableActivityLogger = "Disable Activity Logger";
const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment";
@ -89,11 +91,12 @@ namespace MenuOption {
const QString DisplayModelElementChildProxies = "Display Model Element Children";
const QString DisplayModelElementProxy = "Display Model Element Bounds";
const QString DisplayDebugTimingDetails = "Display Timing Details";
const QString LocationBookmarks = "Bookmarks";
const QString DontDoPrecisionPicking = "Don't Do Precision Picking";
const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene";
const QString EchoLocalAudio = "Echo Local Audio";
const QString EchoServerAudio = "Echo Server Audio";
const QString EnableCharacterController = "Enable avatar collisions";
const QString EnableCharacterController = "Collide with world";
const QString EnableInverseKinematics = "Enable Inverse Kinematics";
const QString EntityScriptServerLog = "Entity Script Server Log";
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";

View file

@ -1,44 +0,0 @@
//
// Physics.cpp
// interface/src
//
// Copyright 2013 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 <glm/glm.hpp>
#include <SharedUtil.h>
#include "Util.h"
#include "world.h"
#include "Physics.h"
//
// Applies static friction: maxVelocity is the largest velocity for which there
// there is friction, and strength is the amount of friction force applied to reduce
// velocity.
//
void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength) {
float v = glm::length(velocity);
if (v < maxVelocity) {
velocity *= glm::clamp((1.0f - deltaTime * strength * (1.0f - v / maxVelocity)), 0.0f, 1.0f);
}
}
//
// Applies velocity damping, with a strength value for linear and squared velocity damping
//
void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength) {
if (squaredStrength == 0.0f) {
velocity *= glm::clamp(1.0f - deltaTime * linearStrength, 0.0f, 1.0f);
} else {
velocity *= glm::clamp(1.0f - deltaTime * (linearStrength + glm::length(velocity) * squaredStrength), 0.0f, 1.0f);
}
}
void applyDampedSpring(float deltaTime, glm::vec3& velocity, glm::vec3& position, glm::vec3& targetPosition, float k, float damping) {
}

View file

@ -1,18 +0,0 @@
//
// Physics.h
// interface/src
//
// Created by Philip on 4/25/13.
// Copyright 2013 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_Physics_h
#define hifi_Physics_h
void applyStaticFriction(float deltaTime, glm::vec3& velocity, float maxVelocity, float strength);
void applyDamping(float deltaTime, glm::vec3& velocity, float linearStrength, float squaredStrength);
#endif // hifi_Physics_h

View file

@ -9,44 +9,32 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <vector>
#include "Avatar.h"
#include <QDesktopWidget>
#include <QWindow>
#include <glm/glm.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/vector_angle.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <QtCore/QThread>
#include <glm/gtx/transform.hpp>
#include <glm/gtx/vector_query.hpp>
#include <DeferredLightingEffect.h>
#include <GeometryUtil.h>
#include <LODManager.h>
#include <EntityTreeRenderer.h>
#include <NodeList.h>
#include <NumericalConstants.h>
#include <OctreeUtils.h>
#include <udt/PacketHeaders.h>
#include <PerfStat.h>
#include <Rig.h>
#include <SharedUtil.h>
#include <TextRenderer3D.h>
#include <TextureCache.h>
#include <VariantMapToScriptValue.h>
#include <DebugDraw.h>
#include "Application.h"
#include "Avatar.h"
#include "AvatarManager.h"
#include "AvatarMotionState.h"
#include "Head.h"
#include "Camera.h"
#include "Menu.h"
#include "Physics.h"
#include "Util.h"
#include "world.h"
#include "InterfaceLogging.h"
#include "SceneScriptingInterface.h"
#include "SoftAttachmentModel.h"
#include <Rig.h>
using namespace std;
@ -71,7 +59,8 @@ namespace render {
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
if (avatarPtr->isInitialized() && args) {
PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload");
avatarPtr->render(args, qApp->getCamera()->getPosition());
// TODO AVATARS_RENDERER: remove need for qApp
avatarPtr->render(args);
}
}
template <> uint32_t metaFetchMetaSubItems(const AvatarSharedPointer& avatar, ItemIDs& subItems) {
@ -85,7 +74,7 @@ namespace render {
}
}
Avatar::Avatar(RigPointer rig) :
Avatar::Avatar(QThread* thread, RigPointer rig) :
AvatarData(),
_skeletonOffset(0.0f),
_bodyYawDelta(0.0f),
@ -96,11 +85,19 @@ Avatar::Avatar(RigPointer rig) :
_lastOrientation(),
_worldUpDirection(DEFAULT_UP_DIRECTION),
_moving(false),
_smoothPositionTime(SMOOTH_TIME_POSITION),
_smoothPositionTimer(std::numeric_limits<float>::max()),
_smoothOrientationTime(SMOOTH_TIME_ORIENTATION),
_smoothOrientationTimer(std::numeric_limits<float>::max()),
_smoothPositionInitial(),
_smoothPositionTarget(),
_smoothOrientationInitial(),
_smoothOrientationTarget(),
_initialized(false),
_voiceSphereID(GeometryCache::UNKNOWN_ID)
{
// we may have been created in the network thread, but we live in the main thread
moveToThread(qApp->thread());
moveToThread(thread);
setScale(glm::vec3(1.0f)); // avatar scale is uniform
@ -118,9 +115,7 @@ Avatar::Avatar(RigPointer rig) :
}
Avatar::~Avatar() {
assert(isDead()); // mark dead before calling the dtor
auto treeRenderer = qApp->getEntities();
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
if (entityTree) {
entityTree->withWriteLock([&] {
@ -229,7 +224,7 @@ void Avatar::updateAvatarEntities() {
return; // wait until MyAvatar gets an ID before doing this.
}
auto treeRenderer = qApp->getEntities();
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
if (!entityTree) {
return;
@ -349,6 +344,33 @@ void Avatar::simulate(float deltaTime, bool inView) {
_simulationInViewRate.increment();
}
if (!isMyAvatar()) {
if (_smoothPositionTimer < _smoothPositionTime) {
// Smooth the remote avatar movement.
_smoothPositionTimer += deltaTime;
if (_smoothPositionTimer < _smoothPositionTime) {
AvatarData::setPosition(
lerp(_smoothPositionInitial,
_smoothPositionTarget,
easeInOutQuad(glm::clamp(_smoothPositionTimer / _smoothPositionTime, 0.0f, 1.0f)))
);
updateAttitude();
}
}
if (_smoothOrientationTimer < _smoothOrientationTime) {
// Smooth the remote avatar movement.
_smoothOrientationTimer += deltaTime;
if (_smoothOrientationTimer < _smoothOrientationTime) {
AvatarData::setOrientation(
slerp(_smoothOrientationInitial,
_smoothOrientationTarget,
easeInOutQuad(glm::clamp(_smoothOrientationTimer / _smoothOrientationTime, 0.0f, 1.0f)))
);
updateAttitude();
}
}
}
PerformanceTimer perfTimer("simulate");
{
@ -483,19 +505,20 @@ static TextRenderer3D* textRenderer(TextRendererType type) {
return displayNameRenderer;
}
void Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
auto avatarPayload = new render::Payload<AvatarData>(self);
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
_renderItemID = scene->allocateID();
transaction.resetItem(_renderItemID, avatarPayloadPointer);
_skeletonModel->addToScene(scene, transaction);
if (_skeletonModel->addToScene(scene, transaction)) {
_renderItemID = scene->allocateID();
transaction.resetItem(_renderItemID, avatarPayloadPointer);
for (auto& attachmentModel : _attachmentModels) {
attachmentModel->addToScene(scene, transaction);
for (auto& attachmentModel : _attachmentModels) {
attachmentModel->addToScene(scene, transaction);
}
}
}
void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::Transaction& transaction) {
void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
transaction.removeItem(_renderItemID);
render::Item::clearID(_renderItemID);
_skeletonModel->removeFromScene(scene, transaction);
@ -543,11 +566,13 @@ void Avatar::postUpdate(float deltaTime) {
}
}
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
auto& batch = *renderArgs->_batch;
void Avatar::render(RenderArgs* renderArgs) {
auto& batch = *(renderArgs->_batch);
PROFILE_RANGE_BATCH(batch, __FUNCTION__);
if (glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), getPosition()) < 10.0f) {
glm::vec3 viewPos = renderArgs->getViewFrustum().getPosition();
const float MAX_DISTANCE_SQUARED_FOR_SHOWING_POINTING_LASERS = 100.0f; // 10^2
if (glm::distance2(viewPos, getPosition()) < MAX_DISTANCE_SQUARED_FOR_SHOWING_POINTING_LASERS) {
auto geometryCache = DependencyManager::get<GeometryCache>();
// render pointing lasers
@ -606,23 +631,16 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
}
}
{ // simple frustum check
ViewFrustum frustum;
if (renderArgs->_renderMode == RenderArgs::SHADOW_RENDER_MODE) {
qApp->copyShadowViewFrustum(frustum);
} else {
qApp->copyDisplayViewFrustum(frustum);
}
if (!frustum.sphereIntersectsFrustum(getPosition(), getBoundingRadius())) {
return;
}
ViewFrustum frustum = renderArgs->getViewFrustum();
if (!frustum.sphereIntersectsFrustum(getPosition(), getBoundingRadius())) {
return;
}
glm::vec3 toTarget = cameraPosition - getPosition();
glm::vec3 toTarget = frustum.getPosition() - getPosition();
float distanceToTarget = glm::length(toTarget);
{
fixupModelsInScene();
fixupModelsInScene(renderArgs->_scene);
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) {
// add local lights
@ -651,8 +669,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
const float DISPLAYNAME_DISTANCE = 20.0f;
setShowDisplayName(distanceToTarget < DISPLAYNAME_DISTANCE);
auto cameraMode = qApp->getCamera()->getMode();
if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) {
if (!isMyAvatar() || renderArgs->_cameraMode != (int8_t)CAMERA_MODE_FIRST_PERSON) {
auto& frustum = renderArgs->getViewFrustum();
auto textPosition = getDisplayNamePosition();
if (frustum.pointIntersectsFrustum(textPosition)) {
@ -677,12 +694,11 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
return glm::angleAxis(angle * proportion, axis);
}
void Avatar::fixupModelsInScene() {
void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
_attachmentsToDelete.clear();
// 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::Transaction transaction;
if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) {
_skeletonModel->removeFromScene(scene, transaction);
@ -912,6 +928,17 @@ QVector<glm::quat> Avatar::getJointRotations() const {
return jointRotations;
}
QVector<glm::vec3> Avatar::getJointTranslations() const {
if (QThread::currentThread() != thread()) {
return AvatarData::getJointTranslations();
}
QVector<glm::vec3> jointTranslations(_skeletonModel->getJointStateCount());
for (int i = 0; i < _skeletonModel->getJointStateCount(); ++i) {
_skeletonModel->getJointTranslation(i, jointTranslations[i]);
}
return jointTranslations;
}
glm::quat Avatar::getJointRotation(int index) const {
glm::quat rotation;
_skeletonModel->getJointRotation(index, rotation);
@ -1095,11 +1122,20 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
void Avatar::setModelURLFinished(bool success) {
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL;
// call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that
// we don't redo this every time we receive an identity packet from the avatar with the bad url.
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL",
Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl()));
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 ||
_skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) {
qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL
<< "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts.";
// call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that
// we don't redo this every time we receive an identity packet from the avatar with the bad url.
QMetaObject::invokeMethod(_skeletonModel.get(), "setURL",
Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl()));
} else {
qCWarning(interfaceapp) << "Avatar model: " << _skeletonModelURL
<< "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts()
<< "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS;
}
}
}
@ -1361,13 +1397,31 @@ glm::quat Avatar::getUncachedRightPalmRotation() const {
}
void Avatar::setPosition(const glm::vec3& position) {
AvatarData::setPosition(position);
updateAttitude();
if (isMyAvatar()) {
// This is the local avatar, no need to handle any position smoothing.
AvatarData::setPosition(position);
updateAttitude();
return;
}
// Whether or not there is an existing smoothing going on, just reset the smoothing timer and set the starting position as the avatar's current position, then smooth to the new position.
_smoothPositionInitial = getPosition();
_smoothPositionTarget = position;
_smoothPositionTimer = 0.0f;
}
void Avatar::setOrientation(const glm::quat& orientation) {
AvatarData::setOrientation(orientation);
updateAttitude();
if (isMyAvatar()) {
// This is the local avatar, no need to handle any position smoothing.
AvatarData::setOrientation(orientation);
updateAttitude();
return;
}
// Whether or not there is an existing smoothing going on, just reset the smoothing timer and set the starting position as the avatar's current position, then smooth to the new position.
_smoothOrientationInitial = getOrientation();
_smoothOrientationTarget = orientation;
_smoothOrientationTimer = 0.0f;
}
void Avatar::updatePalms() {
@ -1437,8 +1491,7 @@ QList<QVariant> Avatar::getSkeleton() {
return QList<QVariant>();
}
void Avatar::addToScene(AvatarSharedPointer myHandle) {
render::ScenePointer scene = qApp->getMain3DScene();
void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) {
if (scene) {
render::Transaction transaction;
auto nodelist = DependencyManager::get<NodeList>();
@ -1452,8 +1505,9 @@ void Avatar::addToScene(AvatarSharedPointer myHandle) {
qCWarning(interfaceapp) << "AvatarManager::addAvatar() : Unexpected null scene, possibly during application shutdown";
}
}
void Avatar::ensureInScene(AvatarSharedPointer self) {
void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene) {
if (!render::Item::isValidID(_renderItemID)) {
addToScene(self);
addToScene(self, scene);
}
}

View file

@ -14,17 +14,15 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QtCore/QScopedPointer>
#include <QtCore/QUuid>
#include <AvatarData.h>
#include <ShapeInfo.h>
#include <render/Scene.h>
#include "Head.h"
#include "SkeletonModel.h"
#include "world.h"
#include "Rig.h"
#include <ThreadSafeValueCache.h>
@ -68,7 +66,7 @@ class Avatar : public AvatarData {
Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset)
public:
explicit Avatar(RigPointer rig = nullptr);
explicit Avatar(QThread* thread, RigPointer rig = nullptr);
~Avatar();
typedef render::Payload<AvatarData> Payload;
@ -79,12 +77,12 @@ public:
void simulate(float deltaTime, bool inView);
virtual void simulateAttachments(float deltaTime);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
virtual void render(RenderArgs* renderArgs);
void addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene,
render::Transaction& transaction);
void removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene,
void removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene,
render::Transaction& transaction);
void updateRenderItem(render::Transaction& transaction);
@ -114,6 +112,7 @@ public:
virtual QVector<glm::quat> getJointRotations() const override;
virtual glm::quat getJointRotation(int index) const override;
virtual QVector<glm::vec3> getJointTranslations() const override;
virtual glm::vec3 getJointTranslation(int index) const override;
virtual int getJointIndex(const QString& name) const override;
virtual QStringList getJointNames() const override;
@ -230,6 +229,16 @@ public:
bool hasNewJointData() const { return _hasNewJointData; }
inline float easeInOutQuad(float lerpValue) {
assert(!((lerpValue < 0.0f) || (lerpValue > 1.0f)));
if (lerpValue < 0.5f) {
return (2.0f * lerpValue * lerpValue);
}
return (lerpValue*(4.0f - 2.0f * lerpValue) - 1.0f);
}
public slots:
// FIXME - these should be migrated to use Pose data instead
@ -244,6 +253,9 @@ public slots:
protected:
friend class AvatarManager;
const float SMOOTH_TIME_POSITION = 0.125f;
const float SMOOTH_TIME_ORIENTATION = 0.075f;
virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send.
QString _empty{};
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter!
@ -292,7 +304,7 @@ protected:
Transform calculateDisplayNameTransform(const ViewFrustum& view, const glm::vec3& textPosition) const;
void renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const;
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const;
virtual void fixupModelsInScene();
virtual void fixupModelsInScene(const render::ScenePointer& scene);
virtual void updatePalms();
@ -303,8 +315,8 @@ protected:
ThreadSafeValueCache<glm::vec3> _rightPalmPositionCache { glm::vec3() };
ThreadSafeValueCache<glm::quat> _rightPalmRotationCache { glm::quat() };
void addToScene(AvatarSharedPointer self);
void ensureInScene(AvatarSharedPointer self);
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene);
void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene);
bool isInScene() const { return render::Item::isValidID(_renderItemID); }
// Some rate tracking support
@ -313,6 +325,15 @@ protected:
RateCounter<> _skeletonModelSimulationRate;
RateCounter<> _jointDataSimulationRate;
// Smoothing data for blending from one position/orientation to another on remote agents.
float _smoothPositionTime;
float _smoothPositionTimer;
float _smoothOrientationTime;
float _smoothOrientationTimer;
glm::vec3 _smoothPositionInitial;
glm::vec3 _smoothPositionTarget;
glm::quat _smoothOrientationInitial;
glm::quat _smoothOrientationTarget;
private:
class AvatarEntityDataHash {

View file

@ -0,0 +1,64 @@
//
// AvatarActionFarGrab.cpp
// interface/src/avatar/
//
// Created by Seth Alves 2017-4-14
// 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 "AvatarActionFarGrab.h"
AvatarActionFarGrab::AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity) :
ObjectActionSpring(id, ownerEntity) {
_type = DYNAMIC_TYPE_FAR_GRAB;
#if WANT_DEBUG
qDebug() << "AvatarActionFarGrab::AvatarActionFarGrab";
#endif
}
AvatarActionFarGrab::~AvatarActionFarGrab() {
#if WANT_DEBUG
qDebug() << "AvatarActionFarGrab::~AvatarActionFarGrab";
#endif
}
QByteArray AvatarActionFarGrab::serialize() const {
QByteArray serializedActionArguments;
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
dataStream << DYNAMIC_TYPE_FAR_GRAB;
dataStream << getID();
dataStream << ObjectActionSpring::springVersion;
serializeParameters(dataStream);
return serializedActionArguments;
}
void AvatarActionFarGrab::deserialize(QByteArray serializedArguments) {
QDataStream dataStream(serializedArguments);
EntityDynamicType type;
dataStream >> type;
QUuid id;
dataStream >> id;
if (type != getType() || id != getID()) {
qDebug() << "AvatarActionFarGrab::deserialize type or ID don't match." << type << id << getID();
return;
}
uint16_t serializationVersion;
dataStream >> serializationVersion;
if (serializationVersion != ObjectActionSpring::springVersion) {
assert(false);
return;
}
deserializeParameters(serializedArguments, dataStream);
}

View file

@ -0,0 +1,27 @@
//
// AvatarActionFarGrab.h
// interface/src/avatar/
//
// Created by Seth Alves 2017-4-14
// 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_AvatarActionFarGrab_h
#define hifi_AvatarActionFarGrab_h
#include <EntityItem.h>
#include <ObjectActionSpring.h>
class AvatarActionFarGrab : public ObjectActionSpring {
public:
AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AvatarActionFarGrab();
QByteArray serialize() const override;
virtual void deserialize(QByteArray serializedArguments) override;
};
#endif // hifi_AvatarActionFarGrab_h

View file

@ -23,7 +23,7 @@ const int AvatarActionHold::velocitySmoothFrames = 6;
AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity) :
ObjectActionSpring(id, ownerEntity)
{
_type = ACTION_TYPE_HOLD;
_type = DYNAMIC_TYPE_HOLD;
_measuredLinearVelocities.resize(AvatarActionHold::velocitySmoothFrames);
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
@ -323,28 +323,28 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
bool ignoreIK;
bool needUpdate = false;
bool somethingChanged = ObjectAction::updateArguments(arguments);
bool somethingChanged = ObjectDynamic::updateArguments(arguments);
withReadLock([&]{
bool ok = true;
relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
relativePosition = EntityDynamicInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false);
if (!ok) {
relativePosition = _relativePosition;
}
ok = true;
relativeRotation = EntityActionInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
relativeRotation = EntityDynamicInterface::extractQuatArgument("hold", arguments, "relativeRotation", ok, false);
if (!ok) {
relativeRotation = _relativeRotation;
}
ok = true;
timeScale = EntityActionInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
timeScale = EntityDynamicInterface::extractFloatArgument("hold", arguments, "timeScale", ok, false);
if (!ok) {
timeScale = _linearTimeScale;
}
ok = true;
hand = EntityActionInterface::extractStringArgument("hold", arguments, "hand", ok, false);
hand = EntityDynamicInterface::extractStringArgument("hold", arguments, "hand", ok, false);
if (!ok || !(hand == "left" || hand == "right")) {
hand = _hand;
}
@ -353,20 +353,20 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
holderID = myAvatar->getSessionUUID();
ok = true;
kinematic = EntityActionInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
kinematic = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false);
if (!ok) {
kinematic = _kinematic;
}
ok = true;
kinematicSetVelocity = EntityActionInterface::extractBooleanArgument("hold", arguments,
kinematicSetVelocity = EntityDynamicInterface::extractBooleanArgument("hold", arguments,
"kinematicSetVelocity", ok, false);
if (!ok) {
kinematicSetVelocity = _kinematicSetVelocity;
}
ok = true;
ignoreIK = EntityActionInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false);
ignoreIK = EntityDynamicInterface::extractBooleanArgument("hold", arguments, "ignoreIK", ok, false);
if (!ok) {
ignoreIK = _ignoreIK;
}
@ -400,8 +400,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
auto ownerEntity = _ownerEntity.lock();
if (ownerEntity) {
ownerEntity->setActionDataDirty(true);
ownerEntity->setActionDataNeedsTransmit(true);
ownerEntity->setDynamicDataDirty(true);
ownerEntity->setDynamicDataNeedsTransmit(true);
}
});
}
@ -410,7 +410,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
}
QVariantMap AvatarActionHold::getArguments() {
QVariantMap arguments = ObjectAction::getArguments();
QVariantMap arguments = ObjectDynamic::getArguments();
withReadLock([&]{
arguments["holderID"] = _holderID;
arguments["relativePosition"] = glmToQMap(_relativePosition);
@ -429,7 +429,7 @@ QByteArray AvatarActionHold::serialize() const {
QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly);
withReadLock([&]{
dataStream << ACTION_TYPE_HOLD;
dataStream << DYNAMIC_TYPE_HOLD;
dataStream << getID();
dataStream << AvatarActionHold::holdVersion;
@ -451,7 +451,7 @@ QByteArray AvatarActionHold::serialize() const {
void AvatarActionHold::deserialize(QByteArray serializedArguments) {
QDataStream dataStream(serializedArguments);
EntityActionType type;
EntityDynamicType type;
dataStream >> type;
assert(type == getType());

View file

@ -69,7 +69,7 @@ void AvatarManager::registerMetaTypes(QScriptEngine* engine) {
AvatarManager::AvatarManager(QObject* parent) :
_avatarsToFade(),
_myAvatar(std::make_shared<MyAvatar>(std::make_shared<Rig>()))
_myAvatar(std::make_shared<MyAvatar>(qApp->thread(), std::make_shared<Rig>()))
{
// register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar
qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");
@ -105,7 +105,7 @@ void AvatarManager::init() {
this, &AvatarManager::updateAvatarRenderStatus, Qt::QueuedConnection);
if (_shouldRender) {
render::ScenePointer scene = qApp->getMain3DScene();
const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction;
_myAvatar->addToScene(_myAvatar, scene, transaction);
scene->enqueueTransaction(transaction);
@ -198,7 +198,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
// for ALL avatars...
if (_shouldRender) {
avatar->ensureInScene(avatar);
avatar->ensureInScene(avatar, qApp->getMain3DScene());
}
if (!avatar->getMotionState()) {
ShapeInfo shapeInfo;
@ -213,10 +213,6 @@ 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();
@ -306,12 +302,10 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
// fading to zero is such a rare event we push unique transaction for each one
if (avatar->isInScene()) {
render::ScenePointer scene = qApp->getMain3DScene();
const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction;
avatar->removeFromScene(*itr, scene, transaction);
if (scene) {
scene->enqueueTransaction(transaction);
}
scene->enqueueTransaction(transaction);
}
// only remove from _avatarsToFade if we're sure its motionState has been removed from PhysicsEngine
@ -329,38 +323,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
}
AvatarSharedPointer AvatarManager::newSharedAvatar() {
return std::make_shared<Avatar>(std::make_shared<Rig>());
}
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);
}
}
}
}
return std::make_shared<Avatar>(qApp->thread(), std::make_shared<Rig>());
}
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) {
@ -368,8 +331,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
// removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar
// class in this context so we can call methods that don't exist at the base class.
Avatar* avatar = static_cast<Avatar*>(removedAvatar.get());
avatar->die();
auto avatar = std::static_pointer_cast<Avatar>(removedAvatar);
AvatarMotionState* motionState = avatar->getMotionState();
if (motionState) {
@ -394,7 +356,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
void AvatarManager::clearOtherAvatars() {
// 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();
const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction;
QReadLocker locker(&_hashLock);
@ -405,18 +367,13 @@ void AvatarManager::clearOtherAvatars() {
if (avatar->isInScene()) {
avatar->removeFromScene(avatar, scene, transaction);
}
AvatarMotionState* motionState = avatar->getMotionState();
if (motionState) {
_motionStatesThatMightUpdate.remove(motionState);
_motionStatesToAddToPhysics.remove(motionState);
_motionStatesToRemoveFromPhysics.push_back(motionState);
}
handleRemovedAvatar(avatar);
avatarIterator = _avatarHash.erase(avatarIterator);
} else {
++avatarIterator;
}
++avatarIterator;
}
if (scene) {
scene->enqueueTransaction(transaction);
}
scene->enqueueTransaction(transaction);
_myAvatar->clearLookAtTargetAvatar();
}
@ -522,7 +479,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
_shouldRender = shouldRenderAvatars;
render::ScenePointer scene = qApp->getMain3DScene();
const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction;
if (_shouldRender) {
for (auto avatarData : _avatarHash) {
@ -535,9 +492,7 @@ void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) {
avatar->removeFromScene(avatar, scene, transaction);
}
}
if (scene) {
scene->enqueueTransaction(transaction);
}
scene->enqueueTransaction(transaction);
}
AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) const {

View file

@ -23,6 +23,7 @@
#include <shared/RateCounter.h>
#include "Avatar.h"
#include "MyAvatar.h"
#include "AvatarMotionState.h"
#include "ScriptAvatar.h"
@ -42,6 +43,7 @@ public:
void init();
std::shared_ptr<MyAvatar> getMyAvatar() { return _myAvatar; }
glm::vec3 getMyAvatarPosition() const { return _myAvatar->getPosition(); }
// Null/Default-constructed QUuids will return MyAvatar
Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) override { return new ScriptAvatar(getAvatarBySessionID(avatarID)); }
@ -96,9 +98,6 @@ public slots:
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
void updateAvatarRenderStatus(bool shouldRenderAvatars);
protected slots:
void processAvatarDataPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
private:
explicit AvatarManager(QObject* parent = 0);
explicit AvatarManager(const AvatarManager& other);
@ -110,7 +109,7 @@ private:
QVector<AvatarSharedPointer> _avatarsToFade;
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
QSet<AvatarMotionState*> _motionStatesThatMightUpdate;
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
SetOfMotionStates _motionStatesToAddToPhysics;

View file

@ -83,6 +83,4 @@ protected:
uint32_t _dirtyFlags;
};
typedef QSet<AvatarMotionState*> SetOfAvatarMotionStates;
#endif // hifi_AvatarMotionState_h

View file

@ -48,7 +48,6 @@
#include "AvatarActionHold.h"
#include "Menu.h"
#include "MyAvatar.h"
#include "Physics.h"
#include "Util.h"
#include "InterfaceLogging.h"
#include "DebugDraw.h"
@ -99,8 +98,8 @@ static const glm::quat DEFAULT_AVATAR_LEFTFOOT_ROT { -0.40167322754859924f, 0.91
static const glm::vec3 DEFAULT_AVATAR_RIGHTFOOT_POS { 0.08f, -0.96f, 0.029f };
static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.9154615998268127f, 0.0053307069465518f, 0.023696165531873703f };
MyAvatar::MyAvatar(RigPointer rig) :
Avatar(rig),
MyAvatar::MyAvatar(QThread* thread, RigPointer rig) :
Avatar(thread, rig),
_wasPushing(false),
_isPushing(false),
_isBeingPushed(false),
@ -276,7 +275,7 @@ void MyAvatar::simulateAttachments(float deltaTime) {
}
QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail) {
CameraMode mode = qApp->getCamera()->getMode();
CameraMode mode = qApp->getCamera().getMode();
_globalPosition = getPosition();
_globalBoundingBoxDimensions.x = _characterController.getCapsuleRadius();
_globalBoundingBoxDimensions.y = _characterController.getCapsuleHalfHeight();
@ -429,9 +428,7 @@ void MyAvatar::update(float deltaTime) {
Q_ARG(glm::vec3, (getPosition() - halfBoundingBoxDimensions)),
Q_ARG(glm::vec3, (halfBoundingBoxDimensions*2.0f)));
uint64_t now = usecTimestampNow();
if (now > _identityPacketExpiry || _avatarEntityDataLocallyEdited) {
_identityPacketExpiry = now + AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS;
if (getIdentityDataChanged()) {
sendIdentityPacket();
}
@ -763,13 +760,12 @@ controller::Pose MyAvatar::getRightHandTipPose() const {
}
// virtual
void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
void MyAvatar::render(RenderArgs* renderArgs) {
// don't render if we've been asked to disable local rendering
if (!_shouldRender) {
return; // exit early
}
Avatar::render(renderArgs, cameraPosition);
Avatar::render(renderArgs);
}
void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
@ -938,8 +934,7 @@ void MyAvatar::setEnableDebugDrawIKTargets(bool isEnabled) {
}
void MyAvatar::setEnableMeshVisible(bool isEnabled) {
render::ScenePointer scene = qApp->getMain3DScene();
_skeletonModel->setVisibleInScene(isEnabled, scene);
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene());
}
void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) {
@ -1090,7 +1085,7 @@ void MyAvatar::updateLookAtTargetAvatar() {
_targetAvatarPosition = glm::vec3(0.0f);
glm::vec3 lookForward = getHead()->getFinalOrientationInWorldFrame() * IDENTITY_FORWARD;
glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
glm::vec3 cameraPosition = qApp->getCamera().getPosition();
float smallestAngleTo = glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES) / 2.0f;
const float KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR = 1.3f;
@ -1236,8 +1231,7 @@ void MyAvatar::clearJointsData() {
void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
Avatar::setSkeletonModelURL(skeletonModelURL);
render::ScenePointer scene = qApp->getMain3DScene();
_skeletonModel->setVisibleInScene(true, scene);
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene());
_headBoneSet.clear();
}
@ -1273,7 +1267,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
setSkeletonModelURL(fullAvatarURL);
UserActivityLogger::getInstance().changedModel("skeleton", urlString);
}
_identityPacketExpiry = 0; // triggers an identity packet next update()
markIdentityDataChanged();
}
void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
@ -1286,7 +1280,7 @@ void MyAvatar::setAttachmentData(const QVector<AttachmentData>& attachmentData)
}
glm::vec3 MyAvatar::getSkeletonPosition() const {
CameraMode mode = qApp->getCamera()->getMode();
CameraMode mode = qApp->getCamera().getMode();
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
// The avatar is rotated PI about the yAxis, so we have to correct for it
// to get the skeleton offset contribution in the world-frame.
@ -1610,7 +1604,7 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName,
Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved);
}
void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) {
void MyAvatar::setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visible) {
if (model->isActive() && model->isRenderable()) {
model->setVisibleInScene(visible, scene);
}
@ -1704,8 +1698,7 @@ void MyAvatar::postUpdate(float deltaTime) {
Avatar::postUpdate(deltaTime);
render::ScenePointer scene = qApp->getMain3DScene();
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars() && _skeletonModel->initWhenReady(scene)) {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars() && _skeletonModel->initWhenReady(qApp->getMain3DScene())) {
initHeadBones();
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
@ -1774,13 +1767,13 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f;
bool MyAvatar::cameraInsideHead() const {
const glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
const glm::vec3 cameraPosition = qApp->getCamera().getPosition();
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale());
}
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE;
bool firstPerson = qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON;
bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON;
bool insideHead = cameraInsideHead();
return !defaultMode || !firstPerson || !insideHead;
}
@ -2330,7 +2323,7 @@ glm::vec3 MyAvatar::getPositionForAudio() {
case AudioListenerMode::FROM_HEAD:
return getHead()->getPosition();
case AudioListenerMode::FROM_CAMERA:
return qApp->getCamera()->getPosition();
return qApp->getCamera().getPosition();
case AudioListenerMode::CUSTOM:
return _customListenPosition;
}
@ -2342,7 +2335,7 @@ glm::quat MyAvatar::getOrientationForAudio() {
case AudioListenerMode::FROM_HEAD:
return getHead()->getFinalOrientationInWorldFrame();
case AudioListenerMode::FROM_CAMERA:
return qApp->getCamera()->getOrientation();
return qApp->getCamera().getOrientation();
case AudioListenerMode::CUSTOM:
return _customListenOrientation;
}
@ -2432,7 +2425,7 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
}
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
auto cameraMode = qApp->getCamera()->getMode();
auto cameraMode = qApp->getCamera().getMode();
if (cameraMode == CAMERA_MODE_THIRD_PERSON) {
return false;
} else {
@ -2580,8 +2573,8 @@ void MyAvatar::setAway(bool value) {
glm::mat4 MyAvatar::computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const {
// Fetch the current camera transform.
glm::mat4 cameraWorldMatrix = qApp->getCamera()->getTransform();
if (qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR) {
glm::mat4 cameraWorldMatrix = qApp->getCamera().getTransform();
if (qApp->getCamera().getMode() == CAMERA_MODE_MIRROR) {
cameraWorldMatrix *= createMatFromScaleQuatAndPos(vec3(-1.0f, 1.0f, 1.0f), glm::quat(), glm::vec3());
}
@ -2626,7 +2619,7 @@ glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const {
Transform avatarTransform;
Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
return glmExtractRotation(invAvatarMat * qApp->getCamera()->getTransform());
return glmExtractRotation(invAvatarMat * qApp->getCamera().getTransform());
}
default: {
return Avatar::getAbsoluteJointRotationInObjectFrame(index);
@ -2663,7 +2656,7 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
Transform avatarTransform;
Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
return extractTranslation(invAvatarMat * qApp->getCamera()->getTransform());
return extractTranslation(invAvatarMat * qApp->getCamera().getTransform());
}
default: {
return Avatar::getAbsoluteJointTranslationInObjectFrame(index);

View file

@ -146,7 +146,7 @@ public:
};
Q_ENUM(DriveKeys)
explicit MyAvatar(RigPointer rig);
explicit MyAvatar(QThread* thread, RigPointer rig);
~MyAvatar();
void registerMetaTypes(QScriptEngine* engine);
@ -549,7 +549,7 @@ private:
void simulate(float deltaTime);
void updateFromTrackers(float deltaTime);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
virtual void render(RenderArgs* renderArgs) override;
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); }
bool getShouldRenderLocally() const { return _shouldRender; }
@ -575,7 +575,7 @@ private:
// These are made private for MyAvatar so that you will use the "use" methods instead
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity);
void setVisibleInSceneIfReady(Model* model, const render::ScenePointer& scene, bool visiblity);
private:
@ -725,8 +725,6 @@ private:
std::mutex _holdActionsMutex;
std::vector<AvatarActionHold*> _holdActions;
uint64_t _identityPacketExpiry { 0 };
float AVATAR_MOVEMENT_ENERGY_CONSTANT { 0.001f };
float AUDIO_ENERGY_CONSTANT { 0.000001f };
float MAX_AVATAR_MOVEMENT_PER_FRAME { 30.0f };

View file

@ -262,7 +262,7 @@ void SkeletonModel::updateAttitude() {
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
updateAttitude();
if (fullUpdate) {
setBlendshapeCoefficients(_owningAvatar->getHead()->getBlendshapeCoefficients());
setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients());
Model::simulate(deltaTime, fullUpdate);

View file

@ -79,6 +79,25 @@ int main(int argc, const char* argv[]) {
instanceMightBeRunning = false;
}
QCommandLineParser parser;
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
QCommandLineOption runServerOption("runServer", "Whether to run the server");
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
parser.addOption(checkMinSpecOption);
parser.addOption(runServerOption);
parser.addOption(serverContentPathOption);
parser.addOption(allowMultipleInstancesOption);
parser.parse(arguments);
bool runServer = parser.isSet(runServerOption);
bool serverContentPathOptionIsSet = parser.isSet(serverContentPathOption);
QString serverContentPathOptionValue = serverContentPathOptionIsSet ? parser.value(serverContentPathOption) : QString();
bool allowMultipleInstances = parser.isSet(allowMultipleInstancesOption);
if (allowMultipleInstances) {
instanceMightBeRunning = false;
}
if (instanceMightBeRunning) {
// Try to connect and send message to existing interface instance
QLocalSocket socket;
@ -137,18 +156,6 @@ int main(int argc, const char* argv[]) {
}
}
QCommandLineParser parser;
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
QCommandLineOption runServerOption("runServer", "Whether to run the server");
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
parser.addOption(checkMinSpecOption);
parser.addOption(runServerOption);
parser.addOption(serverContentPathOption);
parser.parse(arguments);
bool runServer = parser.isSet(runServerOption);
bool serverContentPathOptionIsSet = parser.isSet(serverContentPathOption);
QString serverContentPathOptionValue = serverContentPathOptionIsSet ? parser.value(serverContentPathOption) : QString();
QElapsedTimer startupTime;
startupTime.start();

View file

@ -34,9 +34,13 @@ void HFTabletWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestI
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());
static const QString USER_AGENT = "User-Agent";
QString tokenString = "Chrome/48.0 (HighFidelityInterface)";
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
} else {
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

@ -16,15 +16,24 @@
#include "ui/DialogsManager.h"
DialogsManagerScriptingInterface::DialogsManagerScriptingInterface() {
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::addressBarToggled,
this, &DialogsManagerScriptingInterface::addressBarToggled);
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::addressBarShown,
this, &DialogsManagerScriptingInterface::addressBarShown);
}
void DialogsManagerScriptingInterface::toggleAddressBar() {
DialogsManagerScriptingInterface* DialogsManagerScriptingInterface::getInstance() {
static DialogsManagerScriptingInterface sharedInstance;
return &sharedInstance;
}
void DialogsManagerScriptingInterface::showAddressBar() {
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
"toggleAddressBar", Qt::QueuedConnection);
"showAddressBar", Qt::QueuedConnection);
}
void DialogsManagerScriptingInterface::hideAddressBar() {
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
"hideAddressBar", Qt::QueuedConnection);
}
void DialogsManagerScriptingInterface::showFeed() {

View file

@ -18,13 +18,14 @@ class DialogsManagerScriptingInterface : public QObject {
Q_OBJECT
public:
DialogsManagerScriptingInterface();
static DialogsManagerScriptingInterface* getInstance();
Q_INVOKABLE void showFeed();
public slots:
void toggleAddressBar();
void showAddressBar();
void hideAddressBar();
signals:
void addressBarToggled();
void addressBarShown(bool visible);
};

View file

@ -105,9 +105,9 @@ QScriptValue HMDScriptingInterface::getHUDLookAtPosition3D(QScriptContext* conte
}
bool HMDScriptingInterface::getHUDLookAtPosition3D(glm::vec3& result) const {
Camera* camera = qApp->getCamera();
glm::vec3 position = camera->getPosition();
glm::quat orientation = camera->getOrientation();
const Camera& camera = qApp->getCamera();
glm::vec3 position = camera.getPosition();
glm::quat orientation = camera.getOrientation();
glm::vec3 direction = orientation * glm::vec3(0.0f, 0.0f, -1.0f);

View file

@ -28,6 +28,7 @@
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation";
static const QString LAST_BROWSE_ASSETS_LOCATION_SETTING = "LastBrowseAssetsLocation";
QScriptValue CustomPromptResultToScriptValue(QScriptEngine* engine, const CustomPromptResult& result) {
@ -149,6 +150,15 @@ void WindowScriptingInterface::setPreviousBrowseLocation(const QString& location
Setting::Handle<QVariant>(LAST_BROWSE_LOCATION_SETTING).set(location);
}
QString WindowScriptingInterface::getPreviousBrowseAssetLocation() const {
QString ASSETS_ROOT_PATH = "/";
return Setting::Handle<QString>(LAST_BROWSE_ASSETS_LOCATION_SETTING, ASSETS_ROOT_PATH).get();
}
void WindowScriptingInterface::setPreviousBrowseAssetLocation(const QString& location) {
Setting::Handle<QVariant>(LAST_BROWSE_ASSETS_LOCATION_SETTING).set(location);
}
/// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and
/// might be in same thread as a script that sets the reticle to invisible
void WindowScriptingInterface::ensureReticleVisible() const {
@ -202,6 +212,31 @@ QScriptValue WindowScriptingInterface::save(const QString& title, const QString&
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
}
/// Display a select asset dialog that lets the user select an asset from the Asset Server. If `directory` is an invalid
/// directory the browser will start at the root directory.
/// \param const QString& title title of the window
/// \param const QString& directory directory to start the asset browser at
/// \param const QString& nameFilter filter to filter asset names by - see `QFileDialog`
/// \return QScriptValue asset path as a string if one was selected, otherwise `QScriptValue::NullValue`
QScriptValue WindowScriptingInterface::browseAssets(const QString& title, const QString& directory, const QString& nameFilter) {
ensureReticleVisible();
QString path = directory;
if (path.isEmpty()) {
path = getPreviousBrowseAssetLocation();
}
if (path.left(1) != "/") {
path = "/" + path;
}
if (path.right(1) != "/") {
path = path + "/";
}
QString result = OffscreenUi::getOpenAssetName(nullptr, title, path, nameFilter);
if (!result.isEmpty()) {
setPreviousBrowseAssetLocation(QFileInfo(result).absolutePath());
}
return result.isEmpty() ? QScriptValue::NullValue : QScriptValue(result);
}
void WindowScriptingInterface::showAssetServer(const QString& upload) {
QMetaObject::invokeMethod(qApp, "showAssetServerWidget", Qt::QueuedConnection, Q_ARG(QString, upload));
}
@ -243,6 +278,10 @@ void WindowScriptingInterface::makeConnection(bool success, const QString& userN
}
}
void WindowScriptingInterface::displayAnnouncement(const QString& message) {
emit announcement(message);
}
bool WindowScriptingInterface::isPhysicsEnabled() {
return qApp->isPhysicsEnabled();
}

View file

@ -53,10 +53,12 @@ public slots:
CustomPromptResult customPrompt(const QVariant& config);
QScriptValue browse(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
QScriptValue browseAssets(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
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 displayAnnouncement(const QString& message);
void shareSnapshot(const QString& path, const QUrl& href = QUrl(""));
bool isPhysicsEnabled();
@ -78,6 +80,7 @@ signals:
void connectionAdded(const QString& connectionName);
void connectionError(const QString& errorString);
void announcement(const QString& message);
void messageBoxClosed(int id, int button);
@ -88,6 +91,9 @@ private:
QString getPreviousBrowseLocation() const;
void setPreviousBrowseLocation(const QString& location);
QString getPreviousBrowseAssetLocation() const;
void setPreviousBrowseAssetLocation(const QString& location);
void ensureReticleVisible() const;
int createMessageBox(QString title, QString text, int buttons, int defaultButton);

View file

@ -17,6 +17,7 @@
#include "DependencyManager.h"
#include "AddressManager.h"
#include "DialogsManager.h"
#include "LocationBookmarks.h"
HIFI_QML_DEF(AddressBarDialog)
@ -52,7 +53,8 @@ void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions)
void AddressBarDialog::loadHome() {
qDebug() << "Called LoadHome";
QString homeLocation = qApp->getBookmarks()->addressForBookmark(Bookmarks::HOME_BOOKMARK);
auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK);
const QString DEFAULT_HOME_LOCATION = "localhost";
if (homeLocation == "") {
homeLocation = DEFAULT_HOME_LOCATION;

View file

@ -94,7 +94,7 @@ void ApplicationOverlay::renderQmlUi(RenderArgs* renderArgs) {
PROFILE_RANGE(app, __FUNCTION__);
if (!_uiTexture) {
_uiTexture = gpu::TexturePointer(gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda()));
_uiTexture = gpu::Texture::createExternal(OffscreenQmlSurface::getDiscardLambda());
_uiTexture->setSource(__FUNCTION__);
}
// Once we move UI rendering and screen rendering to different
@ -207,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, gpu::Texture::SINGLE_MIP, DEFAULT_SAMPLER));
auto overlayDepthTexture = 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, gpu::Texture::SINGLE_MIP, OVERLAY_SAMPLER));
auto colorBuffer = gpu::Texture::createRenderBuffer(COLOR_FORMAT, width, height, gpu::Texture::SINGLE_MIP, OVERLAY_SAMPLER);
_overlayFramebuffer->setRenderBuffer(0, colorBuffer);
}
}

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