3
0
Fork 0
mirror of https://github.com/lubosz/overte.git synced 2025-04-27 21:55:28 +02:00

Merge remote-tracking branch 'upstream/master' into 21180-C++

This commit is contained in:
humbletim 2017-08-24 16:21:00 -04:00
commit 9348048387
417 changed files with 21281 additions and 4480 deletions
BUILD_WIN.mdCMakeLists.txtTest Plan 2.docx
assignment-client/src
cmake
domain-server
interface

View file

@ -3,7 +3,7 @@ This is a stand-alone guide for creating your first High Fidelity build for Wind
## Building High Fidelity
Note: We are now using Visual Studio 2017 and Qt 5.9.1. If you are upgrading from Visual Studio 2013 and Qt 5.6.2, do a clean uninstall of those versions before going through this guide.
Note: The prerequisites will require about 10 GB of space on your drive.
Note: The prerequisites will require about 10 GB of space on your drive. You will also need a system with at least 8GB of main memory.
### Step 1. Visual Studio 2017

View file

@ -15,10 +15,6 @@ if (WIN32)
cmake_policy(SET CMP0020 NEW)
endif (WIN32)
if (POLICY CMP0028)
cmake_policy(SET CMP0028 OLD)
endif ()
if (POLICY CMP0043)
cmake_policy(SET CMP0043 OLD)
endif ()
@ -83,7 +79,7 @@ endif(WIN32)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.3")
# GLM 0.9.8 on Ubuntu 14 (gcc 4.4) has issues with the simd declarations
# GLM 0.9.8 on Ubuntu 14 (gcc 4.4) has issues with the simd declarations
add_definitions(-DGLM_FORCE_PURE)
endif()
endif()

BIN
Test Plan 2.docx Normal file

Binary file not shown.

View file

@ -184,6 +184,9 @@ void Agent::run() {
// make sure we hear about connected nodes so we can grab an ATP script if a request is pending
connect(nodeList.data(), &LimitedNodeList::nodeActivated, this, &Agent::nodeActivated);
// make sure we hear about dissappearing nodes so we can clear the entity tree if an entity server goes away
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &Agent::nodeKilled);
nodeList->addSetOfNodeTypesToNodeInterestSet({
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer
});
@ -259,6 +262,13 @@ void Agent::nodeActivated(SharedNodePointer activatedNode) {
}
}
void Agent::nodeKilled(SharedNodePointer killedNode) {
if (killedNode->getType() == NodeType::EntityServer) {
// an entity server has gone away, ask the headless viewer to clear its tree
_entityViewer.clear();
}
}
void Agent::negotiateAudioFormat() {
auto nodeList = DependencyManager::get<NodeList>();
auto negotiateFormatPacket = NLPacket::create(PacketType::NegotiateAudioFormat);

View file

@ -77,6 +77,7 @@ private slots:
void handleSelectedAudioFormat(QSharedPointer<ReceivedMessage> message);
void nodeActivated(SharedNodePointer activatedNode);
void nodeKilled(SharedNodePointer killedNode);
void processAgentAvatar();
void processAgentAvatarAudio();
@ -109,7 +110,7 @@ private:
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
AudioGate _audioGate;
bool _audioGateOpen { false };
bool _audioGateOpen { true };
bool _isNoiseGateEnabled { false };
CodecPluginPointer _codec;

View file

@ -127,7 +127,7 @@ void AudioMixer::queueReplicatedAudioPacket(QSharedPointer<ReceivedMessage> mess
// construct a "fake" audio received message from the byte array and packet list information
auto audioData = message->getMessage().mid(NUM_BYTES_RFC4122_UUID);
PacketType rewrittenType = REPLICATED_PACKET_MAPPING.key(message->getType());
PacketType rewrittenType = PacketTypeEnum::getReplicatedPacketMapping().key(message->getType());
if (rewrittenType == PacketType::Unknown) {
qDebug() << "Cannot unwrap replicated packet type not present in REPLICATED_PACKET_WRAPPING";

View file

@ -125,11 +125,11 @@ void AudioMixerClientData::optionallyReplicatePacket(ReceivedMessage& message, c
// now make sure it's a packet type that we want to replicate
// first check if it is an original type that we should replicate
PacketType mirroredType = REPLICATED_PACKET_MAPPING.value(message.getType());
PacketType mirroredType = PacketTypeEnum::getReplicatedPacketMapping().value(message.getType());
if (mirroredType == PacketType::Unknown) {
// if it wasn't check if it is a replicated type that we should re-replicate
if (REPLICATED_PACKET_MAPPING.key(message.getType()) != PacketType::Unknown) {
if (PacketTypeEnum::getReplicatedPacketMapping().key(message.getType()) != PacketType::Unknown) {
mirroredType = message.getType();
} else {
qDebug() << "Packet passed to optionallyReplicatePacket was not a replicatable type - returning";

View file

@ -144,10 +144,10 @@ void AvatarMixer::optionallyReplicatePacket(ReceivedMessage& message, const Node
// check if this is a packet type we replicate
// which means it must be a packet type present in REPLICATED_PACKET_MAPPING or must be the
// replicated version of one of those packet types
PacketType replicatedType = REPLICATED_PACKET_MAPPING.value(message.getType());
PacketType replicatedType = PacketTypeEnum::getReplicatedPacketMapping().value(message.getType());
if (replicatedType == PacketType::Unknown) {
if (REPLICATED_PACKET_MAPPING.key(message.getType()) != PacketType::Unknown) {
if (PacketTypeEnum::getReplicatedPacketMapping().key(message.getType()) != PacketType::Unknown) {
replicatedType = message.getType();
} else {
qDebug() << __FUNCTION__ << "called without replicatable packet type - returning";

View file

@ -935,39 +935,7 @@ void OctreeServer::handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> m
// so here we just store a special file at our persist path
// and then force a stop of the server so that it can pick it up when it relaunches
if (!_persistAbsoluteFilePath.isEmpty()) {
// before we restart the server and make it try and load this data, let's make sure it is valid
auto compressedOctree = message->getMessage();
QByteArray jsonOctree;
// assume we have GZipped content
bool wasCompressed = gunzip(compressedOctree, jsonOctree);
if (!wasCompressed) {
// the source was not compressed, assume we were sent regular JSON data
jsonOctree = compressedOctree;
}
// check the JSON data to verify it is an object
if (QJsonDocument::fromJson(jsonOctree).isObject()) {
if (!wasCompressed) {
// source was not compressed, we compress it before we write it locally
gzip(jsonOctree, compressedOctree);
}
// write the compressed octree data to a special file
auto replacementFilePath = _persistAbsoluteFilePath.append(OctreePersistThread::REPLACEMENT_FILE_EXTENSION);
QFile replacementFile(replacementFilePath);
if (replacementFile.open(QIODevice::WriteOnly) && replacementFile.write(compressedOctree) != -1) {
// we've now written our replacement file, time to take the server down so it can
// process it when it comes back up
qInfo() << "Wrote octree replacement file to" << replacementFilePath << "- stopping server";
setFinished(true);
} else {
qWarning() << "Could not write replacement octree data to file - refusing to process";
}
} else {
qDebug() << "Received replacement octree file that is invalid - refusing to process";
}
replaceContentFromMessageData(message->getMessage());
} else {
qDebug() << "Cannot perform octree file replacement since current persist file path is not yet known";
}
@ -977,6 +945,68 @@ void OctreeServer::handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> m
}
}
// Message->getMessage() contains a QByteArray representation of the URL to download from
void OctreeServer::handleOctreeFileReplacementFromURL(QSharedPointer<ReceivedMessage> message) {
qInfo() << "Received request to replace content from a url";
if (!_isFinished && !_isShuttingDown) {
// This call comes from Interface, so we skip our domain server check
// but confirm that we have permissions to replace content sets
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
if (!_persistAbsoluteFilePath.isEmpty()) {
// Convert message data into our URL
QString url(message->getMessage());
QUrl modelsURL = QUrl(url, QUrl::StrictMode);
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest request(modelsURL);
QNetworkReply* reply = networkAccessManager.get(request);
connect(reply, &QNetworkReply::finished, [this, reply, modelsURL]() {
QNetworkReply::NetworkError networkError = reply->error();
if (networkError == QNetworkReply::NoError) {
QByteArray contents = reply->readAll();
replaceContentFromMessageData(contents);
} else {
qDebug() << "Error downloading JSON from specified file";
}
});
} else {
qDebug() << "Cannot perform octree file replacement since current persist file path is not yet known";
}
}
}
}
void OctreeServer::replaceContentFromMessageData(QByteArray content) {
//Assume we have compressed data
auto compressedOctree = content;
QByteArray jsonOctree;
bool wasCompressed = gunzip(compressedOctree, jsonOctree);
if (!wasCompressed) {
// the source was not compressed, assume we were sent regular JSON data
jsonOctree = compressedOctree;
}
// check the JSON data to verify it is an object
if (QJsonDocument::fromJson(jsonOctree).isObject()) {
if (!wasCompressed) {
// source was not compressed, we compress it before we write it locally
gzip(jsonOctree, compressedOctree);
}
// write the compressed octree data to a special file
auto replacementFilePath = _persistAbsoluteFilePath.append(OctreePersistThread::REPLACEMENT_FILE_EXTENSION);
QFile replacementFile(replacementFilePath);
if (replacementFile.open(QIODevice::WriteOnly) && replacementFile.write(compressedOctree) != -1) {
// we've now written our replacement file, time to take the server down so it can
// process it when it comes back up
qInfo() << "Wrote octree replacement file to" << replacementFilePath << "- stopping server";
setFinished(true);
} else {
qWarning() << "Could not write replacement octree data to file - refusing to process";
}
} else {
qDebug() << "Received replacement octree file that is invalid - refusing to process";
}
}
bool OctreeServer::readOptionBool(const QString& optionName, const QJsonObject& settingsSectionObject, bool& result) {
result = false; // assume it doesn't exist
bool optionAvailable = false;
@ -1202,6 +1232,7 @@ void OctreeServer::domainSettingsRequestComplete() {
packetReceiver.registerListener(PacketType::OctreeDataNack, this, "handleOctreeDataNackPacket");
packetReceiver.registerListener(PacketType::JurisdictionRequest, this, "handleJurisdictionRequestPacket");
packetReceiver.registerListener(PacketType::OctreeFileReplacement, this, "handleOctreeFileReplacement");
packetReceiver.registerListener(PacketType::OctreeFileReplacementFromUrl, this, "handleOctreeFileReplacementFromURL");
readConfiguration();

View file

@ -137,6 +137,7 @@ private slots:
void handleOctreeDataNackPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleJurisdictionRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
void handleOctreeFileReplacement(QSharedPointer<ReceivedMessage> message);
void handleOctreeFileReplacementFromURL(QSharedPointer<ReceivedMessage> message);
void removeSendThread();
protected:
@ -161,6 +162,8 @@ protected:
UniqueSendThread createSendThread(const SharedNodePointer& node);
virtual UniqueSendThread newSendThread(const SharedNodePointer& node);
void replaceContentFromMessageData(QByteArray content);
int _argc;
const char** _argv;
char** _parsedArgV;

View file

@ -2,57 +2,30 @@ set(EXTERNAL_NAME tbb)
include(ExternalProject)
if (ANDROID)
find_program(NDK_BUILD_COMMAND NAMES ndk-build DOC "Path to the ndk-build command")
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150316oss_src.tgz
URL_MD5 bf090eaa86cf89ea014b7b462786a440
BUILD_COMMAND ${NDK_BUILD_COMMAND} --directory=jni target=android tbb tbbmalloc arch=arm
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ""
INSTALL_COMMAND ${CMAKE_COMMAND} -DTBB_LIBS_SUFFIX=so -P ${CMAKE_CURRENT_SOURCE_DIR}/TBBLibCopy.cmake
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
if (WIN32)
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_win_slim.zip)
set(DOWNLOAD_MD5 065934458e3db88397f3d10e7eea536c)
elseif (APPLE)
find_program(MAKE_COMMAND NAMES make DOC "Path to the make command")
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/tbb43_20150316oss_src.tgz
URL_MD5 bf090eaa86cf89ea014b7b462786a440
BUILD_COMMAND ${MAKE_COMMAND} tbb_os=macos
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ""
INSTALL_COMMAND ${CMAKE_COMMAND} -DTBB_LIBS_SUFFIX=dylib -P ${CMAKE_CURRENT_SOURCE_DIR}/TBBLibCopy.cmake
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb2017_20170604oss_mac_slim.tar.gz)
set(DOWNLOAD_MD5 62bde626b396f8e1a85c6a8ded1d8105)
elseif (ANDROID)
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_and_slim.tar.gz)
set(DOWNLOAD_MD5 04d50b64e1d81245a1be5f75f34d64c7)
else ()
if (WIN32)
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150316oss_win.zip)
set(DOWNLOAD_MD5 d250d40bb93b255f75bcbb19e976a440)
else ()
set(DOWNLOAD_URL http://s3.amazonaws.com/hifi-public/dependencies/tbb43_20150316oss_lin.tgz)
set(DOWNLOAD_MD5 7830ba2bc62438325fba2ec0c95367a5)
endif ()
ExternalProject_Add(
${EXTERNAL_NAME}
URL ${DOWNLOAD_URL}
URL_MD5 ${DOWNLOAD_MD5}
BUILD_COMMAND ""
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD ON
)
set(DOWNLOAD_URL http://hifi-public.s3.amazonaws.com/dependencies/tbb2017_20170604oss_lin_slim.tar.gz)
set(DOWNLOAD_MD5 2a5c721f40fa3503ffc12c18dd00011c)
endif ()
ExternalProject_Add(
${EXTERNAL_NAME}
URL ${DOWNLOAD_URL}
URL_MD5 ${DOWNLOAD_MD5}
BUILD_COMMAND ""
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD ON
)
# Hide this external target (for ide users)
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
@ -70,22 +43,32 @@ if (APPLE)
change-install-name
COMMENT "Calling install_name_tool on TBB libraries to fix install name for dylib linking"
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_TBB_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
DEPENDEES install
DEPENDEES download
WORKING_DIRECTORY <SOURCE_DIR>
LOG 1
)
elseif (WIN32)
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/intel64/vc12")
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/intel64/vc12" CACHE PATH "Path to TBB DLLs")
if (MSVC_VERSION GREATER_EQUAL 1900)
set(_TBB_MSVC_DIR "vc14")
elseif (MSVC_VERSION GREATER_EQUAL 1800)
set(_TBB_MSVC_DIR "vc12")
elseif (MSVC_VERSION GREATER_EQUAL 1700)
set(_TBB_MSVC_DIR "vc11")
else()
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/ia32/vc12")
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/ia32/vc12" CACHE PATH "Path to TBB DLLs")
message(FATAL_ERROR "MSVC ${MSVC_VERSION} not supported by Intel TBB")
endif()
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/intel64/${_TBB_MSVC_DIR}")
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/intel64/${_TBB_MSVC_DIR}" CACHE PATH "Path to TBB DLLs")
else()
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/ia32/${_TBB_MSVC_DIR}")
set(${EXTERNAL_NAME_UPPER}_DLL_PATH "${SOURCE_DIR}/bin/ia32/${_TBB_MSVC_DIR}" CACHE PATH "Path to TBB DLLs")
endif()
set(_LIB_EXT "lib")
elseif (ANDROID)
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib")
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/android")
set(_LIB_PREFIX "lib")
set(_LIB_EXT "so")
elseif (UNIX)
@ -103,15 +86,15 @@ elseif (UNIX)
OUTPUT_VARIABLE GCC_VERSION
)
if (GCC_VERSION VERSION_GREATER 4.4 OR GCC_VERSION VERSION_EQUAL 4.4)
if (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/${_TBB_ARCH_DIR}/gcc4.7")
elseif (GCC_VERSION VERSION_GREATER 4.4 OR GCC_VERSION VERSION_EQUAL 4.4)
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/${_TBB_ARCH_DIR}/gcc4.4")
elseif (GCC_VERSION VERSION_GREATER 4.1 OR GCC_VERSION VERSION_EQUAL 4.1)
set(_TBB_LIB_DIR "${SOURCE_DIR}/lib/${_TBB_ARCH_DIR}/gcc4.1")
else ()
message(STATUS "Could not find a compatible version of Threading Building Blocks library for your compiler.")
endif ()
endif ()
if (DEFINED _TBB_LIB_DIR)

View file

@ -6,8 +6,8 @@ if (WIN32)
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi8.zip
URL_MD5 b01510437ea15527156bc25cdf733bd9
URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi9.zip
URL_MD5 94f4765bdbcd53cd099f349ae031e769
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View file

@ -22,23 +22,17 @@ macro(GENERATE_INSTALLERS)
set(CPACK_PACKAGE_FILE_NAME "HighFidelity-Beta-${BUILD_VERSION}")
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME})
if (PR_BUILD)
set(CPACK_NSIS_COMPRESSOR "/SOLID bzip2")
endif ()
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${_DISPLAY_NAME})
if (WIN32)
# include CMake module that will install compiler system libraries
# so that we have msvcr120 and msvcp120 installed with targets
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${INTERFACE_INSTALL_DIR})
# as long as we're including sixense plugin with installer
# we need re-distributables for VS 2011 as well
# this should be removed if/when sixense support is pulled
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS
"${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcr100.dll"
"${EXTERNALS_BINARY_DIR}/sixense/project/src/sixense/samples/win64/msvcp100.dll"
)
# Do not install the Visual Studio C runtime libraries. The installer will do this automatically
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
include(InstallRequiredSystemLibraries)
set(CPACK_NSIS_MUI_ICON "${HF_CMAKE_DIR}/installer/installer.ico")
# install and reference the Add/Remove icon
@ -90,3 +84,4 @@ macro(GENERATE_INSTALLERS)
include(CPack)
endmacro()

View file

@ -22,9 +22,12 @@ macro(install_beside_console)
else ()
# setup install of executable and things copied by fixup/windeployqt
install(
FILES "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
DIRECTORY "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
DESTINATION ${COMPONENT_INSTALL_DIR}
COMPONENT ${SERVER_COMPONENT}
PATTERN "*.pdb" EXCLUDE
PATTERN "*.lib" EXCLUDE
PATTERN "*.exp" EXCLUDE
)
# on windows for PR and production builds, sign the executable

View file

@ -111,14 +111,14 @@ macro(SET_PACKAGING_PARAMETERS)
# shortcut names
if (PRODUCTION_BUILD)
set(INTERFACE_SHORTCUT_NAME "Interface")
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface")
set(CONSOLE_SHORTCUT_NAME "Sandbox")
else ()
set(INTERFACE_SHORTCUT_NAME "Interface - ${BUILD_VERSION}")
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface - ${BUILD_VERSION}")
set(CONSOLE_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION}")
endif ()
set(INTERFACE_HF_SHORTCUT_NAME "High Fidelity ${INTERFACE_SHORTCUT_NAME}")
set(INTERFACE_HF_SHORTCUT_NAME "${INTERFACE_SHORTCUT_NAME}")
set(CONSOLE_HF_SHORTCUT_NAME "High Fidelity ${CONSOLE_SHORTCUT_NAME}")
set(PRE_SANDBOX_INTERFACE_SHORTCUT_NAME "High Fidelity")

View file

@ -1,6 +1,6 @@
#
#
# FindTBB.cmake
#
#
# Try to find the Intel Threading Building Blocks library
#
# You can provide a TBB_ROOT_DIR which contains lib and include directories
@ -16,7 +16,7 @@
#
# 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("tbb")
@ -34,31 +34,43 @@ elseif (UNIX AND NOT ANDROID)
else()
set(_TBB_ARCH_DIR "ia32")
endif()
execute_process(
COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION
)
if (GCC_VERSION VERSION_GREATER 4.4 OR GCC_VERSION VERSION_EQUAL 4.4)
if (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)
set(_TBB_LIB_DIR "lib/${_TBB_ARCH_DIR}/gcc4.7")
elseif (GCC_VERSION VERSION_GREATER 4.4 OR GCC_VERSION VERSION_EQUAL 4.4)
set(_TBB_LIB_DIR "lib/${_TBB_ARCH_DIR}/gcc4.4")
elseif (GCC_VERSION VERSION_GREATER 4.1 OR GCC_VERSION VERSION_EQUAL 4.1)
set(_TBB_LIB_DIR "lib/${_TBB_ARCH_DIR}/gcc4.1")
else ()
message(FATAL_ERROR "Could not find a compatible version of Threading Building Blocks library for your compiler.")
endif ()
elseif (WIN32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_TBB_ARCH_DIR "intel64")
else()
set(_TBB_ARCH_DIR "ia32")
endif()
set(_TBB_LIB_DIR "lib/${_TBB_ARCH_DIR}/vc12")
find_path(TBB_DLL_PATH tbb_debug.dll PATH_SUFFIXES "bin/${_TBB_ARCH_DIR}/vc12" HINTS ${TBB_SEARCH_DIRS})
if (MSVC_VERSION GREATER_EQUAL 1900)
set(_TBB_MSVC_DIR "vc14")
elseif (MSVC_VERSION GREATER_EQUAL 1800)
set(_TBB_MSVC_DIR "vc12")
elseif (MSVC_VERSION GREATER_EQUAL 1700)
set(_TBB_MSVC_DIR "vc11")
else()
message(FATAL_ERROR "MSVC ${MSVC_VERSION} not supported by Intel TBB")
endif()
set(_TBB_LIB_DIR "lib/${_TBB_ARCH_DIR}/${_TBB_MSVC_DIR}")
find_path(TBB_DLL_PATH tbb_debug.dll PATH_SUFFIXES "bin/${_TBB_ARCH_DIR}/${_TBB_MSVC_DIR}" HINTS ${TBB_SEARCH_DIRS})
elseif (ANDROID)
set(_TBB_DEFAULT_INSTALL_DIR "/tbb")
set(_TBB_LIB_NAME "tbb")

View file

@ -11,34 +11,28 @@
include(BundleUtilities)
# replace copy_resolved_item_into_bundle
#
# The official version of copy_resolved_item_into_bundle will print out a "warning:" when
# the resolved item matches the resolved embedded item. This not not really an issue that
# should rise to the level of a "warning" so we replace this message with a "status:"
#
function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
if (WIN32)
# ignore case on Windows
string(TOLOWER "${resolved_item}" resolved_item_compare)
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
else()
set(resolved_item_compare "${resolved_item}")
set(resolved_embedded_item_compare "${resolved_embedded_item}")
function(gp_resolved_file_type_override resolved_file type_var)
if( file MATCHES ".*VCRUNTIME140.*" )
set(type "system" PARENT_SCOPE)
endif()
if ("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
# this is our only change from the original version
message(STATUS "status: resolved_item == resolved_embedded_item - not copying...")
else()
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
if(UNIX AND NOT APPLE)
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
endif()
if( file MATCHES ".*concrt140.*" )
set(type "system" PARENT_SCOPE)
endif()
if( file MATCHES ".*msvcp140.*" )
set(type "system" PARENT_SCOPE)
endif()
if( file MATCHES ".*vcruntime140.*" )
set(type "system" PARENT_SCOPE)
endif()
if( file MATCHES ".*api-ms-win-crt-conio.*" )
set(type "system" PARENT_SCOPE)
endif()
if( file MATCHES ".*api-ms-win-core-winrt.*" )
set(type "system" PARENT_SCOPE)
endif()
endfunction()
message(STATUS "FIXUP_LIBS for fixup_bundle called for bundle ${BUNDLE_EXECUTABLE} are @FIXUP_LIBS@")
message(STATUS "Scanning for plugins from ${BUNDLE_PLUGIN_DIR}")
@ -52,3 +46,4 @@ endif()
file(GLOB EXTRA_PLUGINS "${BUNDLE_PLUGIN_DIR}/*.${PLUGIN_EXTENSION}")
fixup_bundle("${BUNDLE_EXECUTABLE}" "${EXTRA_PLUGINS}" "@FIXUP_LIBS@")

View file

@ -484,16 +484,14 @@ Function InstallTypesPage
StrCpy $OffsetUnits u
StrCpy $Express "0"
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls High Fidelity Interface and High Fidelity Sandbox"
pop $ExpressInstallRadioButton
${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel
IntOp $CurrentOffset $CurrentOffset + 15
${NSD_CreateRadioButton} 30% $CurrentOffset$OffsetUnits 100% 10u "Express Install (Recommended)"; $\nInstalls High Fidelity Interface and High Fidelity Sandbox"
pop $ExpressInstallRadioButton
${NSD_OnClick} $ExpressInstallRadioButton ChangeExpressLabel
IntOp $CurrentOffset $CurrentOffset + 15
${NSD_CreateRadiobutton} 30% $CurrentOffset$OffsetUnits 100% 10u "Custom Install (Advanced)"
pop $CustomInstallRadioButton
${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel
${EndIf}
${NSD_CreateRadiobutton} 30% $CurrentOffset$OffsetUnits 100% 10u "Custom Install (Advanced)"
pop $CustomInstallRadioButton
${NSD_OnClick} $CustomInstallRadioButton ChangeCustomLabel
; Express Install selected by default
${NSD_Check} $ExpressInstallRadioButton
@ -518,7 +516,6 @@ FunctionEnd
Function AbortFunction
; Check if Express is set, if so, abort the post install options page
Call HandleInstallTypes ; Sets Express if ExpressInstallRadioButton is checked and installs with defaults
StrCmp $Express "1" 0 end
Abort
end:
@ -529,10 +526,15 @@ Function PostInstallOptionsPage
nsDialogs::Create 1018
Pop $PostInstallDialog
${If} $PostInstallDialog == error
Abort
${EndIf}
; Check if Express is set, if so, abort the post install options page
StrCmp $Express "1" 0 end
Abort
end:
StrCpy $CurrentOffset 0
StrCpy $OffsetUnits u
@ -617,12 +619,6 @@ Function PostInstallOptionsPage
${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED}
${EndIf}
; Check if Express is set, if so, abort the post install options page
Call HandleInstallTypes ; Sets Express if ExpressInstallRadioButton is checked and installs with defaults
StrCmp $Express "1" 0 end
Abort
end:
nsDialogs::Show
FunctionEnd
@ -638,15 +634,30 @@ Var LaunchServerNowState
Var LaunchClientNowState
Var CopyFromProductionState
Var CleanInstallState
Var ExpressInstallState
Var ExpressInstallState
Var CustomInstallState
Function ReadInstallTypes
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
; check if the user asked for express/custom install
${NSD_GetState} $ExpressInstallRadioButton $ExpressInstallState
${NSD_GetState} $CustomInstallRadioButton $CustomInstallState
; check if the user asked for express/custom install
${NSD_GetState} $ExpressInstallRadioButton $ExpressInstallState
${NSD_GetState} $CustomInstallRadioButton $CustomInstallState
${If} $ExpressInstallState == ${BST_CHECKED}
StrCpy $Express "1"
StrCpy $DesktopClientState ${BST_CHECKED}
StrCpy $ServerStartupState ${BST_CHECKED}
StrCpy $LaunchServerNowState ${BST_CHECKED}
StrCpy $LaunchClientNowState ${BST_CHECKED}
StrCpy $CleanInstallState ${BST_UNCHECKED}
StrCpy $DesktopServerState ${BST_UNCHECKED}
${If} @PR_BUILD@ == 1
StrCpy $CopyFromProductionState ${BST_UNCHECKED}
${EndIf}
${EndIf}
FunctionEnd
Function ReadPostInstallOptions
@ -684,28 +695,6 @@ Function ReadPostInstallOptions
${EndIf}
FunctionEnd
Function HandleInstallTypes
${If} $ExpressInstallState == ${BST_CHECKED}
StrCpy $Express "1"
; over ride custom checkboxes and select defaults
${NSD_SetState} $DesktopClientCheckbox ${BST_CHECKED}
${NSD_SetState} $ServerStartupCheckbox ${BST_CHECKED}
${NSD_SetState} $LaunchServerNowCheckbox ${BST_CHECKED}
${NSD_SetState} $LaunchClientNowCheckbox ${BST_CHECKED}
${If} @PR_BUILD@ == 1
${NSD_SetState} $CopyFromProductionCheckbox ${BST_UNCHECKED}
${EndIf}
; call ReadPostInstallOptions and HandlePostInstallOptions with defaults selected
Call ReadPostInstallOptions
Call HandlePostInstallOptions
${EndIf}
FunctionEnd
Function HandlePostInstallOptions
${If} ${SectionIsSelected} ${@CLIENT_COMPONENT_NAME@}
; check if the user asked for a desktop shortcut to High Fidelity
@ -853,6 +842,8 @@ Section "-Core installation"
; Rename the incorrectly cased Raleway font
Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml"
ExecWait "$INSTDIR\vcredist_x64.exe /install /q /norestart"
; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console)
RMDir /r "$INSTDIR\Interface"
Delete "$INSTDIR\vcredist_x64.exe"
@ -952,9 +943,9 @@ Section "-Core installation"
Call ConditionalAddToRegisty
!insertmacro MUI_STARTMENU_WRITE_END
@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@
@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@
; Handle whichever post install options were set
Call HandlePostInstallOptions

View file

@ -1,5 +1,5 @@
{
"version": 1.7,
"version": 1.8,
"settings": [
{
"name": "metaverse",
@ -112,7 +112,6 @@
"label": "Adult (18+)"
}
]
},
{
"name": "hosts",
@ -161,15 +160,15 @@
"label": "HTTP Password",
"type": "password",
"help": "Password used for basic HTTP authentication. Leave this alone if you do not want to change it.",
"password_placeholder" : "******",
"password_placeholder": "******",
"value-hidden": true
},
{
"name": "verify_http_password",
"label": "Verify HTTP Password",
"type": "password",
"help": "Must match the password entered above for change to be saved.",
"value-hidden": true
"name": "verify_http_password",
"label": "Verify HTTP Password",
"type": "password",
"help": "Must match the password entered above for change to be saved.",
"value-hidden": true
},
{
"name": "maximum_user_capacity",
@ -208,21 +207,19 @@
"name": "standard_permissions",
"type": "table",
"label": "Domain-Wide User Permissions",
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let&rsquo;s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
"help": "Indicate which types of users can have which <a data-toggle='tooltip' data-html=true title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let&rsquo;s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>domain-wide permissions</a>.",
"caption": "Standard Permissions",
"can_add_new_rows": false,
"groups": [
{
"label": "Type of User",
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let&rsquo;s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
"span": 7
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level permissions that might otherwise apply to that user. Additionally, if more than one parameter is applicable to a given user, the permissions given to that user will be the sum of all applicable parameters. For example, let&rsquo;s say only localhost users can connect and only logged in users can lock and unlock entities. If a user is both logged in and on localhost then they will be able to both connect and lock/unlock entities.</p>'>?</a>",
"span": 8
}
],
"columns": [
{
"name": "permissions_id",
@ -276,9 +273,15 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_replace_content",
"label": "Replace Content",
"type": "checkbox",
"editable": true,
"default": false
}
],
"non-deletable-row-key": "permissions_id",
"non-deletable-row-values": ["localhost", "anonymous", "logged-in"]
},
@ -291,18 +294,16 @@
"can_add_new_rows": false,
"new_category_placeholder": "Add Group",
"new_category_message": "Save and reload to see ranks",
"groups": [
{
"label": "Rank",
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn&rsquo;t have their own row in the per-account section, below.</p>'>?</a>",
"span": 7
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in, as well as permissions from the previous section. Group permissions are only granted if the user doesn&rsquo;t have their own row in the per-account section, below.</p>'>?</a>",
"span": 8
}
],
"columns": [
{
"name": "permissions_id",
@ -381,6 +382,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_replace_content",
"label": "Replace Content",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
@ -393,18 +401,16 @@
"can_add_new_rows": false,
"new_category_placeholder": "Add Blacklist Group",
"new_category_message": "Save and reload to see ranks",
"groups": [
{
"label": "Rank",
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn&rsquo;t have their own row in the per-account section, below.</p>'>?</a>",
"span": 7
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users in specific groups can replace entire content sets by wiping existing domain content.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn&rsquo;t have their own row in the per-account section, below.</p>'>?</a>",
"span": 8
}
],
"columns": [
{
"name": "permissions_id",
@ -480,6 +486,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_replace_content",
"label": "Replace Content",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
@ -488,18 +501,16 @@
"type": "table",
"caption": "Permissions for Specific Users",
"can_add_new_rows": true,
"groups": [
{
"label": "User",
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
"span": 7
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether a user can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether a user change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether a user can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether a user can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether a user can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether a user can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether a user can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific user will supersede any parameter-level or group permissions that might otherwise apply to that user.</p>'>?</a>",
"span": 8
}
],
"columns": [
{
"name": "permissions_id",
@ -553,6 +564,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_replace_content",
"label": "Replace Content",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
@ -567,11 +585,10 @@
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 7
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide IP Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users from specific IPs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific IPs can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users from specific IPs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users from specific IPs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users from specific IPs can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users from specific IPs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users from specific IPs can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific IP will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). IP address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 8
}
],
"columns": [
{
"name": "permissions_id",
@ -625,6 +642,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_replace_content",
"label": "Replace Content",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
@ -639,11 +663,10 @@
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 7
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide MAC Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific MACs can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific MACs can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific MACs can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific MACs can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific MACs can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific MACs can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific MACs can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific MAC will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). MAC address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 8
}
],
"columns": [
{
"name": "permissions_id",
@ -697,6 +720,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_replace_content",
"label": "Replace Content",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
@ -711,11 +741,10 @@
"span": 1
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 7
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide Machine Fingerprint Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users with specific Machine Fingerprints can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users from specific Machine Fingerprints can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users with specific Machine Fingerprints can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users with specific Machine Fingerprints can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users with specific Machine Fingerprints can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether users with specific Machine Fingerprints can connect even if the domain has reached or exceeded its maximum allowed agents.</li><li><strong>Replace Content</strong><br>Sets whether users with specific Machine Fingerprints can replace entire content sets by wiping existing domain content.</li></ul><p>Note that permissions assigned to a specific Machine Fingerprint will supersede any parameter-level permissions that might otherwise apply to that user (from groups or standard permissions above). Machine Fingerprint address permissions are overriden if the user has their own row in the users section.</p>'>?</a>",
"span": 8
}
],
"columns": [
{
"name": "permissions_id",
@ -769,6 +798,13 @@
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_replace_content",
"label": "Replace Content",
"type": "checkbox",
"editable": true,
"default": false
}
]
}
@ -784,7 +820,6 @@
"label": "Persistent Scripts",
"help": "Add the URLs for scripts that you would like to ensure are always running in your domain.",
"can_add_new_rows": true,
"columns": [
{
"name": "url",
@ -946,7 +981,6 @@
"help": "In this table you can define a set of zones in which you can specify various audio properties.",
"numbered": false,
"can_add_new_rows": true,
"key": {
"name": "name",
"label": "Name",
@ -999,7 +1033,6 @@
"numbered": true,
"can_order": true,
"can_add_new_rows": true,
"columns": [
{
"name": "source",
@ -1028,7 +1061,6 @@
"help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet/dry mix of 25%. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet/dry mix of 50%.",
"numbered": true,
"can_add_new_rows": true,
"columns": [
{
"name": "zone",
@ -1063,7 +1095,9 @@
{
"name": "audio_buffer",
"label": "Audio Buffers",
"assignment-types": [0],
"assignment-types": [
0
],
"settings": [
{
"name": "dynamic_jitter_buffer",
@ -1082,35 +1116,37 @@
"advanced": true
},
{
"name": "max_frames_over_desired",
"deprecated": true
"name": "max_frames_over_desired",
"deprecated": true
},
{
"name": "window_starve_threshold",
"deprecated": true
"name": "window_starve_threshold",
"deprecated": true
},
{
"name": "window_seconds_for_desired_calc_on_too_many_starves",
"deprecated": true
"name": "window_seconds_for_desired_calc_on_too_many_starves",
"deprecated": true
},
{
"name": "window_seconds_for_desired_reduction",
"deprecated": true
"name": "window_seconds_for_desired_reduction",
"deprecated": true
},
{
"name": "use_stdev_for_desired_calc",
"deprecated": true
"name": "use_stdev_for_desired_calc",
"deprecated": true
},
{
"name": "repetition_with_fade",
"deprecated": true
"name": "repetition_with_fade",
"deprecated": true
}
]
},
{
"name": "entity_server_settings",
"label": "Entity Server Settings",
"assignment-types": [6],
"assignment-types": [
6
],
"settings": [
{
"name": "maxTmpLifetime",
@ -1167,13 +1203,32 @@
"help": "In this table you can define a set of rules for how frequently to backup copies of your entites content file.",
"numbered": false,
"can_add_new_rows": true,
"default": [
{"Name":"Half Hourly Rolling","backupInterval":1800,"format":".backup.halfhourly.%N","maxBackupVersions":5},
{"Name":"Daily Rolling","backupInterval":86400,"format":".backup.daily.%N","maxBackupVersions":7},
{"Name":"Weekly Rolling","backupInterval":604800,"format":".backup.weekly.%N","maxBackupVersions":4},
{"Name":"Thirty Day Rolling","backupInterval":2592000,"format":".backup.thirtyday.%N","maxBackupVersions":12}
],
"default": [
{
"Name": "Half Hourly Rolling",
"backupInterval": 1800,
"format": ".backup.halfhourly.%N",
"maxBackupVersions": 5
},
{
"Name": "Daily Rolling",
"backupInterval": 86400,
"format": ".backup.daily.%N",
"maxBackupVersions": 7
},
{
"Name": "Weekly Rolling",
"backupInterval": 604800,
"format": ".backup.weekly.%N",
"maxBackupVersions": 4
},
{
"Name": "Thirty Day Rolling",
"backupInterval": 2592000,
"format": ".backup.thirtyday.%N",
"maxBackupVersions": 12
}
],
"columns": [
{
"name": "Name",
@ -1309,7 +1364,9 @@
{
"name": "avatar_mixer",
"label": "Avatar Mixer",
"assignment-types": [1],
"assignment-types": [
1
],
"settings": [
{
"name": "max_node_send_bandwidth",
@ -1362,7 +1419,10 @@
{
"name": "downstream_servers",
"label": "Receiving Servers",
"assignment-types": [0,1],
"assignment-types": [
0,
1
],
"type": "table",
"advanced": true,
"can_add_new_rows": true,
@ -1402,7 +1462,10 @@
{
"name": "upstream_servers",
"label": "Broadcasting Servers",
"assignment-types": [0,1],
"assignment-types": [
0,
1
],
"type": "table",
"advanced": true,
"can_add_new_rows": true,
@ -1442,4 +1505,4 @@
]
}
]
}
}

View file

@ -54,6 +54,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
if (message->getSize() == 0) {
return;
}
QDataStream packetStream(message->getMessage());
// read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it
@ -269,6 +270,7 @@ void DomainGatekeeper::updateNodePermissions() {
userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities;
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities;
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
} else {
// this node is an agent
const QHostAddress& addr = node->getLocalSocket().getAddress();
@ -357,6 +359,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities;
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities;
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
newNode->setPermissions(userPerms);
return newNode;
}

View file

@ -458,7 +458,7 @@ const QString DISABLED_AUTOMATIC_NETWORKING_VALUE = "disabled";
bool DomainServer::packetVersionMatch(const udt::Packet& packet) {
bool DomainServer::isPacketVerified(const udt::Packet& packet) {
PacketType headerType = NLPacket::typeInHeader(packet);
PacketVersion headerVersion = NLPacket::versionInHeader(packet);
@ -471,7 +471,48 @@ bool DomainServer::packetVersionMatch(const udt::Packet& packet) {
DomainGatekeeper::sendProtocolMismatchConnectionDenial(packet.getSenderSockAddr());
}
// let the normal nodeList implementation handle all other packets.
if (!PacketTypeEnum::getNonSourcedPackets().contains(headerType)) {
// this is a sourced packet - first check if we have a node that matches
QUuid sourceID = NLPacket::sourceIDInHeader(packet);
SharedNodePointer sourceNode = nodeList->nodeWithUUID(sourceID);
if (sourceNode) {
// unverified DS packets (due to a lack of connection secret between DS + node)
// must come either from the same public IP address or a local IP address (set by RFC 1918)
DomainServerNodeData* nodeData = static_cast<DomainServerNodeData*>(sourceNode->getLinkedData());
bool exactAddressMatch = nodeData->getSendingSockAddr() == packet.getSenderSockAddr();
bool bothPrivateAddresses = nodeData->getSendingSockAddr().hasPrivateAddress()
&& packet.getSenderSockAddr().hasPrivateAddress();
if (nodeData && (exactAddressMatch || bothPrivateAddresses)) {
// to the best of our ability we've verified that this packet comes from the right place
// let the NodeList do its checks now (but pass it the sourceNode so it doesn't need to look it up again)
return nodeList->isPacketVerifiedWithSource(packet, sourceNode.data());
} else {
static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unmatched IP for UUID";
static QString repeatedMessage
= LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX);
qDebug() << "Packet of type" << headerType
<< "received from unmatched IP for UUID" << uuidStringWithoutCurlyBraces(sourceID);
return false;
}
} else {
static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown node with UUID";
static QString repeatedMessage
= LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX);
qDebug() << "Packet of type" << headerType
<< "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID);
return false;
}
}
// fallback to allow the normal NodeList implementation to verify packets
return nodeList->isPacketVerified(packet);
}
@ -570,7 +611,7 @@ void DomainServer::setupNodeListAndAssignments() {
addStaticAssignmentsToQueue();
// set a custom packetVersionMatch as the verify packet operator for the udt::Socket
nodeList->setPacketFilterOperator(&DomainServer::packetVersionMatch);
nodeList->setPacketFilterOperator(&DomainServer::isPacketVerified);
}
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
@ -853,7 +894,6 @@ void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Ass
}
void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
QDataStream packetStream(message->getMessage());
NodeConnectionData nodeRequestData = NodeConnectionData::fromDataStream(packetStream, message->getSenderSockAddr(), false);

View file

@ -71,6 +71,7 @@ public slots:
void restart();
private slots:
void processRequestAssignmentPacket(QSharedPointer<ReceivedMessage> packet);
void processListRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void processNodeJSONStatsPacket(QSharedPointer<ReceivedMessage> packetList, SharedNodePointer sendingNode);
@ -79,7 +80,6 @@ public slots:
void processICEServerHeartbeatDenialPacket(QSharedPointer<ReceivedMessage> message);
void processICEServerHeartbeatACK(QSharedPointer<ReceivedMessage> message);
private slots:
void setupPendingAssignmentCredits();
void sendPendingTransactionsToServer();
@ -129,7 +129,7 @@ private:
void getTemporaryName(bool force = false);
static bool packetVersionMatch(const udt::Packet& packet);
static bool isPacketVerified(const udt::Packet& packet);
bool resetAccountManagerAccessToken();

View file

@ -112,6 +112,7 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
const QString RESTRICTED_ACCESS_SETTINGS_KEYPATH = "security.restricted_access";
const QString ALLOWED_EDITORS_SETTINGS_KEYPATH = "security.allowed_editors";
const QString EDITORS_ARE_REZZERS_KEYPATH = "security.editors_are_rezzers";
const QString EDITORS_CAN_REPLACE_CONTENT_KEYPATH = "security.editors_can_replace_content";
qDebug() << "Previous domain-server settings version was"
<< QString::number(oldVersion, 'g', 8) << "and the new version is"
@ -294,6 +295,13 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
// persist the new config so the user config file has the correctly merged config
persistToFile();
}
if (oldVersion < 1.8) {
unpackPermissions();
// This was prior to addition of domain content replacement, add that to localhost permissions by default
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canReplaceDomainContent);
packPermissions();
}
}
unpackPermissions();

View file

@ -18,6 +18,7 @@ find_package(Qt5LinguistTools REQUIRED)
find_package(Qt5LinguistToolsMacros)
if (WIN32)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -bigobj")
add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h
add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines
endif()
@ -183,6 +184,12 @@ if (WIN32)
add_dependency_external_projects(steamworks)
endif()
# include OPENSSL
include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}")
# append OpenSSL to our list of libraries to link
target_link_libraries(${TARGET_NAME} ${OPENSSL_LIBRARIES})
# disable /OPT:REF and /OPT:ICF for the Debug builds
# This will prevent the following linker warnings
# LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification
@ -309,9 +316,12 @@ else (APPLE)
# setup install of executable and things copied by fixup/windeployqt
install(
FILES "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
DIRECTORY "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
DESTINATION ${INTERFACE_INSTALL_DIR}
COMPONENT ${CLIENT_COMPONENT}
PATTERN "*.pdb" EXCLUDE
PATTERN "*.lib" EXCLUDE
PATTERN "*.exp" EXCLUDE
)
set(SCRIPTS_INSTALL_DIR "${INTERFACE_INSTALL_DIR}")

View file

@ -126,24 +126,6 @@
"weightVar": "headWeight",
"weight": 4.0,
"flexCoefficients": [1, 0.5, 0.25, 0.2, 0.1]
},
{
"jointName": "LeftArm",
"positionVar": "leftArmPosition",
"rotationVar": "leftArmRotation",
"typeVar": "leftArmType",
"weightVar": "leftArmWeight",
"weight": 0.75,
"flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0]
},
{
"jointName": "RightArm",
"positionVar": "rightArmPosition",
"rotationVar": "rightArmRotation",
"typeVar": "rightArmType",
"weightVar": "rightArmWeight",
"weight": 0.75,
"flexCoefficients": [1.0, 0.35, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0]
}
]
},

View file

@ -15,7 +15,7 @@
{ "comment" : "Mouse turn need to be small continuous increments",
"from": { "makeAxis" : [
[ "Keyboard.MouseMoveLeft" ],
[ "Keyboard.MouseMoveRight" ]
[ "Keyboard.MouseMoveRight" ]
]
},
"when": [ "Application.InHMD", "Application.SnapTurn", "Keyboard.RightMouseButton" ],
@ -31,8 +31,8 @@
{ "comment" : "Touchpad turn need to be small continuous increments, but without the RMB constraint",
"from": { "makeAxis" : [
[ "Keyboard.TouchpadLeft" ],
[ "Keyboard.TouchpadRight" ]
]
[ "Keyboard.TouchpadRight" ]
]
},
"when": [ "Application.InHMD", "Application.SnapTurn" ],
"to": "Actions.StepYaw",
@ -131,6 +131,6 @@
{ "from": "Keyboard.Space", "to": "Actions.SHIFT" },
{ "from": "Keyboard.R", "to": "Actions.ACTION1" },
{ "from": "Keyboard.T", "to": "Actions.ACTION2" },
{ "from": "Keyboard.RightMouseClicked", "to": "Actions.ContextMenu" }
{ "from": "Keyboard.Tab", "to": "Actions.ContextMenu" }
]
}

View file

@ -109,6 +109,23 @@
{ "from": "Standard.Head", "to": "Actions.Head" },
{ "from": "Standard.LeftArm", "to": "Actions.LeftArm" },
{ "from": "Standard.RightArm", "to": "Actions.RightArm" }
{ "from": "Standard.RightArm", "to": "Actions.RightArm" },
{ "from": "Standard.TrackedObject00", "to" : "Actions.TrackedObject00" },
{ "from": "Standard.TrackedObject01", "to" : "Actions.TrackedObject01" },
{ "from": "Standard.TrackedObject02", "to" : "Actions.TrackedObject02" },
{ "from": "Standard.TrackedObject03", "to" : "Actions.TrackedObject03" },
{ "from": "Standard.TrackedObject04", "to" : "Actions.TrackedObject04" },
{ "from": "Standard.TrackedObject05", "to" : "Actions.TrackedObject05" },
{ "from": "Standard.TrackedObject06", "to" : "Actions.TrackedObject06" },
{ "from": "Standard.TrackedObject07", "to" : "Actions.TrackedObject07" },
{ "from": "Standard.TrackedObject08", "to" : "Actions.TrackedObject08" },
{ "from": "Standard.TrackedObject09", "to" : "Actions.TrackedObject09" },
{ "from": "Standard.TrackedObject10", "to" : "Actions.TrackedObject10" },
{ "from": "Standard.TrackedObject11", "to" : "Actions.TrackedObject11" },
{ "from": "Standard.TrackedObject12", "to" : "Actions.TrackedObject12" },
{ "from": "Standard.TrackedObject13", "to" : "Actions.TrackedObject13" },
{ "from": "Standard.TrackedObject14", "to" : "Actions.TrackedObject14" },
{ "from": "Standard.TrackedObject15", "to" : "Actions.TrackedObject15" }
]
}

View file

@ -77,6 +77,23 @@
{ "from": "Vive.Head", "to" : "Standard.Head"},
{ "from": "Vive.RightArm", "to" : "Standard.RightArm" },
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm" }
{ "from": "Vive.LeftArm", "to" : "Standard.LeftArm" },
{ "from": "Vive.TrackedObject00", "to" : "Standard.TrackedObject00" },
{ "from": "Vive.TrackedObject01", "to" : "Standard.TrackedObject01" },
{ "from": "Vive.TrackedObject02", "to" : "Standard.TrackedObject02" },
{ "from": "Vive.TrackedObject03", "to" : "Standard.TrackedObject03" },
{ "from": "Vive.TrackedObject04", "to" : "Standard.TrackedObject04" },
{ "from": "Vive.TrackedObject05", "to" : "Standard.TrackedObject05" },
{ "from": "Vive.TrackedObject06", "to" : "Standard.TrackedObject06" },
{ "from": "Vive.TrackedObject07", "to" : "Standard.TrackedObject07" },
{ "from": "Vive.TrackedObject08", "to" : "Standard.TrackedObject08" },
{ "from": "Vive.TrackedObject09", "to" : "Standard.TrackedObject09" },
{ "from": "Vive.TrackedObject10", "to" : "Standard.TrackedObject10" },
{ "from": "Vive.TrackedObject11", "to" : "Standard.TrackedObject11" },
{ "from": "Vive.TrackedObject12", "to" : "Standard.TrackedObject12" },
{ "from": "Vive.TrackedObject13", "to" : "Standard.TrackedObject13" },
{ "from": "Vive.TrackedObject14", "to" : "Standard.TrackedObject14" },
{ "from": "Vive.TrackedObject15", "to" : "Standard.TrackedObject15" }
]
}

Binary file not shown.

Before

(image error) Size: 254 KiB

After

(image error) Size: 312 KiB

Binary file not shown.

Before

(image error) Size: 186 KiB

After

(image error) Size: 232 KiB

Binary file not shown.

Before

(image error) Size: 253 KiB

After

(image error) Size: 307 KiB

Binary file not shown.

Before

(image error) Size: 211 KiB

After

(image error) Size: 268 KiB

File diff suppressed because one or more lines are too long

After

(image error) Size: 52 KiB

File diff suppressed because one or more lines are too long

After

(image error) Size: 56 KiB

Binary file not shown.

After

(image error) Size: 67 KiB

Binary file not shown.

After

(image error) Size: 27 KiB

View file

@ -39,7 +39,7 @@ Item {
width: parent.width
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
profile: HFTabletWebEngineProfile;
profile: HFWebEngineProfile;
property string userScriptUrl: ""

View file

@ -15,6 +15,11 @@ import "controls" as Controls
Controls.WebView {
// This is for JS/QML communication, which is unused in a Web3DOverlay,
// but not having this here results in spurious warnings about a
// missing signal
signal sendToScript(var message);
function onWebEventReceived(event) {
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));

View file

@ -27,6 +27,7 @@ TreeView {
model: treeModel
selection: ItemSelectionModel {
id: selectionModel
model: treeModel
}
@ -215,6 +216,10 @@ TreeView {
onDoubleClicked: isExpanded(index) ? collapse(index) : expand(index)
onClicked: {
selectionModel.setCurrentIndex(index, ItemSelectionModel.ClearAndSelect);
}
onActivated: {
var path = scriptsModel.data(index, 0x100)
if (path) {

View file

@ -0,0 +1,48 @@
//
// GlyphButton.qml
//
// Created by Vlad Stelmahovsky on 2017-06-21
// 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 as Original
import QtQuick.Controls.Styles 1.4
import "../styles-uit"
Original.Button {
id: control
property int colorScheme: hifi.colorSchemes.light
property string glyph: ""
property int size: 32
//colors
readonly property color normalColor: "#AFAFAF"
readonly property color hoverColor: "#00B4EF"
readonly property color clickedColor: "#FFFFFF"
readonly property color disabledColor: "#575757"
style: ButtonStyle {
background: Item {}
label: HiFiGlyphs {
color: control.enabled ? (control.pressed ? control.clickedColor :
(control.hovered ? control.hoverColor : control.normalColor)) :
control.disabledColor
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
anchors {
// Tweak horizontal alignment so that it looks right.
left: parent.left
leftMargin: -0.5
}
text: control.glyph
size: control.size
}
}
}

View file

@ -31,7 +31,7 @@ Item {
width: parent.width
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
profile: HFTabletWebEngineProfile;
profile: HFWebEngineProfile;
property string userScriptUrl: ""

View file

@ -24,6 +24,7 @@ Item {
property alias webView: webview
property alias profile: webview.profile
property bool remove: false
property bool closeButtonVisible: true
// Manage own browse history because WebEngineView history is wiped when a new URL is loaded via
// onNewViewRequested, e.g., as happens when a social media share button is clicked.
@ -64,6 +65,7 @@ Item {
disabledColor: hifi.colors.lightGrayText
enabled: true
text: "CLOSE"
visible: closeButtonVisible
MouseArea {
anchors.fill: parent
@ -142,7 +144,7 @@ Item {
width: parent.width
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - web.headerHeight : parent.height - web.headerHeight
anchors.top: buttons.bottom
profile: HFTabletWebEngineProfile;
profile: HFWebEngineProfile;
property string userScriptUrl: ""
@ -182,8 +184,6 @@ Item {
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";
}
onFeaturePermissionRequested: {

View file

@ -73,7 +73,6 @@ Item {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
});
root.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
}
onFeaturePermissionRequested: {

View file

@ -41,6 +41,11 @@ FocusScope {
// when they're opened.
signal showDesktop();
// This is for JS/QML communication, which is unused in the Desktop,
// but not having this here results in spurious warnings about a
// missing signal
signal sendToScript(var message);
// Allows QML/JS to find the desktop through the parent chain
property bool desktopRoot: true

View file

@ -46,6 +46,8 @@ Item {
property int stackShadowNarrowing: 5;
property string defaultThumbnail: Qt.resolvedUrl("../../images/default-domain.gif");
property int shadowHeight: 10;
property bool hovered: false
HifiConstants { id: hifi }
function pastTime(timestamp) { // Answer a descriptive string
@ -231,6 +233,13 @@ Item {
// to that which is being hovered over.
property var hoverThunk: function () { };
property var unhoverThunk: function () { };
Rectangle {
anchors.fill: parent;
visible: root.hovered
color: "transparent";
border.width: 4; border.color: hifiStyleConstants.colors.primaryHighlight;
z: 1;
}
MouseArea {
anchors.fill: parent;
acceptedButtons: Qt.LeftButton;

View file

@ -0,0 +1,158 @@
//
// LetterboxMessage.qml
// qml/hifi
//
// Created by Dante Ruiz on 7/21/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"
Item {
property alias text: popupText.text
property alias headerGlyph: headerGlyph.text
property alias headerText: headerText.text
property alias headerGlyphSize: headerGlyph.size
property real popupRadius: hifi.dimensions.borderRadius
property real headerTextPixelSize: 22
property real popupTextPixelSize: 16
property real headerTextMargin: -5
property real headerGlyphMargin: -15
property bool isDesktop: false
FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; }
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
visible: false
id: letterbox
anchors.fill: parent
Rectangle {
id: textContainer;
width: parent.width
height: parent.height
anchors.centerIn: parent
radius: popupRadius
color: "white"
Item {
id: contentContainer
width: parent.width - 50
height: childrenRect.height
anchors.centerIn: parent
Item {
id: popupHeaderContainer
visible: headerText.text !== "" || headerGlyph.text !== ""
height: 30
// Anchors
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
// Header Glyph
HiFiGlyphs {
id: headerGlyph
visible: headerGlyph.text !== ""
// Size
height: parent.height
// Anchors
anchors.left: parent.left
anchors.leftMargin: headerGlyphMargin
// Text Size
size: headerTextPixelSize*2.5
// Style
horizontalAlignment: Text.AlignHLeft
verticalAlignment: Text.AlignVCenter
color: hifi.colors.darkGray
}
// Header Text
Text {
id: headerText
visible: headerText.text !== ""
// Size
height: parent.height
// Anchors
anchors.left: headerGlyph.right
anchors.leftMargin: headerTextMargin
// Text Size
font.pixelSize: headerTextPixelSize
// Style
font.family: ralewaySemiBold.name
color: hifi.colors.darkGray
horizontalAlignment: Text.AlignHLeft
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
textFormat: Text.StyledText
}
}
// Popup Text
Text {
id: popupText
// Size
width: parent.width
// Anchors
anchors.top: popupHeaderContainer.visible ? popupHeaderContainer.bottom : parent.top
anchors.topMargin: popupHeaderContainer.visible ? 15 : 0
anchors.left: parent.left
anchors.right: parent.right
// Text alignment
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHLeft
// Style
font.pixelSize: popupTextPixelSize
font.family: ralewayRegular.name
color: hifi.colors.darkGray
wrapMode: Text.WordWrap
textFormat: Text.StyledText
onLinkActivated: {
Qt.openUrlExternally(link)
}
}
}
}
// Left gray MouseArea
MouseArea {
anchors.left: parent.left;
anchors.right: textContainer.left;
anchors.top: textContainer.top;
anchors.bottom: textContainer.bottom;
acceptedButtons: Qt.LeftButton
onClicked: {
letterbox.visible = false
}
}
// Right gray MouseArea
MouseArea {
anchors.left: textContainer.left;
anchors.right: parent.left;
anchors.top: textContainer.top;
anchors.bottom: textContainer.bottom;
acceptedButtons: Qt.LeftButton
onClicked: {
letterbox.visible = false
}
}
// Top gray MouseArea
MouseArea {
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
anchors.bottom: textContainer.top;
acceptedButtons: Qt.LeftButton
onClicked: {
letterbox.visible = false
}
}
// Bottom gray MouseArea
MouseArea {
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: textContainer.bottom;
anchors.bottom: parent.bottom;
acceptedButtons: Qt.LeftButton
onClicked: {
letterbox.visible = false
}
}
}

View file

@ -35,7 +35,6 @@ Column {
property string metaverseServerUrl: '';
property string actions: 'snapshot';
// sendToScript doesn't get wired until after everything gets created. So we have to queue fillDestinations on nextTick.
Component.onCompleted: delay.start();
property string labelText: actions;
property string filter: '';
onFilterChanged: filterChoicesByText();
@ -125,11 +124,9 @@ Column {
cb();
});
}
property var delay: Timer { // No setTimeout or nextTick in QML.
interval: 0;
onTriggered: fillDestinations();
}
function fillDestinations() { // Public
console.debug('Feed::fillDestinations()')
function report(label, error) {
console.log(label, actions, error || 'ok', allStories.length, 'filtered to', suggestions.count);
}
@ -206,9 +203,9 @@ Column {
id: scroll;
model: suggestions;
orientation: ListView.Horizontal;
highlightFollowsCurrentItem: false
highlightMoveDuration: -1;
highlightMoveVelocity: -1;
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.primaryHighlight; z: 1; }
currentIndex: -1;
spacing: 12;
@ -237,9 +234,8 @@ Column {
textSizeSmall: root.textSizeSmall;
stackShadowNarrowing: root.stackShadowNarrowing;
shadowHeight: root.stackedCardShadowHeight;
hoverThunk: function () { scrollToIndex(index); }
unhoverThunk: function () { scrollToIndex(-1); }
hoverThunk: function () { hovered = true }
unhoverThunk: function () { hovered = false }
}
}
NumberAnimation {

View file

@ -17,9 +17,12 @@ Item {
property alias text: popupText.text
property alias headerGlyph: headerGlyph.text
property alias headerText: headerText.text
property alias headerGlyphSize: headerGlyph.size
property real popupRadius: hifi.dimensions.borderRadius
property real headerTextPixelSize: 22
property real popupTextPixelSize: 16
property real headerTextMargin: -5
property real headerGlyphMargin: -15
FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; }
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
visible: false
@ -59,7 +62,7 @@ Item {
height: parent.height
// Anchors
anchors.left: parent.left
anchors.leftMargin: -15
anchors.leftMargin: headerGlyphMargin
// Text Size
size: headerTextPixelSize*2.5
// Style
@ -75,7 +78,7 @@ Item {
height: parent.height
// Anchors
anchors.left: headerGlyph.right
anchors.leftMargin: -5
anchors.leftMargin: headerTextMargin
// Text Size
font.pixelSize: headerTextPixelSize
// Style

View file

@ -0,0 +1,173 @@
//
// skyboxchanger.qml
//
//
// Created by Cain Kilgore on 9th August 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.Layouts 1.3
Rectangle {
id: root;
color: hifi.colors.baseGray;
Item {
id: titleBarContainer;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
RalewaySemiBold {
id: titleBarText;
text: "Skybox Changer";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.fill: parent;
anchors.leftMargin: 16;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewaySemiBold {
id: titleBarDesc;
text: "Click an image to choose a new Skybox.";
wrapMode: Text.Wrap
// Text size
size: 14;
// Anchors
anchors.fill: parent;
anchors.top: titleBarText.bottom
anchors.leftMargin: 16;
anchors.rightMargin: 16;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
// This RowLayout could be a GridLayout instead for further expandability.
// As this SkyboxChanger task only required 6 images, implementing GridLayout wasn't necessary.
// In the future if this is to be expanded to add more Skyboxes, it might be worth changing this.
RowLayout {
id: row1
anchors.top: titleBarContainer.bottom
anchors.left: parent.left
anchors.leftMargin: 30
Layout.fillWidth: true
anchors.topMargin: 30
spacing: 10
Image {
width: 200; height: 200
fillMode: Image.Stretch
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_1.jpg"
clip: true
id: preview1
MouseArea {
anchors.fill: parent
onClicked: {
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/1.jpg'});
}
}
Layout.fillWidth: true
}
Image {
width: 200; height: 200
fillMode: Image.Stretch
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_2.jpg"
clip: true
id: preview2
MouseArea {
anchors.fill: parent
onClicked: {
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/2.png'});
}
}
}
}
RowLayout {
id: row2
anchors.top: row1.bottom
anchors.topMargin: 10
anchors.left: parent.left
Layout.fillWidth: true
anchors.leftMargin: 30
spacing: 10
Image {
width: 200; height: 200
fillMode: Image.Stretch
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_3.jpg"
clip: true
id: preview3
MouseArea {
anchors.fill: parent
onClicked: {
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/3.jpg'});
}
}
}
Image {
width: 200; height: 200
fillMode: Image.Stretch
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_4.jpg"
clip: true
id: preview4
MouseArea {
anchors.fill: parent
onClicked: {
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/4.jpg'});
}
}
}
}
RowLayout {
id: row3
anchors.top: row2.bottom
anchors.topMargin: 10
anchors.left: parent.left
Layout.fillWidth: true
anchors.leftMargin: 30
spacing: 10
Image {
width: 200; height: 200
fillMode: Image.Stretch
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_5.jpg"
clip: true
id: preview5
MouseArea {
anchors.fill: parent
onClicked: {
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/5.png'});
}
}
}
Image {
width: 200; height: 200
fillMode: Image.Stretch
source: "http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/thumbnails/thumb_6.jpg"
clip: true
id: preview6
MouseArea {
anchors.fill: parent
onClicked: {
sendToScript({method: 'changeSkybox', url: 'http://mpassets.highfidelity.com/05904016-8f7d-4dfc-88e1-2bf9ba3fac20-v1/skyboxes/6.jpg'});
}
}
}
}
signal sendToScript(var message);
}

View file

@ -0,0 +1,253 @@
//
// WebBrowser.qml
//
//
// Created by Vlad Stelmahovsky on 06/22/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.5 as QQControls
import QtQuick.Layouts 1.3
import QtQuick.Controls.Styles 1.4
import QtWebEngine 1.2
import QtWebChannel 1.0
import "../styles-uit"
import "../controls-uit" as HifiControls
import "../windows"
import "../controls"
Rectangle {
id: root;
HifiConstants { id: hifi; }
property string title: "";
signal sendToScript(var message);
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
property bool keyboardRaised: false
property bool punctuationMode: false
color: hifi.colors.baseGray;
// only show the title if loaded through a "loader"
Column {
spacing: 2
width: parent.width;
RowLayout {
width: parent.width;
height: 48
HifiControls.WebGlyphButton {
enabled: webEngineView.canGoBack
glyph: hifi.glyphs.backward;
anchors.verticalCenter: parent.verticalCenter;
size: 38;
onClicked: {
webEngineView.goBack()
}
}
HifiControls.WebGlyphButton {
enabled: webEngineView.canGoForward
glyph: hifi.glyphs.forward;
anchors.verticalCenter: parent.verticalCenter;
size: 38;
onClicked: {
webEngineView.goForward()
}
}
QQControls.TextField {
id: addressBar
Image {
anchors.verticalCenter: addressBar.verticalCenter;
x: 5
z: 2
id: faviconImage
width: 16; height: 16
sourceSize: Qt.size(width, height)
source: webEngineView.icon
}
HifiControls.WebGlyphButton {
glyph: webEngineView.loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
anchors.verticalCenter: parent.verticalCenter;
width: hifi.dimensions.controlLineHeight
z: 2
x: addressBar.width - 28
onClicked: {
if (webEngineView.loading) {
webEngineView.stop()
} else {
reloadTimer.start()
}
}
}
style: TextFieldStyle {
padding {
left: 26;
right: 26
}
}
focus: true
Layout.fillWidth: true
text: webEngineView.url
onAccepted: webEngineView.url = text
}
HifiControls.WebGlyphButton {
checkable: true
//only QtWebEngine 1.3
//checked: webEngineView.audioMuted
glyph: checked ? hifi.glyphs.unmuted : hifi.glyphs.muted
anchors.verticalCenter: parent.verticalCenter;
width: hifi.dimensions.controlLineHeight
onClicked: {
webEngineView.triggerWebAction(WebEngineView.ToggleMediaMute)
}
}
}
QQControls.ProgressBar {
id: loadProgressBar
style: ProgressBarStyle {
background: Rectangle {
color: "#6A6A6A"
}
progress: Rectangle{
color: "#00B4EF"
}
}
width: parent.width;
minimumValue: 0
maximumValue: 100
value: webEngineView.loadProgress
height: 2
}
HifiControls.BaseWebView {
id: webEngineView
focus: true
objectName: "tabletWebEngineView"
url: "http://www.highfidelity.com"
property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
width: parent.width;
height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
profile: HFWebEngineProfile;
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: webEngineView.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
settings.autoLoadImages: true
settings.javascriptEnabled: true
settings.errorPageEnabled: true
settings.pluginsEnabled: true
settings.fullScreenSupportEnabled: false
//from WebEngine 1.3
// settings.autoLoadIconsForPage: false
// settings.touchIconsEnabled: false
onCertificateError: {
error.defer();
}
Component.onCompleted: {
webChannel.registerObject("eventBridge", eventBridge);
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
webEngineView.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
onNewViewRequested: {
if (!request.userInitiated) {
print("Warning: Blocked a popup window.");
}
}
onRenderProcessTerminated: {
var status = "";
switch (terminationStatus) {
case WebEngineView.NormalTerminationStatus:
status = "(normal exit)";
break;
case WebEngineView.AbnormalTerminationStatus:
status = "(abnormal exit)";
break;
case WebEngineView.CrashedTerminationStatus:
status = "(crashed)";
break;
case WebEngineView.KilledTerminationStatus:
status = "(killed)";
break;
}
print("Render process exited with code " + exitCode + " " + status);
reloadTimer.running = true;
}
onWindowCloseRequested: {
}
Timer {
id: reloadTimer
interval: 0
running: false
repeat: false
onTriggered: webEngineView.reload()
}
}
}
HifiControls.Keyboard {
id: keyboard
raised: parent.keyboardEnabled && parent.keyboardRaised
numeric: parent.punctuationMode
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
}
}

View file

@ -33,7 +33,7 @@ Rectangle {
// only show the title if loaded through a "loader"
function showTitle() {
return root.parent.objectName == "loader";
return (root.parent !== null) && root.parent.objectName == "loader";
}
Column {

View file

@ -27,7 +27,7 @@ Rectangle {
color: "#00000000";
border {
width: (standalone || Audio.muted || mouseArea.containsMouse) ? 2 : 0;
width: mouseArea.containsMouse || mouseArea.containsPress ? 2 : 0;
color: colors.border;
}
@ -61,7 +61,7 @@ Rectangle {
drag.target: dragTarget;
}
Item {
QtObject {
id: colors;
readonly property string unmuted: "#FFF";
@ -72,7 +72,7 @@ Rectangle {
readonly property string red: colors.muted;
readonly property string fill: "#55000000";
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
readonly property string icon: (Audio.muted && !mouseArea.containsMouse) ? muted : unmuted;
readonly property string icon: Audio.muted ? muted : unmuted;
}
Item {
@ -92,10 +92,8 @@ Rectangle {
readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg";
readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg";
function exclusiveOr(a, b) { return (a || b) && !(a && b); }
id: image;
source: exclusiveOr(Audio.muted, mouseArea.containsMouse) ? mutedIcon : unmutedIcon;
source: Audio.muted ? mutedIcon : unmutedIcon;
width: 30;
height: 30;
@ -118,9 +116,9 @@ Rectangle {
Item {
id: status;
readonly property string color: (Audio.muted && !mouseArea.containsMouse) ? colors.muted : colors.unmuted;
readonly property string color: Audio.muted ? colors.muted : colors.unmuted;
visible: Audio.muted || mouseArea.containsMouse;
visible: Audio.muted;
anchors {
left: parent.left;
@ -133,14 +131,14 @@ Rectangle {
Text {
anchors {
horizontalCenter: parent.horizontalCenter;
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
color: parent.color;
text: Audio.muted ? (mouseArea.containsMouse ? "UNMUTE" : "MUTED") : "MUTE";
font.pointSize: 12;
text: Audio.muted ? "MUTED" : "MUTE";
font.pointSize: 12;
}
Rectangle {
@ -150,7 +148,7 @@ Rectangle {
}
width: 50;
height: 4;
height: 4;
color: parent.color;
}
@ -161,7 +159,7 @@ Rectangle {
}
width: 50;
height: 4;
height: 4;
color: parent.color;
}
}

View file

@ -0,0 +1,463 @@
//
// Checkout.qml
// qml/hifi/commerce
//
// Checkout
//
// Created by Zach Fox on 2017-08-07
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../styles-uit"
import "../../controls-uit" as HifiControlsUit
import "../../controls" as HifiControls
import "./wallet" as HifiWallet
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: checkoutRoot;
property bool inventoryReceived: false;
property bool balanceReceived: false;
property string itemId: "";
property string itemHref: "";
property int balanceAfterPurchase: 0;
property bool alreadyOwned: false;
property int itemPriceFull: 0;
// Style
color: hifi.colors.baseGray;
Hifi.QmlCommerce {
id: commerce;
onBuyResult: {
if (result.status !== 'success') {
buyButton.text = result.message;
buyButton.enabled = false;
} else {
if (urlHandler.canHandleUrl(itemHref)) {
urlHandler.handleUrl(itemHref);
}
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
}
}
onBalanceResult: {
if (result.status !== 'success') {
console.log("Failed to get balance", result.message);
} else {
balanceReceived = true;
hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2);
balanceAfterPurchase = parseFloat(result.data.balance/100) - parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2);
}
}
onInventoryResult: {
if (result.status !== 'success') {
console.log("Failed to get inventory", result.message);
} else {
inventoryReceived = true;
console.log('inventory fixme', JSON.stringify(result));
if (inventoryContains(result.data.assets, itemId)) {
alreadyOwned = true;
} else {
alreadyOwned = false;
}
}
}
}
//
// TITLE BAR START
//
Item {
id: titleBarContainer;
// Size
width: checkoutRoot.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
id: titleBarText;
text: "Checkout";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
}
}
//
// TITLE BAR END
//
//
// ITEM DESCRIPTION START
//
Item {
id: itemDescriptionContainer;
// Size
width: checkoutRoot.width;
height: childrenRect.height + 20;
// Anchors
anchors.left: parent.left;
anchors.top: titleBarContainer.bottom;
// Item Name text
Item {
id: itemNameContainer;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 4;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: childrenRect.height;
RalewaySemiBold {
id: itemNameTextLabel;
text: "Item Name:";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
width: paintedWidth;
// Text size
size: 16;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: itemNameText;
// Text size
size: itemNameTextLabel.size;
// Anchors
anchors.top: parent.top;
anchors.left: itemNameTextLabel.right;
anchors.leftMargin: 16;
width: paintedWidth;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Item Author text
Item {
id: itemAuthorContainer;
// Anchors
anchors.top: itemNameContainer.bottom;
anchors.topMargin: 4;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: childrenRect.height;
RalewaySemiBold {
id: itemAuthorTextLabel;
text: "Item Author:";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
width: paintedWidth;
// Text size
size: 16;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: itemAuthorText;
// Text size
size: itemAuthorTextLabel.size;
// Anchors
anchors.top: parent.top;
anchors.left: itemAuthorTextLabel.right;
anchors.leftMargin: 16;
width: paintedWidth;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// HFC Balance text
Item {
id: hfcBalanceContainer;
// Anchors
anchors.top: itemAuthorContainer.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: childrenRect.height;
RalewaySemiBold {
id: hfcBalanceTextLabel;
text: "HFC Balance:";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
width: paintedWidth;
// Text size
size: 20;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: hfcBalanceText;
text: "--";
// Text size
size: hfcBalanceTextLabel.size;
// Anchors
anchors.top: parent.top;
anchors.left: hfcBalanceTextLabel.right;
anchors.leftMargin: 16;
width: paintedWidth;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Item Price text
Item {
id: itemPriceContainer;
// Anchors
anchors.top: hfcBalanceContainer.bottom;
anchors.topMargin: 4;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: childrenRect.height;
RalewaySemiBold {
id: itemPriceTextLabel;
text: "Item Price:";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
width: paintedWidth;
// Text size
size: 20;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: itemPriceText;
// Text size
size: itemPriceTextLabel.size;
// Anchors
anchors.top: parent.top;
anchors.left: itemPriceTextLabel.right;
anchors.leftMargin: 16;
width: paintedWidth;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// HFC "Balance After Purchase" text
Item {
id: hfcBalanceAfterPurchaseContainer;
// Anchors
anchors.top: itemPriceContainer.bottom;
anchors.topMargin: 4;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: childrenRect.height;
RalewaySemiBold {
id: hfcBalanceAfterPurchaseTextLabel;
text: "HFC Balance After Purchase:";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
width: paintedWidth;
// Text size
size: 20;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: hfcBalanceAfterPurchaseText;
text: balanceAfterPurchase;
// Text size
size: hfcBalanceAfterPurchaseTextLabel.size;
// Anchors
anchors.top: parent.top;
anchors.left: hfcBalanceAfterPurchaseTextLabel.right;
anchors.leftMargin: 16;
width: paintedWidth;
// Style
color: (balanceAfterPurchase >= 0) ? hifi.colors.lightGrayText : hifi.colors.redHighlight;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
}
//
// ITEM DESCRIPTION END
//
//
// ACTION BUTTONS START
//
Item {
id: actionButtonsContainer;
// Size
width: checkoutRoot.width;
height: 40;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 8;
// "Cancel" button
HifiControlsUit.Button {
id: cancelButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: "Cancel"
onClicked: {
sendToScript({method: 'checkout_cancelClicked', params: itemId});
}
}
// "Buy" button
HifiControlsUit.Button {
id: buyButton;
enabled: balanceAfterPurchase >= 0 && inventoryReceived && balanceReceived;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
text: (inventoryReceived && balanceReceived) ? (alreadyOwned ? "Already Owned: Get Item" : "Buy") : "--";
onClicked: {
if (!alreadyOwned) {
commerce.buy(itemId, parseFloat(itemPriceText.text*100));
} else {
if (urlHandler.canHandleUrl(itemHref)) {
urlHandler.handleUrl(itemHref);
}
sendToScript({method: 'checkout_buySuccess', itemId: itemId});
}
}
}
}
//
// ACTION BUTTONS END
//
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript, in this case the Marketplaces JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
case 'updateCheckoutQML':
itemId = message.params.itemId;
itemNameText.text = message.params.itemName;
itemAuthorText.text = message.params.itemAuthor;
checkoutRoot.itemPriceFull = message.params.itemPrice;
itemPriceText.text = parseFloat(checkoutRoot.itemPriceFull/100).toFixed(2);
itemHref = message.params.itemHref;
commerce.balance();
commerce.inventory();
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}
}
signal sendToScript(var message);
function inventoryContains(inventoryJson, id) {
for (var idx = 0; idx < inventoryJson.length; idx++) {
if(inventoryJson[idx].id === id) {
return true;
}
}
return false;
}
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,280 @@
//
// Inventory.qml
// qml/hifi/commerce
//
// Inventory
//
// Created by Zach Fox on 2017-08-10
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../styles-uit"
import "../../controls-uit" as HifiControlsUit
import "../../controls" as HifiControls
import "./wallet" as HifiWallet
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: inventoryRoot;
property string referrerURL: "";
// Style
color: hifi.colors.baseGray;
Hifi.QmlCommerce {
id: commerce;
onBalanceResult: {
if (result.status !== 'success') {
console.log("Failed to get balance", result.message);
} else {
hfcBalanceText.text = parseFloat(result.data.balance/100).toFixed(2);
}
}
onInventoryResult: {
if (result.status !== 'success') {
console.log("Failed to get inventory", result.message);
} else {
inventoryContentsList.model = result.data.assets;
}
}
}
//
// TITLE BAR START
//
Item {
id: titleBarContainer;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
id: titleBarText;
text: "Inventory";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
}
}
//
// TITLE BAR END
//
//
// HFC BALANCE START
//
Item {
id: hfcBalanceContainer;
// Size
width: inventoryRoot.width;
height: childrenRect.height + 20;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 4;
RalewaySemiBold {
id: hfcBalanceTextLabel;
text: "HFC Balance:";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
width: paintedWidth;
// Text size
size: 20;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: hfcBalanceText;
text: "--";
// Text size
size: hfcBalanceTextLabel.size;
// Anchors
anchors.top: parent.top;
anchors.left: hfcBalanceTextLabel.right;
anchors.leftMargin: 16;
width: paintedWidth;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
//
// HFC BALANCE END
//
//
// INVENTORY CONTENTS START
//
Item {
id: inventoryContentsContainer;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
anchors.top: hfcBalanceContainer.bottom;
anchors.topMargin: 8;
anchors.bottom: actionButtonsContainer.top;
anchors.bottomMargin: 8;
RalewaySemiBold {
id: inventoryContentsLabel;
text: "Inventory:";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
width: paintedWidth;
// Text size
size: 24;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
ListView {
id: inventoryContentsList;
clip: true;
// Anchors
anchors.top: inventoryContentsLabel.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width;
delegate: Item {
width: parent.width;
height: 30;
RalewayRegular {
id: thisItemId;
// Text size
size: 20;
// Style
color: hifi.colors.blueAccent;
text: modelData.title;
// Alignment
horizontalAlignment: Text.AlignHLeft;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToScript({method: 'inventory_itemClicked', itemId: modelData.id});
}
onEntered: {
thisItemId.color = hifi.colors.blueHighlight;
}
onExited: {
thisItemId.color = hifi.colors.blueAccent;
}
}
}
}
}
//
// INVENTORY CONTENTS END
//
//
// ACTION BUTTONS START
//
Item {
id: actionButtonsContainer;
// Size
width: inventoryRoot.width;
height: 40;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 8;
// "Back" button
HifiControlsUit.Button {
id: backButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: "Back"
onClicked: {
sendToScript({method: 'inventory_backClicked', referrerURL: referrerURL});
}
}
}
//
// ACTION BUTTONS END
//
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript, in this case the Marketplaces JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
case 'updateInventory':
referrerURL = message.referrerURL;
commerce.balance();
commerce.inventory();
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}
}
signal sendToScript(var message);
//
// FUNCTION DEFINITIONS END
//
}

Binary file not shown.

After

(image error) Size: 62 KiB

Binary file not shown.

After

(image error) Size: 99 KiB

Binary file not shown.

After

(image error) Size: 113 KiB

Binary file not shown.

After

(image error) Size: 86 KiB

Binary file not shown.

After

(image error) Size: 61 KiB

Binary file not shown.

After

(image error) Size: 74 KiB

View file

@ -0,0 +1,73 @@
//
// SendMoney.qml
// qml/hifi/commerce/wallet
//
// SendMoney
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
Hifi.QmlCommerce {
id: commerce;
}
// "Unavailable"
RalewayRegular {
text: "Help me!";
// Anchors
anchors.fill: parent;
// Text size
size: 24;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,127 @@
//
// NotSetUp.qml
// qml/hifi/commerce/wallet
//
// NotSetUp
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
Hifi.QmlCommerce {
id: commerce;
}
//
// TAB CONTENTS START
//
// Text below title bar
RalewaySemiBold {
id: notSetUpText;
text: "Your Wallet Account Has Not Been Set Up";
// Text size
size: 22;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 100;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
// Explanitory text
RalewayRegular {
text: "To buy and sell items in High Fidelity Coin (HFC), you first need " +
"to set up your wallet.<br><b>You do not need to submit a credit card or personal information to set up your wallet.</b>";
// Text size
size: 18;
// Anchors
anchors.top: notSetUpText.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: 100;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
// "Set Up" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 150;
anchors.horizontalCenter: parent.horizontalCenter;
width: parent.width/2;
height: 50;
text: "Set Up My Wallet";
onClicked: {
sendSignalToWallet({method: 'setUpClicked'});
}
}
//
// TAB CONTENTS END
//
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,232 @@
//
// PassphraseSelection.qml
// qml/hifi/commerce/wallet
//
// PassphraseSelection
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup.
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
passphrasePageSecurityImage.source = "";
passphrasePageSecurityImage.source = "image://security/securityImage";
}
onPassphraseSetupStatusResult: {
sendMessageToLightbox({method: 'statusResult', status: passphraseIsSetup});
}
}
onVisibleChanged: {
if (visible) {
passphraseField.focus = true;
}
}
SecurityImageModel {
id: gridModel;
}
HifiControlsUit.TextField {
id: passphraseField;
anchors.top: parent.top;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.leftMargin: 16;
width: 280;
height: 50;
echoMode: TextInput.Password;
placeholderText: "passphrase";
onVisibleChanged: {
if (visible) {
text = "";
}
}
}
HifiControlsUit.TextField {
id: passphraseFieldAgain;
anchors.top: passphraseField.bottom;
anchors.topMargin: 10;
anchors.left: passphraseField.left;
anchors.right: passphraseField.right;
height: 50;
echoMode: TextInput.Password;
placeholderText: "re-enter passphrase";
onVisibleChanged: {
if (visible) {
text = "";
}
}
}
// Security Image
Item {
id: securityImageContainer;
// Anchors
anchors.top: passphraseField.top;
anchors.left: passphraseField.right;
anchors.leftMargin: 12;
anchors.right: parent.right;
Image {
id: passphrasePageSecurityImage;
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: 75;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
cache: false;
onVisibleChanged: {
commerce.getSecurityImage();
}
}
// "Security picture" text below pic
RalewayRegular {
text: "security picture";
// Text size
size: 12;
// Anchors
anchors.top: passphrasePageSecurityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
// Error text below TextFields
RalewaySemiBold {
id: errorText;
text: "";
// Text size
size: 16;
// Anchors
anchors.top: passphraseFieldAgain.bottom;
anchors.topMargin: 0;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 30;
// Style
color: hifi.colors.redHighlight;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Text below TextFields
RalewaySemiBold {
id: passwordReqs;
text: "Passphrase must be at least 4 characters";
// Text size
size: 16;
// Anchors
anchors.top: passphraseFieldAgain.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 30;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Show passphrase text
HifiControlsUit.CheckBox {
id: showPassphrase;
colorScheme: hifi.colorSchemes.dark;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.top: passwordReqs.bottom;
anchors.topMargin: 16;
height: 30;
text: "Show passphrase as plain text";
boxSize: 24;
onClicked: {
passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
passphraseFieldAgain.echoMode = checked ? TextInput.Normal : TextInput.Password;
}
}
// Text below checkbox
RalewayRegular {
text: "Your passphrase is used to encrypt your private keys. <b>Please write it down.</b> If it is lost, you will not be able to recover it.";
// Text size
size: 16;
// Anchors
anchors.top: showPassphrase.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
function validateAndSubmitPassphrase() {
if (passphraseField.text.length < 4) {
setErrorText("Passphrase too short.");
return false;
} else if (passphraseField.text !== passphraseFieldAgain.text) {
setErrorText("Passphrases don't match.");
return false;
} else {
setErrorText("");
commerce.setPassphrase(passphraseField.text);
return true;
}
}
function setErrorText(text) {
errorText.text = text;
}
signal sendMessageToLightbox(var msg);
}

View file

@ -0,0 +1,175 @@
//
// PassphraseSelectionLightbox.qml
// qml/hifi/commerce/wallet
//
// PassphraseSelectionLightbox
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
// Style
color: hifi.colors.baseGray;
onVisibleChanged: {
if (visible) {
root.resetSubmitButton();
}
}
Connections {
target: passphraseSelection;
onSendMessageToLightbox: {
if (msg.method === 'statusResult') {
if (msg.status) {
// Success submitting new passphrase
root.resetSubmitButton();
root.visible = false;
} else {
// Error submitting new passphrase
root.resetSubmitButton();
passphraseSelection.setErrorText("Backend error");
}
}
}
}
//
// SECURE PASSPHRASE SELECTION START
//
Item {
id: choosePassphraseContainer;
// Anchors
anchors.fill: parent;
Item {
id: passphraseTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "CHANGE PASSPHRASE";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: passphraseTitleHelper;
text: "Choose a Secure Passphrase";
// Text size
size: 24;
// Anchors
anchors.top: passphraseTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
PassphraseSelection {
id: passphraseSelection;
anchors.top: passphraseTitleHelper.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: passphraseNavBar.top;
}
// Navigation Bar
Item {
id: passphraseNavBar;
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
text: "Cancel"
onClicked: {
root.visible = false;
}
}
// "Submit" button
HifiControlsUit.Button {
id: passphraseSubmitButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Submit";
onClicked: {
if (passphraseSelection.validateAndSubmitPassphrase()) {
passphraseSubmitButton.text = "Submitting...";
passphraseSubmitButton.enabled = false;
}
}
}
}
}
//
// SECURE PASSPHRASE SELECTION END
//
function resetSubmitButton() {
passphraseSubmitButton.enabled = true;
passphraseSubmitButton.text = "Submit";
}
}

View file

@ -0,0 +1,322 @@
//
// Security.qml
// qml/hifi/commerce/wallet
//
// Security
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (exists) { // "If security image is set up"
var path = "image://security/securityImage";
topSecurityImage.source = "";
topSecurityImage.source = path;
changeSecurityImageImage.source = "";
changeSecurityImageImage.source = path;
changePassphraseImage.source = "";
changePassphraseImage.source = path;
}
}
onKeyFilePathResult: {
if (path !== "") {
keyFilePath.text = path;
}
}
}
SecurityImageModel {
id: securityImageModel;
}
// Username Text
RalewayRegular {
id: usernameText;
text: Account.username;
// Text size
size: 24;
// Style
color: hifi.colors.faintGray;
elide: Text.ElideRight;
// Anchors
anchors.top: securityImageContainer.top;
anchors.bottom: securityImageContainer.bottom;
anchors.left: parent.left;
anchors.right: securityImageContainer.left;
}
// Security Image
Item {
id: securityImageContainer;
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
width: 75;
height: childrenRect.height;
onVisibleChanged: {
if (visible) {
commerce.getSecurityImage();
}
}
Image {
id: topSecurityImage;
// Anchors
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: parent.width - 10;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
cache: false;
}
// "Security picture" text below pic
RalewayRegular {
text: "security picture";
// Text size
size: 12;
// Anchors
anchors.top: topSecurityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
Item {
id: securityContainer;
anchors.top: securityImageContainer.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;
RalewayRegular {
id: securityText;
text: "Security";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 30;
// Text size
size: 22;
// Style
color: hifi.colors.faintGray;
}
Item {
id: changePassphraseContainer;
anchors.top: securityText.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.right: parent.right;
height: 75;
Image {
id: changePassphraseImage;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
height: parent.height;
width: height;
source: "image://security/securityImage";
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
}
// "Change Passphrase" button
HifiControlsUit.Button {
id: changePassphraseButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.verticalCenter: parent.verticalCenter;
anchors.left: changePassphraseImage.right;
anchors.leftMargin: 16;
width: 250;
height: 50;
text: "Change My Passphrase";
onClicked: {
sendSignalToWallet({method: 'walletSecurity_changePassphrase'});
}
}
}
Item {
id: changeSecurityImageContainer;
anchors.top: changePassphraseContainer.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.right: parent.right;
height: 75;
Image {
id: changeSecurityImageImage;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
height: parent.height;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
source: "image://security/securityImage";
}
// "Change Security Image" button
HifiControlsUit.Button {
id: changeSecurityImageButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.verticalCenter: parent.verticalCenter;
anchors.left: changeSecurityImageImage.right;
anchors.leftMargin: 16;
width: 250;
height: 50;
text: "Change My Security Image";
onClicked: {
sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'});
}
}
}
}
Item {
id: yourPrivateKeysContainer;
anchors.top: securityContainer.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;
RalewaySemiBold {
id: yourPrivateKeysText;
text: "Your Private Keys";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 30;
// Text size
size: 22;
// Style
color: hifi.colors.faintGray;
}
// Text below "your private keys"
RalewayRegular {
id: explanitoryText;
text: "Your money and purchases are secured with private keys that only you " +
"have access to. <b>If they are lost, you will not be able to access your money or purchases. " +
"To safeguard your private keys, back up this file regularly:</b>";
// Text size
size: 18;
// Anchors
anchors.top: yourPrivateKeysText.bottom;
anchors.topMargin: 10;
anchors.left: parent.left;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
HifiControlsUit.TextField {
id: keyFilePath;
anchors.top: explanitoryText.bottom;
anchors.topMargin: 10;
anchors.left: parent.left;
anchors.right: clipboardButton.left;
height: 40;
readOnly: true;
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePath();
}
}
}
HifiControlsUit.Button {
id: clipboardButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.top: keyFilePath.top;
anchors.bottom: keyFilePath.bottom;
width: height;
HiFiGlyphs {
text: hifi.glyphs.question;
// Size
size: parent.height*1.3;
// Anchors
anchors.fill: parent;
// Style
horizontalAlignment: Text.AlignHCenter;
color: enabled ? hifi.colors.white : hifi.colors.faintGray;
}
onClicked: {
Window.copyToClipboard(keyFilePath.text);
}
}
}
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,46 @@
//
// SecurityImageModel.qml
// qml/hifi/commerce
//
// SecurityImageModel
//
// Created by Zach Fox on 2017-08-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
ListModel {
id: root;
ListElement{
sourcePath: "images/01cat.jpg"
securityImageEnumValue: 1;
}
ListElement{
sourcePath: "images/02car.jpg"
securityImageEnumValue: 2;
}
ListElement{
sourcePath: "images/03dog.jpg"
securityImageEnumValue: 3;
}
ListElement{
sourcePath: "images/04stars.jpg"
securityImageEnumValue: 4;
}
ListElement{
sourcePath: "images/05plane.jpg"
securityImageEnumValue: 5;
}
ListElement{
sourcePath: "images/06gingerbread.jpg"
securityImageEnumValue: 6;
}
function getImagePathFromImageID(imageID) {
return (imageID ? root.get(imageID - 1).sourcePath : "");
}
}

View file

@ -0,0 +1,98 @@
//
// SecurityImageSelection.qml
// qml/hifi/commerce/wallet
//
// SecurityImageSelection
//
// Created by Zach Fox on 2017-08-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 Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
}
}
onVisibleChanged: {
if (visible) {
commerce.getSecurityImage();
}
}
SecurityImageModel {
id: gridModel;
}
GridView {
id: securityImageGrid;
clip: true;
// Anchors
anchors.fill: parent;
currentIndex: -1;
cellWidth: width / 3;
cellHeight: height / 2;
model: gridModel;
delegate: Item {
width: securityImageGrid.cellWidth;
height: securityImageGrid.cellHeight;
Item {
anchors.fill: parent;
Image {
width: parent.width - 8;
height: parent.height - 8;
source: sourcePath;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.verticalCenter: parent.verticalCenter;
fillMode: Image.PreserveAspectFit;
mipmap: true;
}
}
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
onClicked: {
securityImageGrid.currentIndex = index;
}
}
}
highlight: Rectangle {
width: securityImageGrid.cellWidth;
height: securityImageGrid.cellHeight;
color: hifi.colors.blueHighlight;
}
}
//
// FUNCTION DEFINITIONS START
//
signal sendToScript(var message);
function getImagePathFromImageID(imageID) {
return (imageID ? gridModel.getImagePathFromImageID(imageID) : "");
}
function getSelectedImageIndex() {
return gridModel.get(securityImageGrid.currentIndex).securityImageEnumValue;
}
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,200 @@
//
// SecurityImageSelectionLightbox.qml
// qml/hifi/commerce/wallet
//
// SecurityImageSelectionLightbox
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
property bool justSubmitted: false;
// Style
color: hifi.colors.baseGray;
onVisibleChanged: {
if (visible) {
root.resetSubmitButton();
}
}
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (exists) { // Success submitting new security image
if (root.justSubmitted) {
root.resetSubmitButton();
root.visible = false;
root.justSubmitted = false;
}
} else if (root.justSubmitted) {
// Error submitting new security image.
root.resetSubmitButton();
root.justSubmitted = false;
}
}
}
//
// SECURITY IMAGE SELECTION START
//
Item {
id: securityImageContainer;
// Anchors
anchors.fill: parent;
Item {
id: securityImageTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "CHANGE SECURITY IMAGE";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: securityImageTitleHelper;
text: "Choose a Security Picture:";
// Text size
size: 24;
// Anchors
anchors.top: securityImageTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
height: 50;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
SecurityImageSelection {
id: securityImageSelection;
// Anchors
anchors.top: securityImageTitleHelper.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 280;
}
// Text below security images
RalewayRegular {
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
// Text size
size: 18;
// Anchors
anchors.top: securityImageSelection.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Navigation Bar
Item {
id: securityImageNavBar;
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
text: "Cancel"
onClicked: {
root.visible = false;
}
}
// "Submit" button
HifiControlsUit.Button {
id: securityImageSubmitButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Submit";
onClicked: {
root.justSubmitted = true;
securityImageSubmitButton.text = "Submitting...";
securityImageSubmitButton.enabled = false;
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
commerce.chooseSecurityImage(securityImagePath);
}
}
}
}
//
// SECURITY IMAGE SELECTION END
//
function resetSubmitButton() {
securityImageSubmitButton.enabled = true;
securityImageSubmitButton.text = "Submit";
}
}

View file

@ -0,0 +1,73 @@
//
// SendMoney.qml
// qml/hifi/commerce/wallet
//
// SendMoney
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
Hifi.QmlCommerce {
id: commerce;
}
// "Unavailable"
RalewayRegular {
text: "You currently cannot send money to other High Fidelity users.";
// Anchors
anchors.fill: parent;
// Text size
size: 24;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,479 @@
//
// Wallet.qml
// qml/hifi/commerce/wallet
//
// Wallet
//
// Created by Zach Fox on 2017-08-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 Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
property string activeView: "walletHome";
// Style
color: hifi.colors.baseGray;
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (!exists) { // "If security image is not set up"
if (root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
}
}
}
onKeyFilePathResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
}
}
}
SecurityImageModel {
id: securityImageModel;
}
Connections {
target: walletSetupLightbox;
onSendSignalToWallet: {
if (msg.method === 'walletSetup_cancelClicked') {
walletSetupLightbox.visible = false;
} else if (msg.method === 'walletSetup_finished') {
root.activeView = "walletHome";
} else {
sendToScript(msg);
}
}
}
Connections {
target: notSetUp;
onSendSignalToWallet: {
if (msg.method === 'setUpClicked') {
walletSetupLightbox.visible = true;
}
}
}
Rectangle {
id: walletSetupLightboxContainer;
visible: walletSetupLightbox.visible || passphraseSelectionLightbox.visible || securityImageSelectionLightbox.visible;
z: 998;
anchors.fill: parent;
color: "black";
opacity: 0.5;
}
WalletSetupLightbox {
id: walletSetupLightbox;
visible: false;
z: 999;
anchors.centerIn: walletSetupLightboxContainer;
width: walletSetupLightboxContainer.width - 50;
height: walletSetupLightboxContainer.height - 50;
}
PassphraseSelectionLightbox {
id: passphraseSelectionLightbox;
visible: false;
z: 999;
anchors.centerIn: walletSetupLightboxContainer;
width: walletSetupLightboxContainer.width - 50;
height: walletSetupLightboxContainer.height - 50;
}
SecurityImageSelectionLightbox {
id: securityImageSelectionLightbox;
visible: false;
z: 999;
anchors.centerIn: walletSetupLightboxContainer;
width: walletSetupLightboxContainer.width - 50;
height: walletSetupLightboxContainer.height - 50;
}
//
// TITLE BAR START
//
Item {
id: titleBarContainer;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
id: titleBarText;
text: "WALLET";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
}
}
//
// TITLE BAR END
//
//
// TAB CONTENTS START
//
NotSetUp {
id: notSetUp;
visible: root.activeView === "notSetUp";
anchors.top: titleBarContainer.bottom;
anchors.bottom: tabButtonsContainer.top;
anchors.left: parent.left;
anchors.right: parent.right;
}
WalletHome {
id: walletHome;
visible: root.activeView === "walletHome";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
}
SendMoney {
id: sendMoney;
visible: root.activeView === "sendMoney";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
}
Security {
id: security;
visible: root.activeView === "security";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
}
Connections {
target: security;
onSendSignalToWallet: {
if (msg.method === 'walletSecurity_changePassphrase') {
passphraseSelectionLightbox.visible = true;
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
securityImageSelectionLightbox.visible = true;
}
}
}
Help {
id: help;
visible: root.activeView === "help";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
}
//
// TAB CONTENTS END
//
//
// TAB BUTTONS START
//
Item {
id: tabButtonsContainer;
property int numTabs: 5;
// Size
width: root.width;
height: 80;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
}
// "WALLET HOME" tab button
Rectangle {
id: walletHomeButtonContainer;
visible: !notSetUp.visible;
color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
RalewaySemiBold {
text: "WALLET HOME";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.fill: parent;
anchors.leftMargin: 4;
anchors.rightMargin: 4;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
MouseArea {
enabled: !walletSetupLightboxContainer.visible;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
root.activeView = "walletHome";
tabButtonsContainer.resetTabButtonColors();
}
onEntered: parent.color = hifi.colors.blueHighlight;
onExited: parent.color = root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black;
}
onVisibleChanged: {
if (visible) {
commerce.getSecurityImage();
commerce.balance();
}
}
}
// "SEND MONEY" tab button
Rectangle {
id: sendMoneyButtonContainer;
visible: !notSetUp.visible;
color: hifi.colors.black;
anchors.top: parent.top;
anchors.left: walletHomeButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
RalewaySemiBold {
text: "SEND MONEY";
// Text size
size: 14;
// Anchors
anchors.fill: parent;
anchors.leftMargin: 4;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGray50;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
// "EXCHANGE MONEY" tab button
Rectangle {
id: exchangeMoneyButtonContainer;
visible: !notSetUp.visible;
color: hifi.colors.black;
anchors.top: parent.top;
anchors.left: sendMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
RalewaySemiBold {
text: "EXCHANGE MONEY";
// Text size
size: 14;
// Anchors
anchors.fill: parent;
anchors.leftMargin: 4;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGray50;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
// "SECURITY" tab button
Rectangle {
id: securityButtonContainer;
visible: !notSetUp.visible;
color: root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: exchangeMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
RalewaySemiBold {
text: "SECURITY";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.fill: parent;
anchors.leftMargin: 4;
anchors.rightMargin: 4;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
MouseArea {
enabled: !walletSetupLightboxContainer.visible;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
root.activeView = "security";
tabButtonsContainer.resetTabButtonColors();
}
onEntered: parent.color = hifi.colors.blueHighlight;
onExited: parent.color = root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
}
onVisibleChanged: {
if (visible) {
commerce.getSecurityImage();
commerce.getKeyFilePath();
}
}
}
// "HELP" tab button
Rectangle {
id: helpButtonContainer;
visible: !notSetUp.visible;
color: root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: securityButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
RalewaySemiBold {
text: "HELP";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.fill: parent;
anchors.leftMargin: 4;
anchors.rightMargin: 4;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
MouseArea {
enabled: !walletSetupLightboxContainer.visible;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
root.activeView = "help";
tabButtonsContainer.resetTabButtonColors();
}
onEntered: parent.color = hifi.colors.blueHighlight;
onExited: parent.color = root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black;
}
}
function resetTabButtonColors() {
walletHomeButtonContainer.color = hifi.colors.black;
sendMoneyButtonContainer.color = hifi.colors.black;
securityButtonContainer.color = hifi.colors.black;
helpButtonContainer.color = hifi.colors.black;
if (root.activeView === "walletHome") {
walletHomeButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView === "sendMoney") {
sendMoneyButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView === "security") {
securityButtonContainer.color = hifi.colors.blueAccent;
} else if (root.activeView === "help") {
helpButtonContainer.color = hifi.colors.blueAccent;
}
}
}
//
// TAB BUTTONS END
//
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendToScript(var message);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,357 @@
//
// WalletHome.qml
// qml/hifi/commerce/wallet
//
// WalletHome
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (exists) {
// just set the source again (to be sure the change was noticed)
securityImage.source = "";
securityImage.source = "image://security/securityImage";
}
}
onBalanceResult : {
balanceText.text = parseFloat(result.data.balance/100).toFixed(2);
}
}
SecurityImageModel {
id: securityImageModel;
}
Connections {
target: GlobalServices
onMyUsernameChanged: {
usernameText.text = Account.username;
}
}
// Username Text
RalewayRegular {
id: usernameText;
text: Account.username;
// Text size
size: 24;
// Style
color: hifi.colors.faintGray;
elide: Text.ElideRight;
// Anchors
anchors.top: securityImageContainer.top;
anchors.bottom: securityImageContainer.bottom;
anchors.left: parent.left;
anchors.right: hfcBalanceContainer.left;
}
// HFC Balance Container
Item {
id: hfcBalanceContainer;
// Anchors
anchors.top: securityImageContainer.top;
anchors.right: securityImageContainer.left;
anchors.rightMargin: 16;
width: 175;
height: 60;
Rectangle {
id: hfcBalanceField;
anchors.right: parent.right;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
height: parent.height - 15;
// "HFC" balance label
RalewayRegular {
id: balanceLabel;
text: "HFC";
// Text size
size: 20;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: hfcBalanceField.right;
anchors.rightMargin: 4;
width: paintedWidth;
// Style
color: hifi.colors.darkGray;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter;
onVisibleChanged: {
if (visible) {
commerce.balance();
}
}
}
// Balance Text
FiraSansRegular {
id: balanceText;
text: "--";
// Text size
size: 28;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: balanceLabel.left;
anchors.rightMargin: 4;
// Style
color: hifi.colors.darkGray;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter;
}
}
// "balance" text above field
RalewayRegular {
text: "balance";
// Text size
size: 12;
// Anchors
anchors.top: parent.top;
anchors.bottom: hfcBalanceField.top;
anchors.bottomMargin: 4;
anchors.left: hfcBalanceField.left;
anchors.right: hfcBalanceField.right;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Security Image
Item {
id: securityImageContainer;
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
width: 75;
height: childrenRect.height;
onVisibleChanged: {
if (visible) {
commerce.getSecurityImage();
}
}
Image {
id: securityImage;
// Anchors
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: parent.width - 10;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
source: "image://security/securityImage";
}
// "Security picture" text below pic
RalewayRegular {
text: "security picture";
// Text size
size: 12;
// Anchors
anchors.top: securityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
// Recent Activity
Item {
id: recentActivityContainer;
anchors.top: securityImageContainer.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: homeMessage.visible ? homeMessage.top : root.bottom;
anchors.bottomMargin: 10;
RalewayRegular {
id: recentActivityText;
text: "Recent Activity";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 30;
// Text size
size: 22;
// Style
color: hifi.colors.faintGray;
}
Rectangle {
id: transactionHistory;
anchors.top: recentActivityText.bottom;
anchors.topMargin: 4;
anchors.bottom: toggleFullHistoryButton.top;
anchors.bottomMargin: 8;
anchors.left: parent.left;
anchors.right: parent.right;
// some placeholder stuff
RalewayRegular {
text: homeMessage.visible ? "you <b>CANNOT</b> scroll through this." : "you <b>CAN</b> scroll through this";
// Text size
size: 16;
// Anchors
anchors.fill: parent;
// Style
color: hifi.colors.darkGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
HifiControlsUit.Button {
id: toggleFullHistoryButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
width: 250;
height: 40;
text: homeMessage.visible ? "See Full Transaction History" : "Collapse Transaction History";
onClicked: {
if (homeMessage.visible) {
homeMessage.visible = false;
} else {
homeMessage.visible = true;
}
}
}
}
// Item for "messages" - like "Welcome"
Item {
id: homeMessage;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.right: parent.right;
anchors.rightMargin: 20;
height: childrenRect.height;
RalewayRegular {
id: messageText;
text: "<b>Welcome! Let's get you some spending money.</b><br><br>" +
"Now that your account is all set up, click the button below to request your starter money. " +
"A robot will promptly review your request and put money into your account.";
// Text size
size: 16;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 130;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
Item {
id: homeMessageButtons;
anchors.top: messageText.bottom;
anchors.topMargin: 4;
anchors.left: parent.left;
anchors.right: parent.right;
height: 40;
HifiControlsUit.Button {
id: noThanksButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
width: 100;
text: "No Thanks"
onClicked: {
messageText.text = "Okay...weird. Who doesn't like free money? If you change your mind, too bad. Sorry."
homeMessageButtons.visible = false;
}
}
HifiControlsUit.Button {
id: freeMoneyButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
width: 210;
text: "Free Money Please"
onClicked: {
messageText.text = "Go, MoneyRobots, Go!"
homeMessageButtons.visible = false;
}
}
}
}
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,633 @@
//
// WalletSetupLightbox.qml
// qml/hifi/commerce/wallet
//
// WalletSetupLightbox
//
// Created by Zach Fox on 2017-08-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 Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
property string lastPage: "login";
// Style
color: hifi.colors.baseGray;
Hifi.QmlCommerce {
id: commerce;
onLoginStatusResult: {
if (isLoggedIn) {
securityImageContainer.visible = true;
} else {
loginPageContainer.visible = true;
}
}
onSecurityImageResult: {
if (!exists && root.lastPage === "securityImage") {
// ERROR! Invalid security image.
securityImageContainer.visible = true;
choosePassphraseContainer.visible = false;
}
}
onPassphraseSetupStatusResult: {
securityImageContainer.visible = false;
if (passphraseIsSetup) {
privateKeysReadyContainer.visible = true;
} else {
choosePassphraseContainer.visible = true;
}
}
onKeyFilePathResult: {
if (path !== "") {
keyFilePath.text = path;
}
}
}
//
// LOGIN PAGE START
//
Item {
id: loginPageContainer;
visible: false;
// Anchors
anchors.fill: parent;
Component.onCompleted: {
commerce.getLoginStatus();
}
Item {
id: loginTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "WALLET SETUP - LOGIN";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: loginTitleHelper;
text: "Please Log In to High Fidelity";
// Text size
size: 24;
// Anchors
anchors.top: loginTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Text below helper text
RalewaySemiBold {
id: loginDetailText;
text: "To set up your wallet, you must first log in to High Fidelity.";
// Text size
size: 18;
// Anchors
anchors.top: loginTitleHelper.bottom;
anchors.topMargin: 25;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: loginDetailText.bottom;
anchors.topMargin: 25;
anchors.left: parent.left;
anchors.leftMargin: 16;
width: 150;
height: 50;
text: "Log In"
onClicked: {
sendSignalToWallet({method: 'walletSetup_loginClicked'});
}
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
text: "Cancel"
onClicked: {
sendSignalToWallet({method: 'walletSetup_cancelClicked'});
}
}
}
}
//
// LOGIN PAGE END
//
//
// SECURITY IMAGE SELECTION START
//
Item {
id: securityImageContainer;
visible: false;
// Anchors
anchors.fill: parent;
onVisibleChanged: {
if (visible) {
commerce.getSecurityImage();
}
}
Item {
id: securityImageTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "WALLET SETUP - STEP 1 OF 3";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: securityImageTitleHelper;
text: "Choose a Security Picture:";
// Text size
size: 24;
// Anchors
anchors.top: securityImageTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
height: 50;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
SecurityImageSelection {
id: securityImageSelection;
// Anchors
anchors.top: securityImageTitleHelper.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 280;
}
// Text below security images
RalewayRegular {
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
// Text size
size: 18;
// Anchors
anchors.top: securityImageSelection.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
text: "Cancel"
onClicked: {
sendSignalToWallet({method: 'walletSetup_cancelClicked'});
}
}
// "Next" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Next";
onClicked: {
root.lastPage = "securityImage";
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
commerce.chooseSecurityImage(securityImagePath);
securityImageContainer.visible = false;
choosePassphraseContainer.visible = true;
}
}
}
}
//
// SECURITY IMAGE SELECTION END
//
//
// SECURE PASSPHRASE SELECTION START
//
Item {
id: choosePassphraseContainer;
visible: false;
// Anchors
anchors.fill: parent;
onVisibleChanged: {
if (visible) {
commerce.getPassphraseSetupStatus();
}
}
Item {
id: passphraseTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "WALLET SETUP - STEP 2 OF 3";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: passphraseTitleHelper;
text: "Choose a Secure Passphrase";
// Text size
size: 24;
// Anchors
anchors.top: passphraseTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
PassphraseSelection {
id: passphraseSelection;
anchors.top: passphraseTitleHelper.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: passphraseNavBar.top;
}
// Navigation Bar
Item {
id: passphraseNavBar;
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Back" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
text: "Back"
onClicked: {
root.lastPage = "choosePassphrase";
choosePassphraseContainer.visible = false;
securityImageContainer.visible = true;
}
}
// "Next" button
HifiControlsUit.Button {
id: passphrasePageNextButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Next";
onClicked: {
if (passphraseSelection.validateAndSubmitPassphrase()) {
root.lastPage = "passphrase";
choosePassphraseContainer.visible = false;
privateKeysReadyContainer.visible = true;
commerce.balance(); // Do this here so that keys are generated. Order might change as backend changes?
}
}
}
}
}
//
// SECURE PASSPHRASE SELECTION END
//
//
// PRIVATE KEYS READY START
//
Item {
id: privateKeysReadyContainer;
visible: false;
// Anchors
anchors.fill: parent;
Item {
id: keysReadyTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "WALLET SETUP - STEP 3 OF 3";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: keysReadyTitleHelper;
text: "Your Private Keys are Ready";
// Text size
size: 24;
// Anchors
anchors.top: keysReadyTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Text below checkbox
RalewayRegular {
id: explanationText;
text: "Your money and purchases are secured with private keys that only you have access to. " +
"<b>If they are lost, you will not be able to access your money or purchases.</b><br><br>" +
"<b>To protect your privacy, High Fidelity has no access to your private keys and cannot " +
"recover them for any reason.<br><br>To safeguard your private keys, backup this file on a regular basis:</b>";
// Text size
size: 16;
// Anchors
anchors.top: keysReadyTitleHelper.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
HifiControlsUit.TextField {
id: keyFilePath;
anchors.top: explanationText.bottom;
anchors.topMargin: 10;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: clipboardButton.left;
height: 40;
readOnly: true;
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePath();
}
}
}
HifiControlsUit.Button {
id: clipboardButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.rightMargin: 16;
anchors.top: keyFilePath.top;
anchors.bottom: keyFilePath.bottom;
width: height;
HiFiGlyphs {
text: hifi.glyphs.question;
// Size
size: parent.height*1.3;
// Anchors
anchors.fill: parent;
// Style
horizontalAlignment: Text.AlignHCenter;
color: enabled ? hifi.colors.white : hifi.colors.faintGray;
}
onClicked: {
Window.copyToClipboard(keyFilePath.text);
}
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Next" button
HifiControlsUit.Button {
id: keysReadyPageNextButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Finish";
onClicked: {
root.visible = false;
sendSignalToWallet({method: 'walletSetup_finished'});
}
}
}
}
//
// PRIVATE KEYS READY END
//
//
// FUNCTION DEFINITIONS START
//
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

Binary file not shown.

After

(image error) Size: 62 KiB

Binary file not shown.

After

(image error) Size: 99 KiB

Binary file not shown.

After

(image error) Size: 113 KiB

Binary file not shown.

After

(image error) Size: 86 KiB

Binary file not shown.

After

(image error) Size: 61 KiB

Binary file not shown.

After

(image error) Size: 74 KiB

View file

@ -28,6 +28,11 @@ ScrollingWindow {
HifiConstants { id: hifi }
// This is for JS/QML communication, which is unused in the AttachmentsDialog,
// but not having this here results in spurious warnings about a
// missing signal
signal sendToScript(var message);
property var settings: Settings {
category: "AttachmentsDialog"
property alias x: root.x

View file

@ -16,6 +16,7 @@ import Qt.labs.settings 1.0
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows"
import "../"
ScrollingWindow {
id: root
@ -28,10 +29,11 @@ ScrollingWindow {
minSize: Qt.vector2d(424, 300)
HifiConstants { id: hifi }
property var scripts: ScriptDiscoveryService;
property var scriptsModel: scripts.scriptsModelFilter
property var runningScriptsModel: ListModel { }
property bool developerMenuEnabled: false
property bool isHMD: false
Settings {
@ -39,6 +41,28 @@ ScrollingWindow {
property alias x: root.x
property alias y: root.y
}
Component {
id: letterBoxMessage
Window {
implicitWidth: 400
implicitHeight: 300
minSize: Qt.vector2d(424, 300)
DesktopLetterboxMessage {
visible: true
headerGlyph: hifi.glyphs.lock
headerText: "Developer Mode only"
text: ( "In order to edit, delete or reload this script," +
" turn on Developer Mode by going to:" +
" Menu > Settings > Developer Menus")
popupRadius: 0
headerGlyphSize: 20
headerTextMargin: 2
headerGlyphMargin: -3
}
}
}
Timer {
id: refreshTimer
@ -47,6 +71,15 @@ ScrollingWindow {
running: false
onTriggered: updateRunningScripts();
}
Timer {
id: checkMenu
interval: 1000
repeat: true
running: false
onTriggered: developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
}
Component {
id: listModelBuilder
@ -64,6 +97,8 @@ ScrollingWindow {
Component.onCompleted: {
isHMD = HMD.active;
updateRunningScripts();
developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
checkMenu.restart();
}
function updateRunningScripts() {
@ -110,7 +145,17 @@ ScrollingWindow {
function reloadAll() {
console.log("Reload all scripts");
scripts.reloadAllScripts();
if (!developerMenuEnabled) {
for (var index = 0; index < runningScriptsModel.count; index++) {
var url = runningScriptsModel.get(index).url;
var fileName = url.substring(url.lastIndexOf('/')+1);
if (canEditScript(fileName)) {
scripts.stopScript(url, true);
}
}
} else {
scripts.reloadAllScripts();
}
}
function loadDefaults() {
@ -120,7 +165,22 @@ ScrollingWindow {
function stopAll() {
console.log("Stop all scripts");
scripts.stopAllScripts();
for (var index = 0; index < runningScriptsModel.count; index++) {
var url = runningScriptsModel.get(index).url;
var fileName = url.substring(url.lastIndexOf('/')+1);
if (canEditScript(fileName)) {
scripts.stopScript(url);
}
}
}
function canEditScript(script) {
if ((script === "controllerScripts.js") || (script === "defaultScripts.js")) {
return developerMenuEnabled;
}
return true;
}
Column {
@ -146,6 +206,14 @@ ScrollingWindow {
color: hifi.buttons.red
onClicked: stopAll()
}
HifiControls.Button {
text: "Load Defaults"
color: hifi.buttons.black
height: 26
visible: root.developerMenuEnabled;
onClicked: loadDefaults()
}
}
HifiControls.VerticalSpacer {
@ -162,6 +230,7 @@ ScrollingWindow {
expandSelectedRow: true
itemDelegate: Item {
property bool canEdit: canEditScript(styleData.value);
anchors {
left: parent ? parent.left : undefined
leftMargin: hifi.dimensions.tablePadding
@ -185,8 +254,9 @@ ScrollingWindow {
HiFiGlyphs {
id: reloadButton
text: hifi.glyphs.reloadSmall
text: ((canEditScript(styleData.value)) ? hifi.glyphs.reload : hifi.glyphs.lock)
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
size: 21
anchors {
top: parent.top
right: stopButton.left
@ -195,7 +265,13 @@ ScrollingWindow {
MouseArea {
id: reloadButtonArea
anchors { fill: parent; margins: -2 }
onClicked: reloadScript(model.url)
onClicked: {
if (canEdit) {
reloadScript(model.url)
} else {
letterBoxMessage.createObject(desktop)
}
}
}
}
@ -203,6 +279,7 @@ ScrollingWindow {
id: stopButton
text: hifi.glyphs.closeSmall
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
visible: canEditScript(styleData.value)
anchors {
top: parent.top
right: parent.right
@ -211,7 +288,11 @@ ScrollingWindow {
MouseArea {
id: stopButtonArea
anchors { fill: parent; margins: -2 }
onClicked: stopScript(model.url)
onClicked: {
if (canEdit) {
stopScript(model.url);
}
}
}
}
@ -264,13 +345,6 @@ ScrollingWindow {
height: 26
onClickedQueued: ApplicationInterface.loadDialog()
}
HifiControls.Button {
text: "Load Defaults"
color: hifi.buttons.black
height: 26
onClicked: loadDefaults()
}
}
HifiControls.VerticalSpacer {}

View file

@ -16,6 +16,7 @@ import Qt.labs.settings 1.0
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows"
import "../"
Rectangle {
id: root
@ -26,26 +27,90 @@ Rectangle {
property var scripts: ScriptDiscoveryService;
property var scriptsModel: scripts.scriptsModelFilter
property var runningScriptsModel: ListModel { }
property bool developerMenuEnabled: false
property bool isHMD: false
color: hifi.colors.baseGray
LetterboxMessage {
id: letterBoxMessage
z: 999
visible: false
}
function letterBox(glyph, text, message) {
letterBoxMessage.headerGlyph = glyph;
letterBoxMessage.headerText = text;
letterBoxMessage.text = message;
letterBoxMessage.visible = true;
letterBoxMessage.popupRadius = 0;
letterBoxMessage.headerGlyphSize = 20
letterBoxMessage.headerTextMargin = 2
letterBoxMessage.headerGlyphMargin = -3
}
Timer {
id: refreshTimer
interval: 100
repeat: false
running: false
onTriggered: updateRunningScripts();
}
Timer {
id: checkMenu
interval: 1000
repeat: true
running: false
onTriggered: developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
}
Component {
id: listModelBuilder
ListModel {}
}
Connections {
target: ScriptDiscoveryService
onScriptCountChanged: updateRunningScripts();
onScriptCountChanged: {
runningScriptsModel = listModelBuilder.createObject(root);
refreshTimer.restart();
}
}
Component.onCompleted: {
isHMD = HMD.active;
updateRunningScripts();
developerMenuEnabled = MenuInterface.isMenuEnabled("Developer Menus");
checkMenu.restart();
}
function updateRunningScripts() {
var runningScripts = ScriptDiscoveryService.getRunning();
runningScriptsModel.clear()
for (var i = 0; i < runningScripts.length; ++i) {
runningScriptsModel.append(runningScripts[i]);
function simplify(path) {
// trim URI querystring/fragment
path = (path+'').replace(/[#?].*$/,'');
// normalize separators and grab last path segment (ie: just the filename)
path = path.replace(/\\/g, '/').split('/').pop();
// return lowercased because we want to sort mnemonically
return path.toLowerCase();
}
var runningScripts = ScriptDiscoveryService.getRunning();
runningScripts.sort(function(a,b) {
a = simplify(a.path);
b = simplify(b.path);
return a < b ? -1 : a > b ? 1 : 0;
});
// Calling `runningScriptsModel.clear()` here instead of creating a new object
// triggers some kind of weird heap corruption deep inside Qt. So instead of
// modifying the model in place, possibly triggering behaviors in the table
// instead we create a new `ListModel`, populate it and update the
// existing model atomically.
var newRunningScriptsModel = listModelBuilder.createObject(root);
for (var i = 0; i < runningScripts.length; ++i) {
newRunningScriptsModel.append(runningScripts[i]);
}
runningScriptsModel = newRunningScriptsModel;
}
function loadScript(script) {
@ -65,7 +130,17 @@ Rectangle {
function reloadAll() {
console.log("Reload all scripts");
scripts.reloadAllScripts();
if (!developerMenuEnabled) {
for (var index = 0; index < runningScriptsModel.count; index++) {
var url = runningScriptsModel.get(index).url;
var fileName = url.substring(url.lastIndexOf('/')+1);
if (canEditScript(fileName)) {
scripts.stopScript(url, true);
}
}
} else {
scripts.reloadAllScripts();
}
}
function loadDefaults() {
@ -75,7 +150,22 @@ Rectangle {
function stopAll() {
console.log("Stop all scripts");
scripts.stopAllScripts();
for (var index = 0; index < runningScriptsModel.count; index++) {
var url = runningScriptsModel.get(index).url;
console.log(url);
var fileName = url.substring(url.lastIndexOf('/')+1);
if (canEditScript(fileName)) {
scripts.stopScript(url);
}
}
}
function canEditScript(script) {
if ((script === "controllerScripts.js") || (script === "defaultScripts.js")) {
return developerMenuEnabled;
}
return true;
}
Flickable {
@ -110,6 +200,14 @@ Rectangle {
color: hifi.buttons.red
onClicked: stopAll()
}
HifiControls.Button {
text: "Load Defaults"
color: hifi.buttons.black
height: 26
visible: root.developerMenuEnabled;
onClicked: loadDefaults()
}
}
HifiControls.VerticalSpacer {
@ -125,6 +223,7 @@ Rectangle {
expandSelectedRow: true
itemDelegate: Item {
property bool canEdit: canEditScript(styleData.value);
anchors {
left: parent ? parent.left : undefined
leftMargin: hifi.dimensions.tablePadding
@ -148,8 +247,9 @@ Rectangle {
HiFiGlyphs {
id: reloadButton
text: hifi.glyphs.reloadSmall
text: ((canEditScript(styleData.value)) ? hifi.glyphs.reload : hifi.glyphs.lock)
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
size: 21
anchors {
top: parent.top
right: stopButton.left
@ -158,7 +258,17 @@ Rectangle {
MouseArea {
id: reloadButtonArea
anchors { fill: parent; margins: -2 }
onClicked: reloadScript(model.url)
onClicked: {
if (canEdit) {
reloadScript(model.url)
} else {
letterBox(hifi.glyphs.lock,
"Developer Mode only",
"In order to edit, delete or reload this script," +
" turn on Developer Mode by going to:" +
" Menu > Settings > Developer Menus");
}
}
}
}
@ -166,6 +276,7 @@ Rectangle {
id: stopButton
text: hifi.glyphs.closeSmall
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
visible: canEditScript(styleData.value)
anchors {
top: parent.top
right: parent.right
@ -174,7 +285,11 @@ Rectangle {
MouseArea {
id: stopButtonArea
anchors { fill: parent; margins: -2 }
onClicked: stopScript(model.url)
onClicked: {
if (canEdit) {
stopScript(model.url)
}
}
}
}
@ -250,13 +365,6 @@ Rectangle {
onTriggered: ApplicationInterface.loadDialog();
}
}
HifiControls.Button {
text: "Load Defaults"
color: hifi.buttons.black
height: 26
onClicked: loadDefaults()
}
}
HifiControls.VerticalSpacer {}

View file

@ -50,7 +50,7 @@ Item {
margins: 4
}
clip: true
snapMode: ListView.SnapToItem
cacheBuffer: 4000
model: ListModel {}
delegate: Item {

View file

@ -145,12 +145,13 @@ Rectangle {
visible: headPuckBox.checked
HifiControls.SpinBox {
id: headYOffset
decimals: 4
decimals: 1
width: 112
label: "Y: offset"
label: "Y Offset"
suffix: " cm"
minimumValue: -10
stepSize: 0.0254
value: -0.05
stepSize: 1
value: -5
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
@ -162,11 +163,12 @@ Rectangle {
HifiControls.SpinBox {
id: headZOffset
width: 112
label: "Z: offset"
label: "Z Offset"
minimumValue: -10
stepSize: 0.0254
decimals: 4
value: -0.05
stepSize: 1
decimals: 1
suffix: " cm"
value: -5
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
@ -175,7 +177,6 @@ Rectangle {
}
}
RalewayBold {
id: hands
@ -254,11 +255,12 @@ Rectangle {
HifiControls.SpinBox {
id: handYOffset
decimals: 4
decimals: 1
width: 112
label: "Y: offset"
suffix: " cm"
label: "Y Offset"
minimumValue: -10
stepSize: 0.0254
stepSize: 1
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
@ -270,10 +272,11 @@ Rectangle {
HifiControls.SpinBox {
id: handZOffset
width: 112
label: "Z: offset"
label: "Z Offset"
suffix: " cm"
minimumValue: -10
stepSize: 0.0254
decimals: 4
stepSize: 1
decimals: 1
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
@ -488,15 +491,55 @@ Rectangle {
}
}
Row {
id: shoulderAdditionalConfig
visible: shoulderBox.checked
anchors.top: shoulderConfig.bottom
anchors.topMargin: 5
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 20
spacing: 10
HifiControls.SpinBox {
id: armCircumference
decimals: 1
width: 160
suffix: " cm"
label: "Arm Circumference"
minimumValue: 0
stepSize: 1.0
colorScheme: hifi.colorSchemes.dark
value: 33.0
onEditingFinished: {
sendConfigurationSettings();
}
}
HifiControls.SpinBox {
id: shoulderWidth
width: 160
label: "Shoulder Width"
suffix: " cm"
minimumValue: 0
stepSize: 1.0
decimals: 1
colorScheme: hifi.colorSchemes.dark
value: 48
onEditingFinished: {
sendConfigurationSettings();
}
}
}
Separator {
id: bottomSeperator
width: parent.width
anchors.top: shoulderConfig.bottom
anchors.topMargin: 10
anchors.top: shoulderAdditionalConfig.visible ? shoulderAdditionalConfig.bottom : shoulderConfig.bottom
anchors.topMargin: (shoulderAdditionalConfig.visible ? 25 : 10)
}
Rectangle {
id: calibrationButton
property int color: hifi.buttons.blue
@ -835,6 +878,9 @@ Rectangle {
var viveController = settings["handController"];
var desktopMode = settings["desktopMode"];
armCircumference.value = settings.armCircumference;
shoulderWidth.value = settings.shoulderWidth;
if (HmdHead) {
headBox.checked = true;
headPuckBox.checked = false;
@ -1010,6 +1056,8 @@ Rectangle {
"bodyConfiguration": trackerConfiguration,
"headConfiguration": headObject,
"handConfiguration": handObject,
"armCircumference": armCircumference.value,
"shoulderWidth": shoulderWidth.value,
"desktopMode": viveInDesktop.checked
}

View file

@ -39,11 +39,24 @@ StackView {
property var rpcCounter: 0;
signal sendToScript(var message);
function rpc(method, parameters, callback) {
console.debug('TabletAddressDialog: rpc: method = ', method, 'parameters = ', parameters, 'callback = ', callback)
rpcCalls[rpcCounter] = callback;
var message = {method: method, params: parameters, id: rpcCounter++, jsonrpc: "2.0"};
sendToScript(message);
}
function fromScript(message) {
if (message.method === 'refreshFeeds') {
var feeds = [happeningNow, places, snapshots];
console.debug('TabletAddressDialog::fromScript: refreshFeeds', 'feeds = ', feeds);
feeds.forEach(function(feed) {
Qt.callLater(feed.fillDestinations);
});
return;
}
var callback = rpcCalls[message.id];
if (!callback) {
console.log('No callback for message fromScript', JSON.stringify(message));

View file

@ -116,6 +116,7 @@ Item {
anchors.fill: parent
hoverEnabled: true
enabled: true
preventStealing: true
onClicked: {
console.log("Tablet Button Clicked!");
if (tabletButton.inDebugMode) {

View file

@ -114,6 +114,7 @@ Item {
}
function clearMenus() {
topMenu = null
d.clear()
}

View file

@ -93,6 +93,11 @@ Item {
loader.source = "";
loader.source = "TabletWebView.qml";
}
function loadTabletWebBase() {
loader.source = "";
loader.source = "../../controls/TabletWebView.qml";
}
function returnToPreviousApp() {
tabletApps.remove(currentApp);
@ -121,6 +126,9 @@ Item {
loader.item.url = url;
loader.item.scriptURL = injectedJavaScriptUrl;
tabletApps.append({"appUrl": "TabletWebView.qml", "isWebUrl": true, "scriptUrl": injectedJavaScriptUrl, "appWebUrl": url});
if (loader.item.hasOwnProperty("closeButtonVisible")) {
loader.item.closeButtonVisible = false;
}
}
// used to send a message from qml to interface script.
@ -202,5 +210,11 @@ Item {
width: 480
height: 706
function setShown(value) {}
function setShown(value) {
if (value === true) {
HMD.openTablet()
} else {
HMD.closeTablet()
}
}
}

View file

@ -42,9 +42,17 @@ Windows.ScrollingWindow {
loader.source = "WindowWebView.qml";
}
function loadTabletWebBase() {
loader.source = "";
loader.source = "../../controls/TabletWebView.qml";
}
function loadWebUrl(url, injectedJavaScriptUrl) {
loader.item.url = url;
loader.item.scriptURL = injectedJavaScriptUrl;
if (loader.item.hasOwnProperty("closeButtonVisible")) {
loader.item.closeButtonVisible = false;
}
}
// used to send a message from qml to interface script.

View file

@ -337,5 +337,6 @@ Item {
readonly property string playback_play: "\ue01d"
readonly property string stop_square: "\ue01e"
readonly property string avatarTPose: "\ue01f"
readonly property string lock: "\ue006"
}
}

View file

@ -0,0 +1,30 @@
//
// Created by Bradley Austin Davis on 2016/07/11
// Copyright 2013-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
//
uniform sampler2D sampler;
struct OverlayData {
mat4 mvp;
float alpha;
};
layout(std140) uniform overlayBuffer {
OverlayData overlay;
};
in vec2 vTexCoord;
out vec4 FragColor;
void main() {
FragColor = texture(sampler, vTexCoord);
FragColor.a *= overlay.alpha;
if (FragColor.a <= 0.0) {
discard;
}
}

View file

@ -8,12 +8,7 @@
struct OverlayData {
mat4 mvp;
vec4 glowPoints;
vec4 glowColors[2];
vec4 resolutionRadiusAlpha;
vec4 extraGlowColor;
vec2 extraGlowPoint;
float alpha;
};
layout(std140) uniform overlayBuffer {
@ -25,11 +20,9 @@ mat4 mvp = overlay.mvp;
layout(location = 0) in vec3 Position;
layout(location = 3) in vec2 TexCoord;
out vec3 vPosition;
out vec2 vTexCoord;
void main() {
gl_Position = mvp * vec4(Position, 1);
vTexCoord = TexCoord;
vPosition = Position;
}

View file

@ -1,86 +0,0 @@
//
// Created by Bradley Austin Davis on 2016/07/11
// Copyright 2013-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
//
uniform sampler2D sampler;
struct OverlayData {
mat4 mvp;
vec4 glowPoints;
vec4 glowColors[2];
vec4 resolutionRadiusAlpha;
vec4 extraGlowColor;
vec2 extraGlowPoint;
};
layout(std140) uniform overlayBuffer {
OverlayData overlay;
};
vec2 resolution = overlay.resolutionRadiusAlpha.xy;
float radius = overlay.resolutionRadiusAlpha.z;
float alpha = overlay.resolutionRadiusAlpha.w;
vec4 glowPoints = overlay.glowPoints;
vec4 glowColors[2] = overlay.glowColors;
vec2 extraGlowPoint = overlay.extraGlowPoint;
vec4 extraGlowColor = overlay.extraGlowColor;
in vec3 vPosition;
in vec2 vTexCoord;
out vec4 FragColor;
float easeInOutCubic(float f) {
const float d = 1.0;
const float b = 0.0;
const float c = 1.0;
float t = f;
if ((t /= d / 2.0) < 1.0) return c / 2.0 * t * t * t + b;
return c / 2.0 * ((t -= 2.0) * t * t + 2.0) + b;
}
void main() {
FragColor = texture(sampler, vTexCoord);
vec2 aspect = resolution;
aspect /= resolution.x;
float glowIntensity = 0.0;
float dist1 = distance(vTexCoord * aspect, glowPoints.xy * aspect);
float dist2 = distance(vTexCoord * aspect, glowPoints.zw * aspect);
float dist3 = distance(vTexCoord * aspect, extraGlowPoint * aspect);
float distX = min(dist1, dist2);
float dist = min(distX, dist3);
vec3 glowColor = glowColors[0].rgb;
if (dist2 < dist1) {
glowColor = glowColors[1].rgb;
}
if (dist3 < dist2) {
glowColor = extraGlowColor.rgb;
}
if (dist <= radius) {
glowIntensity = 1.0 - (dist / radius);
glowColor.rgb = pow(glowColor, vec3(1.0 - glowIntensity));
glowIntensity = easeInOutCubic(glowIntensity);
glowIntensity = pow(glowIntensity, 0.5);
}
if (alpha <= 0.0) {
if (glowIntensity <= 0.0) {
discard;
}
FragColor = vec4(glowColor, glowIntensity);
return;
}
FragColor.rgb = mix(FragColor.rgb, glowColor.rgb, glowIntensity);
FragColor.a *= alpha;
}

View file

@ -70,7 +70,7 @@
#include <EntityScriptClient.h>
#include <EntityScriptServerLogClient.h>
#include <EntityScriptingInterface.h>
#include <HoverOverlayInterface.h>
#include "ui/overlays/ContextOverlayInterface.h"
#include <ErrorDialog.h>
#include <FileScriptingInterface.h>
#include <Finally.h>
@ -183,6 +183,7 @@
#include "ui/UpdateDialog.h"
#include "ui/overlays/Overlays.h"
#include "ui/DomainConnectionModel.h"
#include "ui/ImageProvider.h"
#include "Util.h"
#include "InterfaceParentFinder.h"
#include "ui/OctreeStatsProvider.h"
@ -194,6 +195,13 @@
#include <EntityScriptClient.h>
#include <ModelScriptingInterface.h>
#include <raypick/RayPickScriptingInterface.h>
#include <raypick/LaserPointerScriptingInterface.h>
#include "commerce/Ledger.h"
#include "commerce/Wallet.h"
#include "commerce/QmlCommerce.h"
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
// FIXME seems to be broken.
#if defined(Q_OS_WIN)
@ -222,6 +230,7 @@ static const int MIN_PROCESSING_THREAD_POOL_SIZE = 1;
static const QString SNAPSHOT_EXTENSION = ".jpg";
static const QString SVO_EXTENSION = ".svo";
static const QString SVO_JSON_EXTENSION = ".svo.json";
static const QString JSON_GZ_EXTENSION = ".json.gz";
static const QString JSON_EXTENSION = ".json";
static const QString JS_EXTENSION = ".js";
static const QString FST_EXTENSION = ".fst";
@ -229,6 +238,7 @@ static const QString FBX_EXTENSION = ".fbx";
static const QString OBJ_EXTENSION = ".obj";
static const QString AVA_JSON_EXTENSION = ".ava.json";
static const QString WEB_VIEW_TAG = "noDownload=true";
static const QString ZIP_EXTENSION = ".zip";
static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f;
@ -254,13 +264,17 @@ static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
static const QString DOMAIN_SPAWNING_POINT = "/0, -10, 0";
const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensions {
{ SVO_EXTENSION, &Application::importSVOFromURL },
{ SVO_JSON_EXTENSION, &Application::importSVOFromURL },
{ AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl },
{ JSON_EXTENSION, &Application::importJSONFromURL },
{ JS_EXTENSION, &Application::askToLoadScript },
{ FST_EXTENSION, &Application::askToSetAvatarUrl }
{ FST_EXTENSION, &Application::askToSetAvatarUrl },
{ JSON_GZ_EXTENSION, &Application::askToReplaceDomainContent },
{ ZIP_EXTENSION, &Application::importFromZIP }
};
class DeadlockWatchdogThread : public QThread {
@ -595,7 +609,12 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<Snapshot>();
DependencyManager::set<CloseEventSender>();
DependencyManager::set<ResourceManager>();
DependencyManager::set<HoverOverlayInterface>();
DependencyManager::set<ContextOverlayInterface>();
DependencyManager::set<Ledger>();
DependencyManager::set<Wallet>();
DependencyManager::set<LaserPointerScriptingInterface>();
DependencyManager::set<RayPickScriptingInterface>();
return previousSessionCrashed;
}
@ -705,7 +724,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
qInstallMessageHandler(messageHandler);
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf");
_window->setWindowTitle("Interface");
_window->setWindowTitle("High Fidelity Interface");
Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us
@ -956,6 +975,53 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
// Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success.
DependencyManager::get<GeometryCache>()->initializeShapePipelines();
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
static const QString TESTER = "HIFI_TESTER";
auto gpuIdent = GPUIdent::getInstance();
auto glContextData = getGLContextData();
QJsonObject properties = {
{ "version", applicationVersion() },
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) },
{ "previousSessionCrashed", _previousSessionCrashed },
{ "previousSessionRuntime", sessionRunTime.get() },
{ "cpu_architecture", QSysInfo::currentCpuArchitecture() },
{ "kernel_type", QSysInfo::kernelType() },
{ "kernel_version", QSysInfo::kernelVersion() },
{ "os_type", QSysInfo::productType() },
{ "os_version", QSysInfo::productVersion() },
{ "gpu_name", gpuIdent->getName() },
{ "gpu_driver", gpuIdent->getDriver() },
{ "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) },
{ "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) },
{ "gl_version", glContextData["version"] },
{ "gl_vender", glContextData["vendor"] },
{ "gl_sl_version", glContextData["sl_version"] },
{ "gl_renderer", glContextData["renderer"] },
{ "ideal_thread_count", QThread::idealThreadCount() }
};
auto macVersion = QSysInfo::macVersion();
if (macVersion != QSysInfo::MV_None) {
properties["os_osx_version"] = QSysInfo::macVersion();
}
auto windowsVersion = QSysInfo::windowsVersion();
if (windowsVersion != QSysInfo::WV_None) {
properties["os_win_version"] = QSysInfo::windowsVersion();
}
ProcessorInfo procInfo;
if (getProcessorInfo(procInfo)) {
properties["processor_core_count"] = procInfo.numProcessorCores;
properties["logical_processor_count"] = procInfo.numLogicalProcessors;
properties["processor_l1_cache_count"] = procInfo.numProcessorCachesL1;
properties["processor_l2_cache_count"] = procInfo.numProcessorCachesL2;
properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3;
}
// add firstRun flag from settings to launch event
Setting::Handle<bool> firstRun { Settings::firstRun, true };
// once the settings have been loaded, check if we need to flip the default for UserActivityLogger
@ -1327,12 +1393,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Keyboard focus handling for Web overlays.
auto overlays = &(qApp->getOverlays());
connect(overlays, &Overlays::mousePressOnOverlay, [=](OverlayID overlayID, const PointerEvent& event) {
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
setKeyboardFocusOverlay(overlayID);
connect(overlays, &Overlays::mousePressOnOverlay, [=](const OverlayID& overlayID, const PointerEvent& event) {
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(overlays->getOverlay(overlayID));
// Only Web overlays can have keyboard focus.
if (thisOverlay) {
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
setKeyboardFocusOverlay(overlayID);
}
});
connect(overlays, &Overlays::overlayDeleted, [=](OverlayID overlayID) {
connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) {
if (overlayID == _keyboardFocusedOverlay.get()) {
setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
}
@ -1347,6 +1417,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
});
connect(overlays,
SIGNAL(mousePressOnOverlay(const OverlayID&, const PointerEvent&)),
DependencyManager::get<ContextOverlayInterface>().data(),
SLOT(contextOverlays_mousePressOnOverlay(const OverlayID&, const PointerEvent&)));
connect(overlays,
SIGNAL(hoverEnterOverlay(const OverlayID&, const PointerEvent&)),
DependencyManager::get<ContextOverlayInterface>().data(),
SLOT(contextOverlays_hoverEnterOverlay(const OverlayID&, const PointerEvent&)));
connect(overlays,
SIGNAL(hoverLeaveOverlay(const OverlayID&, const PointerEvent&)),
DependencyManager::get<ContextOverlayInterface>().data(),
SLOT(contextOverlays_hoverLeaveOverlay(const OverlayID&, const PointerEvent&)));
// Add periodic checks to send user activity data
static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000;
static int NEARBY_AVATAR_RADIUS_METERS = 10;
@ -1473,7 +1558,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
properties["atp_mapping_requests"] = atpMappingRequests;
properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false;
QJsonObject bytesDownloaded;
bytesDownloaded["atp"] = statTracker->getStat(STAT_ATP_RESOURCE_TOTAL_BYTES).toInt();
bytesDownloaded["http"] = statTracker->getStat(STAT_HTTP_RESOURCE_TOTAL_BYTES).toInt();
@ -1654,6 +1739,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged);
// Setup the mouse ray pick and related operators
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickID(_rayPickManager.createRayPick(
RayPickFilter(DependencyManager::get<RayPickScriptingInterface>()->PICK_ENTITIES() | DependencyManager::get<RayPickScriptingInterface>()->PICK_INCLUDE_NONCOLLIDABLE()),
0.0f, true));
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([&](QUuid rayPickID) {
RayToEntityIntersectionResult entityResult;
RayPickResult result = _rayPickManager.getPrevRayPickResult(rayPickID);
entityResult.intersects = result.type != DependencyManager::get<RayPickScriptingInterface>()->INTERSECTED_NONE();
if (entityResult.intersects) {
entityResult.intersection = result.intersection;
entityResult.distance = result.distance;
entityResult.surfaceNormal = result.surfaceNormal;
entityResult.entityID = result.objectID;
entityResult.entity = DependencyManager::get<EntityTreeRenderer>()->getTree()->findEntityByID(entityResult.entityID);
}
return entityResult;
});
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](QUuid rayPickID, bool value) {
_rayPickManager.setPrecisionPicking(rayPickID, value);
});
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
}
@ -2039,6 +2145,7 @@ void Application::initializeUi() {
LoginDialog::registerType();
Tooltip::registerType();
UpdateDialog::registerType();
QmlCommerce::registerType();
qmlRegisterType<ResourceImageItem>("Hifi", 1, 0, "ResourceImageItem");
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
@ -2062,6 +2169,9 @@ void Application::initializeUi() {
qApp->quit();
});
// register the pixmap image provider (used only for security image, for now)
engine->addImageProvider(ImageProvider::PROVIDER_NAME, new ImageProvider());
setupPreferences();
// For some reason there is already an "Application" object in the QML context,
@ -2134,7 +2244,7 @@ void Application::initializeUi() {
surfaceContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
surfaceContext->setContextProperty("AvatarInputs", AvatarInputs::getInstance());
surfaceContext->setContextProperty("HoverOverlay", DependencyManager::get<HoverOverlayInterface>().data());
surfaceContext->setContextProperty("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
@ -2240,7 +2350,7 @@ void Application::paintGL() {
QMutexLocker viewLocker(&_viewMutex);
_viewFrustum.calculate();
}
renderArgs = RenderArgs(_gpuContext, getEntities(), lodManager->getOctreeSizeScale(),
renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(),
lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
{
@ -2326,7 +2436,7 @@ void Application::paintGL() {
}
} else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
if (isHMDMode()) {
auto mirrorBodyOrientation = myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
auto mirrorBodyOrientation = myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix());
// Mirror HMD yaw and roll
@ -2348,7 +2458,7 @@ void Application::paintGL() {
+ mirrorBodyOrientation * glm::vec3(0.0f, 0.0f, 1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror
+ mirrorBodyOrientation * hmdOffset);
} else {
_myCamera.setOrientation(myAvatar->getWorldAlignedOrientation()
_myCamera.setOrientation(myAvatar->getOrientation()
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
+ glm::vec3(0, _raiseMirror * myAvatar->getUniformScale(), 0)
@ -2393,6 +2503,7 @@ void Application::paintGL() {
finalFramebuffer = framebufferCache->getFramebuffer();
}
mat4 eyeProjections[2];
{
PROFILE_RANGE(render, "/mainRender");
PerformanceTimer perfTimer("mainRender");
@ -2414,7 +2525,6 @@ void Application::paintGL() {
_myCamera.setProjection(displayPlugin->getCullingProjection(_myCamera.getProjection()));
renderArgs._context->enableStereo(true);
mat4 eyeOffsets[2];
mat4 eyeProjections[2];
auto baseProjection = renderArgs.getViewFrustum().getProjection();
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
float IPDScale = hmdInterface->getIPDScale();
@ -2445,6 +2555,19 @@ void Application::paintGL() {
displaySide(&renderArgs, _myCamera);
}
gpu::Batch postCompositeBatch;
{
PROFILE_RANGE(render, "/postComposite");
PerformanceTimer perfTimer("postComposite");
renderArgs._batch = &postCompositeBatch;
renderArgs._batch->setViewportTransform(ivec4(0, 0, finalFramebufferSize.width(), finalFramebufferSize.height()));
renderArgs._batch->setViewTransform(renderArgs.getViewFrustum().getView());
for_each_eye([&](Eye eye) {
renderArgs._batch->setProjectionTransform(eyeProjections[eye]);
_overlays.render3DHUDOverlays(&renderArgs);
});
}
auto frame = _gpuContext->endFrame();
frame->frameIndex = _frameCount;
frame->framebuffer = finalFramebuffer;
@ -2452,6 +2575,7 @@ void Application::paintGL() {
DependencyManager::get<FramebufferCache>()->releaseFramebuffer(framebuffer);
};
frame->overlay = _applicationOverlay.getOverlayTexture();
frame->postCompositeBatch = postCompositeBatch;
// deliver final scene rendering commands to the display plugin
{
PROFILE_RANGE(render, "/pluginOutput");
@ -2740,7 +2864,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
bool Application::importJSONFromURL(const QString& urlString) {
// we only load files that terminate in just .json (not .svo.json and not .ava.json)
// if they come from the High Fidelity Marketplace Assets CDN
QUrl jsonURL { urlString };
if (jsonURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) {
@ -2756,6 +2879,25 @@ bool Application::importSVOFromURL(const QString& urlString) {
return true;
}
bool Application::importFromZIP(const QString& filePath) {
qDebug() << "A zip file has been dropped in: " << filePath;
QUrl empty;
// handle Blocks download from Marketplace
if (filePath.contains("vr.google.com/downloads")) {
addAssetToWorldFromURL(filePath);
} else {
qApp->getFileDownloadInterface()->runUnzip(filePath, empty, true, true, false);
}
return true;
}
void Application::onPresent(quint32 frameCount) {
if (shouldPaint()) {
postEvent(this, new QEvent(static_cast<QEvent::Type>(Idle)), Qt::HighEventPriority);
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
}
}
bool _renderRequested { false };
bool Application::event(QEvent* event) {
@ -2772,23 +2914,9 @@ bool Application::event(QEvent* event) {
// Explicit idle keeps the idle running at a lower interval, but without any rendering
// see (windowMinimizedChanged)
case Event::Idle:
{
float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed();
_lastTimeUpdated.start();
idle(nsecsElapsed);
}
return true;
case Event::Present:
if (!_renderRequested) {
float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed();
if (shouldPaint(nsecsElapsed)) {
_renderRequested = true;
_lastTimeUpdated.start();
idle(nsecsElapsed);
postEvent(this, new QEvent(static_cast<QEvent::Type>(Paint)), Qt::HighEventPriority);
}
}
idle();
// Clear the event queue of pending idle calls
removePostedEvents(this, Idle);
return true;
case Event::Paint:
@ -2796,9 +2924,8 @@ bool Application::event(QEvent* event) {
// or AvatarInputs will mysteriously move to the bottom-right
AvatarInputs::getInstance()->update();
paintGL();
// wait for the next present event before starting idle / paint again
removePostedEvents(this, Present);
_renderRequested = false;
// Clear the event queue of pending paint calls
removePostedEvents(this, Paint);
return true;
default:
@ -3617,7 +3744,7 @@ bool Application::acceptSnapshot(const QString& urlString) {
static uint32_t _renderedFrameIndex { INVALID_FRAME };
bool Application::shouldPaint(float nsecsElapsed) {
bool Application::shouldPaint() {
if (_aboutToQuit) {
return false;
}
@ -3638,10 +3765,8 @@ bool Application::shouldPaint(float nsecsElapsed) {
}
#endif
float msecondsSinceLastUpdate = nsecsElapsed / NSECS_PER_USEC / USECS_PER_MSEC;
// Throttle if requested
if (displayPlugin->isThrottled() && (msecondsSinceLastUpdate < THROTTLED_SIM_FRAME_PERIOD_MS)) {
if (displayPlugin->isThrottled() && (_lastTimeUpdated.elapsed() < THROTTLED_SIM_FRAME_PERIOD_MS)) {
return false;
}
@ -3858,7 +3983,7 @@ void setupCpuMonitorThread() {
#endif
void Application::idle(float nsecsElapsed) {
void Application::idle() {
PerformanceTimer perfTimer("idle");
// Update the deadlock watchdog
@ -3915,7 +4040,8 @@ void Application::idle(float nsecsElapsed) {
steamClient->runCallbacks();
}
float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND;
float secondsSinceLastUpdate = (float)_lastTimeUpdated.nsecsElapsed() / NSECS_PER_MSEC / MSECS_PER_SECOND;
_lastTimeUpdated.start();
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
@ -4149,6 +4275,7 @@ void Application::loadSettings() {
//DependencyManager::get<LODManager>()->setAutomaticLODAdjust(false);
Menu::getInstance()->loadSettings();
// If there is a preferred plugin, we probably messed it up with the menu settings, so fix it.
auto pluginManager = PluginManager::getInstance();
auto plugins = pluginManager->getPreferredDisplayPlugins();
@ -4162,24 +4289,44 @@ void Application::loadSettings() {
break;
}
}
} else {
}
Setting::Handle<bool> firstRun { Settings::firstRun, true };
bool isFirstPerson = false;
if (firstRun.get()) {
// If this is our first run, and no preferred devices were set, default to
// an HMD device if available.
Setting::Handle<bool> firstRun { Settings::firstRun, true };
if (firstRun.get()) {
auto displayPlugins = pluginManager->getDisplayPlugins();
for (auto& plugin : displayPlugins) {
if (plugin->isHmd()) {
if (auto action = menu->getActionForOption(plugin->getName())) {
action->setChecked(true);
action->trigger();
break;
}
auto displayPlugins = pluginManager->getDisplayPlugins();
for (auto& plugin : displayPlugins) {
if (plugin->isHmd()) {
if (auto action = menu->getActionForOption(plugin->getName())) {
action->setChecked(true);
action->trigger();
break;
}
}
}
isFirstPerson = (qApp->isHMDMode());
} else {
// if this is not the first run, the camera will be initialized differently depending on user settings
if (qApp->isHMDMode()) {
// if the HMD is active, use first-person camera, unless the appropriate setting is checked
isFirstPerson = menu->isOptionChecked(MenuOption::FirstPersonHMD);
} else {
// if HMD is not active, only use first person if the menu option is checked
isFirstPerson = menu->isOptionChecked(MenuOption::FirstPerson);
}
}
// finish initializing the camera, based on everything we checked above. Third person camera will be used if no settings
// dictated that we should be in first person
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson);
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson);
_myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON);
cameraMenuChanged();
auto inputs = pluginManager->getInputPlugins();
for (auto plugin : inputs) {
if (!plugin->isActive()) {
@ -4228,7 +4375,6 @@ void Application::init() {
DependencyManager::get<DeferredLightingEffect>()->init();
DependencyManager::get<AvatarManager>()->init();
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
_timerStart.start();
_lastTimeUpdated.start();
@ -4383,10 +4529,9 @@ void Application::updateMyAvatarLookAtPosition() {
}
} else {
// I am not looking at anyone else, so just look forward
auto headPose = myAvatar->getHeadControllerPoseInSensorFrame();
auto headPose = myAvatar->getControllerPoseInWorldFrame(controller::Action::HEAD);
if (headPose.isValid()) {
glm::mat4 worldHeadMat = myAvatar->getSensorToWorldMatrix() * headPose.getMatrix();
lookAtSpot = transformPoint(worldHeadMat, glm::vec3(0.0f, 0.0f, TREE_SCALE));
lookAtSpot = transformPoint(headPose.getMatrix(), glm::vec3(0.0f, 0.0f, TREE_SCALE));
} else {
lookAtSpot = myAvatar->getHead()->getEyePosition() +
(myAvatar->getHead()->getFinalOrientationInWorldFrame() * glm::vec3(0.0f, 0.0f, -TREE_SCALE));
@ -4489,11 +4634,9 @@ void Application::cameraModeChanged() {
void Application::cameraMenuChanged() {
auto menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
if (isHMDMode()) {
menu->setIsOptionChecked(MenuOption::FullscreenMirror, false);
menu->setIsOptionChecked(MenuOption::FirstPerson, true);
} else if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
if (_myCamera.getMode() != CAMERA_MODE_MIRROR) {
_myCamera.setMode(CAMERA_MODE_MIRROR);
getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers
}
} else if (menu->isOptionChecked(MenuOption::FirstPerson)) {
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
@ -4802,52 +4945,76 @@ void Application::update(float deltaTime) {
myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
}
controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
controller::Pose rightHandPose = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
auto myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
auto worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix());
auto avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
myAvatar->setHandControllerPosesInSensorFrame(leftHandPose.transform(avatarToSensorMatrix), rightHandPose.transform(avatarToSensorMatrix));
static const std::vector<controller::Action> avatarControllerActions = {
controller::Action::LEFT_HAND,
controller::Action::RIGHT_HAND,
controller::Action::LEFT_FOOT,
controller::Action::RIGHT_FOOT,
controller::Action::HIPS,
controller::Action::SPINE2,
controller::Action::HEAD,
controller::Action::LEFT_HAND_THUMB1,
controller::Action::LEFT_HAND_THUMB2,
controller::Action::LEFT_HAND_THUMB3,
controller::Action::LEFT_HAND_THUMB4,
controller::Action::LEFT_HAND_INDEX1,
controller::Action::LEFT_HAND_INDEX2,
controller::Action::LEFT_HAND_INDEX3,
controller::Action::LEFT_HAND_INDEX4,
controller::Action::LEFT_HAND_MIDDLE1,
controller::Action::LEFT_HAND_MIDDLE2,
controller::Action::LEFT_HAND_MIDDLE3,
controller::Action::LEFT_HAND_MIDDLE4,
controller::Action::LEFT_HAND_RING1,
controller::Action::LEFT_HAND_RING2,
controller::Action::LEFT_HAND_RING3,
controller::Action::LEFT_HAND_RING4,
controller::Action::LEFT_HAND_PINKY1,
controller::Action::LEFT_HAND_PINKY2,
controller::Action::LEFT_HAND_PINKY3,
controller::Action::LEFT_HAND_PINKY4,
controller::Action::RIGHT_HAND_THUMB1,
controller::Action::RIGHT_HAND_THUMB2,
controller::Action::RIGHT_HAND_THUMB3,
controller::Action::RIGHT_HAND_THUMB4,
controller::Action::RIGHT_HAND_INDEX1,
controller::Action::RIGHT_HAND_INDEX2,
controller::Action::RIGHT_HAND_INDEX3,
controller::Action::RIGHT_HAND_INDEX4,
controller::Action::RIGHT_HAND_MIDDLE1,
controller::Action::RIGHT_HAND_MIDDLE2,
controller::Action::RIGHT_HAND_MIDDLE3,
controller::Action::RIGHT_HAND_MIDDLE4,
controller::Action::RIGHT_HAND_RING1,
controller::Action::RIGHT_HAND_RING2,
controller::Action::RIGHT_HAND_RING3,
controller::Action::RIGHT_HAND_RING4,
controller::Action::RIGHT_HAND_PINKY1,
controller::Action::RIGHT_HAND_PINKY2,
controller::Action::RIGHT_HAND_PINKY3,
controller::Action::RIGHT_HAND_PINKY4,
controller::Action::LEFT_ARM,
controller::Action::RIGHT_ARM,
controller::Action::LEFT_SHOULDER,
controller::Action::RIGHT_SHOULDER,
controller::Action::LEFT_FORE_ARM,
controller::Action::RIGHT_FORE_ARM,
controller::Action::LEFT_LEG,
controller::Action::RIGHT_LEG,
controller::Action::LEFT_UP_LEG,
controller::Action::RIGHT_UP_LEG,
controller::Action::LEFT_TOE_BASE,
controller::Action::RIGHT_TOE_BASE
};
// If have previously done finger poses or there are new valid finger poses, update finger pose values. This so that if
// fingers are not being controlled, finger joints are not updated in MySkeletonModel.
// Assumption: Finger poses are either all present and valid or not present at all; thus can test just one joint.
MyAvatar::FingerPosesMap leftHandFingerPoses;
if (myAvatar->getLeftHandFingerControllerPosesInSensorFrame().size() > 0
|| userInputMapper->getPoseState(controller::Action::LEFT_HAND_THUMB1).isValid()) {
for (int i = (int)controller::Action::LEFT_HAND_THUMB1; i <= (int)controller::Action::LEFT_HAND_PINKY4; i++) {
leftHandFingerPoses[i] = {
userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix),
userInputMapper->getActionName((controller::Action)i)
};
}
// copy controller poses from userInputMapper to myAvatar.
glm::mat4 myAvatarMatrix = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
glm::mat4 worldToSensorMatrix = glm::inverse(myAvatar->getSensorToWorldMatrix());
glm::mat4 avatarToSensorMatrix = worldToSensorMatrix * myAvatarMatrix;
for (auto& action : avatarControllerActions) {
controller::Pose pose = userInputMapper->getPoseState(action);
myAvatar->setControllerPoseInSensorFrame(action, pose.transform(avatarToSensorMatrix));
}
MyAvatar::FingerPosesMap rightHandFingerPoses;
if (myAvatar->getRightHandFingerControllerPosesInSensorFrame().size() > 0
|| userInputMapper->getPoseState(controller::Action::RIGHT_HAND_THUMB1).isValid()) {
for (int i = (int)controller::Action::RIGHT_HAND_THUMB1; i <= (int)controller::Action::RIGHT_HAND_PINKY4; i++) {
rightHandFingerPoses[i] = {
userInputMapper->getPoseState((controller::Action)i).transform(avatarToSensorMatrix),
userInputMapper->getActionName((controller::Action)i)
};
}
}
myAvatar->setFingerControllerPosesInSensorFrame(leftHandFingerPoses, rightHandFingerPoses);
controller::Pose leftFootPose = userInputMapper->getPoseState(controller::Action::LEFT_FOOT);
controller::Pose rightFootPose = userInputMapper->getPoseState(controller::Action::RIGHT_FOOT);
myAvatar->setFootControllerPosesInSensorFrame(leftFootPose.transform(avatarToSensorMatrix), rightFootPose.transform(avatarToSensorMatrix));
controller::Pose hipsPose = userInputMapper->getPoseState(controller::Action::HIPS);
controller::Pose spine2Pose = userInputMapper->getPoseState(controller::Action::SPINE2);
myAvatar->setSpineControllerPosesInSensorFrame(hipsPose.transform(avatarToSensorMatrix), spine2Pose.transform(avatarToSensorMatrix));
controller::Pose headPose = userInputMapper->getPoseState(controller::Action::HEAD);
myAvatar->setHeadControllerPoseInSensorFrame(headPose.transform(avatarToSensorMatrix));
controller::Pose leftArmPose = userInputMapper->getPoseState(controller::Action::LEFT_ARM);
controller::Pose rightArmPose = userInputMapper->getPoseState(controller::Action::RIGHT_ARM);
myAvatar->setArmControllerPosesInSensorFrame(leftArmPose.transform(avatarToSensorMatrix), rightArmPose.transform(avatarToSensorMatrix));
updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
updateDialogs(deltaTime); // update various stats dialogs if present
@ -4968,6 +5135,16 @@ void Application::update(float deltaTime) {
_overlays.update(deltaTime);
}
{
PROFILE_RANGE(app, "RayPickManager");
_rayPickManager.update();
}
{
PROFILE_RANGE(app, "LaserPointerManager");
_laserPointerManager.update();
}
// Update _viewFrustum with latest camera and view frustum data...
// NOTE: we get this from the view frustum, to make it simpler, since the
// loadViewFrumstum() method will get the correct details from the camera
@ -5405,6 +5582,17 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
}
renderArgs->_debugFlags = renderDebugFlags;
//ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, transaction);
RenderArgs::OutlineFlags renderOutlineFlags = RenderArgs::RENDER_OUTLINE_NONE;
auto contextOverlayInterface = DependencyManager::get<ContextOverlayInterface>();
if (contextOverlayInterface->getEnabled()) {
if (DependencyManager::get<ContextOverlayInterface>()->getIsInMarketplaceInspectionMode()) {
renderOutlineFlags = RenderArgs::RENDER_OUTLINE_MARKETPLACE_MODE;
} else {
renderOutlineFlags = RenderArgs::RENDER_OUTLINE_WIREFRAMES;
}
}
renderArgs->_outlineFlags = renderOutlineFlags;
}
}
@ -5818,6 +6006,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
scriptEngine->registerGlobalObject("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
scriptEngine->registerGlobalObject("RayPick", DependencyManager::get<RayPickScriptingInterface>().data());
scriptEngine->registerGlobalObject("LaserPointers", DependencyManager::get<LaserPointerScriptingInterface>().data());
// Caches
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCache>().data());
@ -5868,7 +6059,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
auto entityScriptServerLog = DependencyManager::get<EntityScriptServerLogClient>();
scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data());
scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance());
scriptEngine->registerGlobalObject("HoverOverlay", DependencyManager::get<HoverOverlayInterface>().data());
scriptEngine->registerGlobalObject("ContextOverlay", DependencyManager::get<ContextOverlayInterface>().data());
qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue);
@ -6075,6 +6266,55 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
return true;
}
bool Application::askToReplaceDomainContent(const QString& url) {
QString methodDetails;
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
QUrl originURL { url };
if (originURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) {
// Create a confirmation dialog when this call is made
const int MAX_CHARACTERS_PER_LINE = 90;
static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. "
"If you want to save what you have now, create a backup before proceeding. For more information about backing up "
"and restoring content, visit the documentation page at: ", MAX_CHARACTERS_PER_LINE) +
"\nhttps://docs.highfidelity.com/create-and-explore/start-working-in-your-sandbox/restoring-sandbox-content";
bool agreeToReplaceContent = false; // assume false
agreeToReplaceContent = QMessageBox::Yes == OffscreenUi::question("Are you sure you want to replace this domain's content set?",
infoText, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (agreeToReplaceContent) {
// Given confirmation, send request to domain server to replace content
qCDebug(interfaceapp) << "Attempting to replace domain content: " << url;
QByteArray urlData(url.toUtf8());
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
limitedNodeList->eachMatchingNode([](const SharedNodePointer& node) {
return node->getType() == NodeType::EntityServer && node->getActiveSocket();
}, [&urlData, limitedNodeList](const SharedNodePointer& octreeNode) {
auto octreeFilePacket = NLPacket::create(PacketType::OctreeFileReplacementFromUrl, urlData.size(), true);
octreeFilePacket->write(urlData);
limitedNodeList->sendPacket(std::move(octreeFilePacket), *octreeNode);
});
DependencyManager::get<AddressManager>()->handleLookupString(DOMAIN_SPAWNING_POINT);
methodDetails = "SuccessfulRequestToReplaceContent";
} else {
methodDetails = "UserDeclinedToReplaceContent";
}
} else {
methodDetails = "ContentSetDidNotOriginateFromMarketplace";
}
} else {
methodDetails = "UserDoesNotHavePermissionToReplaceContent";
OffscreenUi::warning("Unable to replace content", "You do not have permissions to replace domain content",
QMessageBox::Ok, QMessageBox::Ok);
}
QJsonObject messageProperties = {
{ "status", methodDetails },
{ "content_set_url", url }
};
UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties);
return true;
}
void Application::displayAvatarAttachmentWarning(const QString& message) const {
auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure");
OffscreenUi::warning(avatarAttachmentWarningTitle, message);
@ -6151,7 +6391,14 @@ void Application::showAssetServerWidget(QString filePath) {
void Application::addAssetToWorldFromURL(QString url) {
qInfo(interfaceapp) << "Download model and add to world from" << url;
QString filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
QString filename;
if (url.contains("filename")) {
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
}
if (url.contains("vr.google.com/downloads")) {
filename = url.section('/', -1);
filename.remove(".zip?noDownload=false");
}
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
QString errorInfo = "You do not have permissions to write to the Asset Server.";
@ -6172,7 +6419,17 @@ void Application::addAssetToWorldFromURLRequestFinished() {
auto url = request->getUrl().toString();
auto result = request->getResult();
QString filename = url.section("filename=", 1, 1); // Filename from trailing "?filename=" URL parameter.
QString filename;
bool isBlocks = false;
if (url.contains("filename")) {
filename = url.section("filename=", 1, 1); // Filename is in "?filename=" parameter at end of URL.
}
if (url.contains("vr.google.com/downloads")) {
filename = url.section('/', -1);
filename.remove(".zip?noDownload=false");
isBlocks = true;
}
if (result == ResourceRequest::Success) {
qInfo(interfaceapp) << "Downloaded model from" << url;
@ -6187,7 +6444,7 @@ void Application::addAssetToWorldFromURLRequestFinished() {
if (tempFile.open(QIODevice::WriteOnly)) {
tempFile.write(request->getData());
addAssetToWorldInfoClear(filename); // Remove message from list; next one added will have a different key.
qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true);
qApp->getFileDownloadInterface()->runUnzip(downloadPath, url, true, false, isBlocks);
} else {
QString errorInfo = "Couldn't open temporary file for download";
qWarning(interfaceapp) << errorInfo;
@ -6217,12 +6474,23 @@ void Application::addAssetToWorldUnzipFailure(QString filePath) {
addAssetToWorldError(filename, "Couldn't unzip file " + filename + ".");
}
void Application::addAssetToWorld(QString filePath) {
void Application::addAssetToWorld(QString filePath, QString zipFile, bool isZip, bool isBlocks) {
// Automatically upload and add asset to world as an alternative manual process initiated by showAssetServerWidget().
QString path = QUrl(filePath).toLocalFile();
QString mapping;
QString path = filePath;
QString filename = filenameFromPath(path);
QString mapping = "/" + filename;
if (isZip) {
QString assetFolder = zipFile.section("/", -1);
assetFolder.remove(".zip");
mapping = "/" + assetFolder + "/" + filename;
} else if (isBlocks) {
qCDebug(interfaceapp) << "Path to asset folder: " << zipFile;
QString assetFolder = zipFile.section('/', -1);
assetFolder.remove(".zip?noDownload=false");
mapping = "/" + assetFolder + "/" + filename;
} else {
mapping = "/" + filename;
}
// Test repeated because possibly different code paths.
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
@ -6303,7 +6571,13 @@ void Application::addAssetToWorldSetMapping(QString filePath, QString mapping, Q
qWarning(interfaceapp) << "Error downloading model: " + errorInfo;
addAssetToWorldError(filenameFromPath(filePath), errorInfo);
} else {
addAssetToWorldAddEntity(filePath, mapping);
// to prevent files that aren't models from being loaded into world automatically
if (filePath.endsWith(".obj") || filePath.endsWith(".fbx")) {
addAssetToWorldAddEntity(filePath, mapping);
} else {
qCDebug(interfaceapp) << "Zipped contents are not supported entity files";
addAssetToWorldInfoDone(filenameFromPath(filePath));
}
}
request->deleteLater();
});
@ -6593,15 +6867,18 @@ void Application::onAssetToWorldMessageBoxClosed() {
}
void Application::handleUnzip(QString zipFile, QString unzipFile, bool autoAdd) {
void Application::handleUnzip(QString zipFile, QStringList unzipFile, bool autoAdd, bool isZip, bool isBlocks) {
if (autoAdd) {
if (!unzipFile.isEmpty()) {
addAssetToWorld(unzipFile);
for (int i = 0; i < unzipFile.length(); i++) {
qCDebug(interfaceapp) << "Preparing file for asset server: " << unzipFile.at(i);
addAssetToWorld(unzipFile.at(i), zipFile, isZip, isBlocks);
}
} else {
addAssetToWorldUnzipFailure(zipFile);
}
} else {
showAssetServerWidget(unzipFile);
showAssetServerWidget(unzipFile.first());
}
}
@ -6726,6 +7003,12 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
});
}
void Application::takeSecondaryCameraSnapshot() {
postLambdaEvent([this] {
Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot());
});
}
void Application::shareSnapshot(const QString& path, const QUrl& href) {
postLambdaEvent([path, href] {
// not much to do here, everything is done in snapshot code...
@ -7064,6 +7347,7 @@ void Application::updateDisplayMode() {
auto oldDisplayPlugin = _displayPlugin;
if (_displayPlugin) {
disconnect(_displayPluginPresentConnection);
_displayPlugin->deactivate();
}
@ -7104,6 +7388,7 @@ void Application::updateDisplayMode() {
_offscreenContext->makeCurrent();
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
_displayPlugin = newDisplayPlugin;
_displayPluginPresentConnection = connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent);
offscreenUi->getDesktop()->setProperty("repositionLocked", wasRepositionLocked);
}

View file

@ -71,6 +71,9 @@
#include "ui/overlays/Overlays.h"
#include "UndoStackScriptingInterface.h"
#include "raypick/RayPickManager.h"
#include "raypick/LaserPointerManager.h"
#include <procedural/ProceduralSkybox.h>
#include <model/Skybox.h>
#include <ModelScriptingInterface.h>
@ -129,8 +132,7 @@ public:
virtual DisplayPluginPointer getActiveDisplayPlugin() const override;
enum Event {
Present = DisplayPlugin::Present,
Paint,
Paint = QEvent::User + 1,
Idle,
Lambda
};
@ -275,6 +277,7 @@ public:
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f);
void takeSecondaryCameraSnapshot();
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
@ -298,9 +301,13 @@ public:
QUuid getTabletFrameID() const; // may be an entity or an overlay
void setAvatarOverrideUrl(const QUrl& url, bool save);
void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; }
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
LaserPointerManager& getLaserPointerManager() { return _laserPointerManager; }
RayPickManager& getRayPickManager() { return _rayPickManager; }
signals:
void svoImportRequested(const QString& url);
@ -332,14 +339,14 @@ public slots:
// FIXME: Move addAssetToWorld* methods to own class?
void addAssetToWorldFromURL(QString url);
void addAssetToWorldFromURLRequestFinished();
void addAssetToWorld(QString filePath);
void addAssetToWorld(QString filePath, QString zipFile, bool isZip, bool isBlocks);
void addAssetToWorldUnzipFailure(QString filePath);
void addAssetToWorldWithNewMapping(QString filePath, QString mapping, int copy);
void addAssetToWorldUpload(QString filePath, QString mapping);
void addAssetToWorldSetMapping(QString filePath, QString mapping, QString hash);
void addAssetToWorldAddEntity(QString filePath, QString mapping);
void handleUnzip(QString sourceFile, QString destinationFile, bool autoAdd);
void handleUnzip(QString sourceFile, QStringList destinationFile, bool autoAdd, bool isZip, bool isBlocks);
FileScriptingInterface* getFileDownloadInterface() { return _fileDownload; }
@ -409,6 +416,7 @@ private slots:
void clearDomainOctreeDetails();
void clearDomainAvatars();
void onAboutToQuit();
void onPresent(quint32 frameCount);
void resettingDomain();
@ -430,6 +438,8 @@ private slots:
void displayAvatarAttachmentWarning(const QString& message) const;
bool displayAvatarAttachmentConfirmationDialog(const QString& name) const;
bool askToReplaceDomainContent(const QString& url);
void setSessionUUID(const QUuid& sessionUUID) const;
void domainChanged(const QString& domainHostname);
@ -455,8 +465,8 @@ private:
void cleanupBeforeQuit();
bool shouldPaint(float nsecsElapsed);
void idle(float nsecsElapsed);
bool shouldPaint();
void idle();
void update(float deltaTime);
// Various helper functions called during update()
@ -481,6 +491,7 @@ private:
bool importJSONFromURL(const QString& urlString);
bool importSVOFromURL(const QString& urlString);
bool importFromZIP(const QString& filePath);
bool nearbyEntitiesAreReadyForPhysics();
int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode);
@ -518,6 +529,7 @@ private:
OffscreenGLCanvas* _offscreenContext { nullptr };
DisplayPluginPointer _displayPlugin;
QMetaObject::Connection _displayPluginPresentConnection;
mutable std::mutex _displayPluginLock;
InputPluginList _activeInputPlugins;
@ -695,5 +707,8 @@ private:
QUrl _avatarOverrideUrl;
bool _saveAvatarOverrideUrl { false };
LaserPointerManager _laserPointerManager;
RayPickManager _rayPickManager;
};
#endif // hifi_Application_h

View file

@ -560,6 +560,8 @@ Menu::Menu() {
avatar.get(), SLOT(setEnableDebugDrawIKConstraints(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderIKChains, 0, false,
avatar.get(), SLOT(setEnableDebugDrawIKChains(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderDetailedCollision, 0, false,
avatar.get(), SLOT(setEnableDebugDrawDetailedCollision(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl,
Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()),

View file

@ -162,6 +162,7 @@ namespace MenuOption {
const QString RenderIKTargets = "Show IK Targets";
const QString RenderIKConstraints = "Show IK Constraints";
const QString RenderIKChains = "Show IK Chains";
const QString RenderDetailedCollision = "Show Detailed Collision";
const QString ResetAvatarSize = "Reset Avatar Size";
const QString ResetSensors = "Reset Sensors";
const QString RunningScripts = "Running Scripts...";

View file

@ -134,9 +134,9 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
// fetch the hand controller pose
controller::Pose pose;
if (isRightHand) {
pose = myAvatar->getRightHandControllerPoseInWorldFrame();
pose = myAvatar->getControllerPoseInWorldFrame(controller::Action::RIGHT_HAND);
} else {
pose = myAvatar->getLeftHandControllerPoseInWorldFrame();
pose = myAvatar->getControllerPoseInWorldFrame(controller::Action::LEFT_HAND);
}
if (pose.isValid()) {

View file

@ -273,19 +273,15 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
return;
}
const float SHRINK_RATE = 0.15f;
const float MIN_FADE_SCALE = MIN_AVATAR_SCALE;
QReadLocker locker(&_hashLock);
QVector<AvatarSharedPointer>::iterator avatarItr = _avatarsToFade.begin();
const render::ScenePointer& scene = qApp->getMain3DScene();
while (avatarItr != _avatarsToFade.end()) {
auto avatar = std::static_pointer_cast<Avatar>(*avatarItr);
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
avatar->animateScaleChanges(deltaTime);
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
avatar->updateFadingStatus(scene);
if (!avatar->isFading()) {
// fading to zero is such a rare event we push a unique transaction for each
if (avatar->isInScene()) {
const render::ScenePointer& scene = qApp->getMain3DScene();
render::Transaction transaction;
avatar->removeFromScene(*avatarItr, scene, transaction);
scene->enqueueTransaction(transaction);
@ -323,6 +319,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
// remove from node sets, if present
DependencyManager::get<NodeList>()->removeFromIgnoreMuteSets(avatar->getSessionUUID());
DependencyManager::get<UsersScriptingInterface>()->avatarDisconnected(avatar->getSessionUUID());
avatar->fadeOut(qApp->getMain3DScene(), removalReason);
}
_avatarsToFade.push_back(removedAvatar);
}
@ -476,19 +473,25 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID)
RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay& ray,
const QScriptValue& avatarIdsToInclude,
const QScriptValue& avatarIdsToDiscard) {
RayToAvatarIntersectionResult result;
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(const_cast<AvatarManager*>(this), "findRayIntersection",
Q_RETURN_ARG(RayToAvatarIntersectionResult, result),
Q_ARG(const PickRay&, ray),
Q_ARG(const QScriptValue&, avatarIdsToInclude),
Q_ARG(const QScriptValue&, avatarIdsToDiscard));
return result;
}
QVector<EntityItemID> avatarsToInclude = qVectorEntityItemIDFromScriptValue(avatarIdsToInclude);
QVector<EntityItemID> avatarsToDiscard = qVectorEntityItemIDFromScriptValue(avatarIdsToDiscard);
return findRayIntersectionVector(ray, avatarsToInclude, avatarsToDiscard);
}
RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const PickRay& ray,
const QVector<EntityItemID>& avatarsToInclude,
const QVector<EntityItemID>& avatarsToDiscard) {
RayToAvatarIntersectionResult result;
if (QThread::currentThread() != thread()) {
BLOCKING_INVOKE_METHOD(const_cast<AvatarManager*>(this), "findRayIntersectionVector",
Q_RETURN_ARG(RayToAvatarIntersectionResult, result),
Q_ARG(const PickRay&, ray),
Q_ARG(const QVector<EntityItemID>&, avatarsToInclude),
Q_ARG(const QVector<EntityItemID>&, avatarsToDiscard));
return result;
}
glm::vec3 normDirection = glm::normalize(ray.direction);
for (auto avatarData : _avatarHash) {

View file

@ -73,6 +73,9 @@ public:
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
const QScriptValue& avatarIdsToInclude = QScriptValue(),
const QScriptValue& avatarIdsToDiscard = QScriptValue());
RayToAvatarIntersectionResult findRayIntersectionVector(const PickRay& ray,
const QVector<EntityItemID>& avatarsToInclude,
const QVector<EntityItemID>& avatarsToDiscard);
// TODO: remove this HACK once we settle on optimal default sort coefficients
Q_INVOKABLE float getAvatarSortCoefficient(const QString& name);

View file

@ -429,7 +429,7 @@ void MyAvatar::update(float deltaTime) {
}
#ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE
glm::vec3 p = transformPoint(getSensorToWorldMatrix(), getHeadControllerPoseInAvatarFrame() *
glm::vec3 p = transformPoint(getSensorToWorldMatrix(), getControllerPoseInAvatarFrame(controller::Pose::HEAD) *
glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y));
DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f));
p = transformPoint(getSensorToWorldMatrix(), getHMDSensorPosition() +
@ -664,7 +664,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
_hmdSensorPosition = newHmdSensorPosition;
_hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix);
auto headPose = _headControllerPoseInSensorFrameCache.get();
auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
if (headPose.isValid()) {
_headControllerFacing = getFacingDir2D(headPose.rotation);
} else {
@ -760,37 +760,37 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
}
glm::vec3 MyAvatar::getLeftHandPosition() const {
auto pose = getLeftHandControllerPoseInAvatarFrame();
auto pose = getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getRightHandPosition() const {
auto pose = getRightHandControllerPoseInAvatarFrame();
auto pose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
return pose.isValid() ? pose.getTranslation() : glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getLeftHandTipPosition() const {
const float TIP_LENGTH = 0.3f;
auto pose = getLeftHandControllerPoseInAvatarFrame();
auto pose = getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f);
}
glm::vec3 MyAvatar::getRightHandTipPosition() const {
const float TIP_LENGTH = 0.3f;
auto pose = getRightHandControllerPoseInAvatarFrame();
auto pose = getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
return pose.isValid() ? pose.getTranslation() * pose.getRotation() + glm::vec3(0.0f, TIP_LENGTH, 0.0f) : glm::vec3(0.0f);
}
controller::Pose MyAvatar::getLeftHandPose() const {
return getLeftHandControllerPoseInAvatarFrame();
return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND);
}
controller::Pose MyAvatar::getRightHandPose() const {
return getRightHandControllerPoseInAvatarFrame();
return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND);
}
controller::Pose MyAvatar::getLeftHandTipPose() const {
auto pose = getLeftHandControllerPoseInAvatarFrame();
auto pose = getLeftHandPose();
glm::vec3 tipTrans = getLeftHandTipPosition();
pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans);
pose.translation = tipTrans;
@ -798,7 +798,7 @@ controller::Pose MyAvatar::getLeftHandTipPose() const {
}
controller::Pose MyAvatar::getRightHandTipPose() const {
auto pose = getRightHandControllerPoseInAvatarFrame();
auto pose = getRightHandPose();
glm::vec3 tipTrans = getRightHandTipPosition();
pose.velocity += glm::cross(pose.getAngularVelocity(), pose.getTranslation() - tipTrans);
pose.translation = tipTrans;
@ -1063,6 +1063,10 @@ void MyAvatar::setEnableDebugDrawIKConstraints(bool isEnabled) {
_enableDebugDrawIKConstraints = isEnabled;
}
void MyAvatar::setEnableDebugDrawDetailedCollision(bool isEnabled) {
_enableDebugDrawDetailedCollision = isEnabled;
}
void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) {
_enableDebugDrawIKChains = isEnabled;
}
@ -1318,7 +1322,7 @@ eyeContactTarget MyAvatar::getEyeContactTarget() {
}
glm::vec3 MyAvatar::getDefaultEyePosition() const {
return getPosition() + getWorldAlignedOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition();
return getPosition() + getOrientation() * Quaternions::Y_180 * _skeletonModel->getDefaultEyeModelPosition();
}
const float SCRIPT_PRIORITY = 1.0f + 1.0f;
@ -1449,159 +1453,43 @@ void MyAvatar::rebuildCollisionShape() {
_characterController.setLocalBoundingBox(corner, diagonal);
}
void MyAvatar::setHandControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
_leftHandControllerPoseInSensorFrameCache.set(left);
_rightHandControllerPoseInSensorFrameCache.set(right);
void MyAvatar::setControllerPoseInSensorFrame(controller::Action action, const controller::Pose& pose) {
std::lock_guard<std::mutex> guard(_controllerPoseMapMutex);
auto iter = _controllerPoseMap.find(action);
if (iter != _controllerPoseMap.end()) {
iter->second = pose;
} else {
_controllerPoseMap.insert({ action, pose });
}
}
controller::Pose MyAvatar::getLeftHandControllerPoseInSensorFrame() const {
return _leftHandControllerPoseInSensorFrameCache.get();
controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action action) const {
std::lock_guard<std::mutex> guard(_controllerPoseMapMutex);
auto iter = _controllerPoseMap.find(action);
if (iter != _controllerPoseMap.end()) {
return iter->second;
} else {
return controller::Pose(); // invalid pose
}
}
controller::Pose MyAvatar::getRightHandControllerPoseInSensorFrame() const {
return _rightHandControllerPoseInSensorFrameCache.get();
controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const {
auto pose = getControllerPoseInSensorFrame(action);
if (pose.valid) {
return pose.transform(getSensorToWorldMatrix());
} else {
return controller::Pose(); // invalid pose
}
}
controller::Pose MyAvatar::getLeftHandControllerPoseInWorldFrame() const {
return _leftHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getRightHandControllerPoseInWorldFrame() const {
return _rightHandControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getLeftHandControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getLeftHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getRightHandControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
void MyAvatar::setFingerControllerPosesInSensorFrame(const FingerPosesMap& left, const FingerPosesMap& right) {
_leftHandFingerPosesInSensorFramceCache.set(left);
_rightHandFingerPosesInSensorFramceCache.set(right);
}
MyAvatar::FingerPosesMap MyAvatar::getLeftHandFingerControllerPosesInSensorFrame() const {
return _leftHandFingerPosesInSensorFramceCache.get();
}
MyAvatar::FingerPosesMap MyAvatar::getRightHandFingerControllerPosesInSensorFrame() const {
return _rightHandFingerPosesInSensorFramceCache.get();
}
void MyAvatar::setFootControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
_leftFootControllerPoseInSensorFrameCache.set(left);
_rightFootControllerPoseInSensorFrameCache.set(right);
}
controller::Pose MyAvatar::getLeftFootControllerPoseInSensorFrame() const {
return _leftFootControllerPoseInSensorFrameCache.get();
}
controller::Pose MyAvatar::getRightFootControllerPoseInSensorFrame() const {
return _rightFootControllerPoseInSensorFrameCache.get();
}
controller::Pose MyAvatar::getLeftFootControllerPoseInWorldFrame() const {
return _leftFootControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getRightFootControllerPoseInWorldFrame() const {
return _rightFootControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getLeftFootControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getLeftFootControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
controller::Pose MyAvatar::getRightFootControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getRightFootControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
void MyAvatar::setSpineControllerPosesInSensorFrame(const controller::Pose& hips, const controller::Pose& spine2) {
_hipsControllerPoseInSensorFrameCache.set(hips);
_spine2ControllerPoseInSensorFrameCache.set(spine2);
}
controller::Pose MyAvatar::getHipsControllerPoseInSensorFrame() const {
return _hipsControllerPoseInSensorFrameCache.get();
}
controller::Pose MyAvatar::getSpine2ControllerPoseInSensorFrame() const {
return _spine2ControllerPoseInSensorFrameCache.get();
}
controller::Pose MyAvatar::getHipsControllerPoseInWorldFrame() const {
return _hipsControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getSpine2ControllerPoseInWorldFrame() const {
return _spine2ControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getHipsControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getHipsControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
controller::Pose MyAvatar::getSpine2ControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getSpine2ControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
void MyAvatar::setHeadControllerPoseInSensorFrame(const controller::Pose& head) {
_headControllerPoseInSensorFrameCache.set(head);
}
controller::Pose MyAvatar::getHeadControllerPoseInSensorFrame() const {
return _headControllerPoseInSensorFrameCache.get();
}
controller::Pose MyAvatar::getHeadControllerPoseInWorldFrame() const {
return _headControllerPoseInSensorFrameCache.get().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getHeadControllerPoseInAvatarFrame() const {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getHeadControllerPoseInWorldFrame().transform(invAvatarMatrix);
}
void MyAvatar::setArmControllerPosesInSensorFrame(const controller::Pose& left, const controller::Pose& right) {
_leftArmControllerPoseInSensorFrameCache.set(left);
_rightArmControllerPoseInSensorFrameCache.set(right);
}
controller::Pose MyAvatar::getLeftArmControllerPoseInSensorFrame() const {
return _leftArmControllerPoseInSensorFrameCache.get();
}
controller::Pose MyAvatar::getRightArmControllerPoseInSensorFrame() const {
return _rightArmControllerPoseInSensorFrameCache.get();
}
controller::Pose MyAvatar::getLeftArmControllerPoseInWorldFrame() const {
return getLeftArmControllerPoseInSensorFrame().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getRightArmControllerPoseInWorldFrame() const {
return getRightArmControllerPoseInSensorFrame().transform(getSensorToWorldMatrix());
}
controller::Pose MyAvatar::getLeftArmControllerPoseInAvatarFrame() const {
glm::mat4 worldToAvatarMat = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getLeftArmControllerPoseInWorldFrame().transform(worldToAvatarMat);
}
controller::Pose MyAvatar::getRightArmControllerPoseInAvatarFrame() const {
glm::mat4 worldToAvatarMat = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return getRightArmControllerPoseInWorldFrame().transform(worldToAvatarMat);
controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const {
auto pose = getControllerPoseInWorldFrame(action);
if (pose.valid) {
glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getOrientation(), getPosition()));
return pose.transform(invAvatarMatrix);
} else {
return controller::Pose(); // invalid pose
}
}
void MyAvatar::updateMotors() {
@ -1613,9 +1501,14 @@ void MyAvatar::updateMotors() {
motorRotation = getMyHead()->getHeadOrientation();
} else {
// non-hovering = walking: follow camera twist about vertical but not lift
// so we decompose camera's rotation and store the twist part in motorRotation
// we decompose camera's rotation and store the twist part in motorRotation
// however, we need to perform the decomposition in the avatar-frame
// using the local UP axis and then transform back into world-frame
glm::quat orientation = getOrientation();
glm::quat headOrientation = glm::inverse(orientation) * getMyHead()->getHeadOrientation(); // avatar-frame
glm::quat liftRotation;
swingTwistDecomposition(getMyHead()->getHeadOrientation(), _worldUpDirection, liftRotation, motorRotation);
swingTwistDecomposition(headOrientation, Vectors::UNIT_Y, liftRotation, motorRotation);
motorRotation = orientation * motorRotation;
}
const float DEFAULT_MOTOR_TIMESCALE = 0.2f;
const float INVALID_MOTOR_TIMESCALE = 1.0e6f;
@ -1659,7 +1552,7 @@ void MyAvatar::prepareForPhysicsSimulation() {
_characterController.setParentVelocity(parentVelocity);
_characterController.setPositionAndOrientation(getPosition(), getOrientation());
auto headPose = getHeadControllerPoseInAvatarFrame();
auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD);
if (headPose.isValid()) {
_follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput());
} else {
@ -1669,11 +1562,31 @@ void MyAvatar::prepareForPhysicsSimulation() {
_prePhysicsRoomPose = AnimPose(_sensorToWorldMatrix);
}
// There are a number of possible strategies for this set of tools through endRender, below.
void MyAvatar::nextAttitude(glm::vec3 position, glm::quat orientation) {
bool success;
Transform trans = getTransform(success);
if (!success) {
qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed";
return;
}
trans.setTranslation(position);
trans.setRotation(orientation);
SpatiallyNestable::setTransform(trans, success);
if (!success) {
qCWarning(interfaceapp) << "Warning -- MyAvatar::nextAttitude failed";
}
updateAttitude(orientation);
}
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) {
glm::vec3 position = getPosition();
glm::quat orientation = getOrientation();
glm::vec3 position;
glm::quat orientation;
if (_characterController.isEnabledAndReady()) {
_characterController.getPositionAndOrientation(position, orientation);
} else {
position = getPosition();
orientation = getOrientation();
}
nextAttitude(position, orientation);
_bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix);
@ -1893,8 +1806,8 @@ void MyAvatar::postUpdate(float deltaTime) {
}
if (_enableDebugDrawHandControllers) {
auto leftHandPose = getLeftHandControllerPoseInWorldFrame();
auto rightHandPose = getRightHandControllerPoseInWorldFrame();
auto leftHandPose = getControllerPoseInWorldFrame(controller::Action::LEFT_HAND);
auto rightHandPose = getControllerPoseInWorldFrame(controller::Action::RIGHT_HAND);
if (leftHandPose.isValid()) {
DebugDraw::getInstance().addMarker("leftHandController", leftHandPose.getRotation(), leftHandPose.getTranslation(), glm::vec4(1));
@ -1915,6 +1828,37 @@ void MyAvatar::postUpdate(float deltaTime) {
AnimPose postUpdateRoomPose(_sensorToWorldMatrix);
updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose);
if (_enableDebugDrawDetailedCollision) {
AnimPose rigToWorldPose(glm::vec3(1.0f), getRotation() * Quaternions::Y_180, getPosition());
const int NUM_DEBUG_COLORS = 8;
const glm::vec4 DEBUG_COLORS[NUM_DEBUG_COLORS] = {
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f),
glm::vec4(1.0f, 0.0f, 0.0f, 1.0f),
glm::vec4(0.0f, 1.0f, 0.0f, 1.0f),
glm::vec4(0.25f, 0.25f, 1.0f, 1.0f),
glm::vec4(1.0f, 1.0f, 0.0f, 1.0f),
glm::vec4(0.25f, 1.0f, 1.0f, 1.0f),
glm::vec4(1.0f, 0.25f, 1.0f, 1.0f),
glm::vec4(1.0f, 0.65f, 0.0f, 1.0f) // Orange you glad I added this color?
};
if (_skeletonModel && _skeletonModel->isLoaded()) {
const Rig& rig = _skeletonModel->getRig();
const FBXGeometry& geometry = _skeletonModel->getFBXGeometry();
for (int i = 0; i < rig.getJointStateCount(); i++) {
AnimPose jointPose;
rig.getAbsoluteJointPoseInRigFrame(i, jointPose);
const FBXJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo;
const AnimPose pose = rigToWorldPose * jointPose;
for (size_t j = 0; j < shapeInfo.debugLines.size() / 2; j++) {
glm::vec3 pointA = pose.xformPoint(shapeInfo.debugLines[2 * j]);
glm::vec3 pointB = pose.xformPoint(shapeInfo.debugLines[2 * j + 1]);
DebugDraw::getInstance().drawRay(pointA, pointB, DEBUG_COLORS[i % NUM_DEBUG_COLORS]);
}
}
}
}
}
void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
@ -2008,8 +1952,8 @@ void MyAvatar::updateOrientation(float deltaTime) {
totalBodyYaw += (speedFactor * deltaAngle * (180.0f / PI));
}
// Use head/HMD roll to turn while walking or flying.
if (qApp->isHMDMode() && _hmdRollControlEnabled) {
// Use head/HMD roll to turn while walking or flying, but not when standing still
if (qApp->isHMDMode() && _hmdRollControlEnabled && hasDriveInput()) {
// Turn with head roll.
const float MIN_CONTROL_SPEED = 0.01f;
float speed = glm::length(getVelocity());
@ -2047,7 +1991,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
auto headPose = getHeadControllerPoseInAvatarFrame();
auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD);
if (headPose.isValid()) {
glm::quat localOrientation = headPose.rotation * Quaternions::Y_180;
// these angles will be in radians
@ -2684,10 +2628,10 @@ bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const {
glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
glm::vec3 headPosition;
glm::quat headOrientation;
auto headPose = getHeadControllerPoseInSensorFrame();
auto headPose = getControllerPoseInSensorFrame(controller::Action::HEAD);
if (headPose.isValid()) {
headPosition = getHeadControllerPoseInSensorFrame().translation;
headOrientation = getHeadControllerPoseInSensorFrame().rotation * Quaternions::Y_180;
headPosition = headPose.translation;
headOrientation = headPose.rotation * Quaternions::Y_180;
}
const glm::quat headOrientationYawOnly = cancelOutRollAndPitch(headOrientation);
@ -2869,7 +2813,8 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co
void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix,
const glm::mat4& currentBodyMatrix, bool hasDriveInput) {
if (myAvatar.getHMDLeanRecenterEnabled()) {
if (myAvatar.getHMDLeanRecenterEnabled() &&
qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) {
if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) {
activate(Rotation);
}
@ -2996,19 +2941,19 @@ glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const {
switch (index) {
case CONTROLLER_LEFTHAND_INDEX: {
return getLeftHandControllerPoseInAvatarFrame().getRotation();
return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getRotation();
}
case CONTROLLER_RIGHTHAND_INDEX: {
return getRightHandControllerPoseInAvatarFrame().getRotation();
return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getRotation();
}
case CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX: {
auto pose = _leftHandControllerPoseInSensorFrameCache.get();
auto pose = getControllerPoseInSensorFrame(controller::Action::LEFT_HAND);
glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation);
glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix);
return glmExtractRotation(result);
}
case CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX: {
auto pose = _rightHandControllerPoseInSensorFrameCache.get();
auto pose = getControllerPoseInSensorFrame(controller::Action::RIGHT_HAND);
glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation);
glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix);
return glmExtractRotation(result);
@ -3033,19 +2978,19 @@ glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
switch (index) {
case CONTROLLER_LEFTHAND_INDEX: {
return getLeftHandControllerPoseInAvatarFrame().getTranslation();
return getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation();
}
case CONTROLLER_RIGHTHAND_INDEX: {
return getRightHandControllerPoseInAvatarFrame().getTranslation();
return getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation();
}
case CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX: {
auto pose = _leftHandControllerPoseInSensorFrameCache.get();
auto pose = getControllerPoseInSensorFrame(controller::Action::LEFT_HAND);
glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation);
glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix);
return extractTranslation(result);
}
case CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX: {
auto pose = _rightHandControllerPoseInSensorFrameCache.get();
auto pose = getControllerPoseInSensorFrame(controller::Action::RIGHT_HAND);
glm::mat4 controllerSensorMatrix = createMatFromQuatAndPos(pose.rotation, pose.translation);
glm::mat4 result = computeCameraRelativeHandControllerMatrix(controllerSensorMatrix);
return extractTranslation(result);

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