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

This commit is contained in:
Seth Alves 2015-05-03 10:45:55 -07:00
commit b2583298a9
328 changed files with 13948 additions and 4240 deletions

9
.gitignore vendored
View file

@ -4,6 +4,7 @@ CMakeFiles/
CMakeScripts/
cmake_install.cmake
build*/
ext/
Makefile
*.user
@ -32,10 +33,6 @@ DerivedData
interface/external/*/*
!interface/external/*/readme.txt
# ignore interface optional resources
interface/resources/visage/*
!interface/resources/visage/tracker.cfg
# Ignore interfaceCache for Linux users
interface/interfaceCache/
@ -46,4 +43,6 @@ libraries/audio-client/external/*/*
gvr-interface/assets/oculussig*
gvr-interface/libs/*
TAGS
# ignore files for various dev environments
TAGS
*.swp

View file

@ -1,9 +1,9 @@
###Dependencies
* [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 2.8.12.2
* [Qt](http://qt-project.org/downloads) ~> 5.3.2
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1g
* IMPORTANT: OpenSSL 1.0.1g is critical to avoid a security vulnerability.
* [Qt](http://www.qt.io/download-open-source) ~> 5.4.1
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m
* IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities.
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
####CMake External Project Dependencies
@ -19,9 +19,9 @@ The following external projects are optional dependencies. You can indicate to C
* [SDL2](https://www.libsdl.org/download-2.0.php) ~> 2.0.3
* Enables game controller support in Interface
The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build-ext` directory in each of the subfolders for each external project.
The above dependencies will be downloaded, built, linked and included automatically by CMake where we require them. The CMakeLists files that handle grabbing each of the following external dependencies can be found in the [cmake/externals folder](cmake/externals). The resulting downloads, source files and binaries will be placed in the `build/ext` folder in each of the subfolders for each external project.
These are not placed in your normal build tree when doing an out of source build so that they do not need to be re-downloaded and re-compiled every time the CMake build folder is cleared. Should you want to force a re-download and re-compile of a specific external, you can simply remove that directory from the appropriate subfolder in `build-ext`. Should you want to force a re-download and re-compile of all externals, just remove the `build-ext` folder.
These are not placed in your normal build tree when doing an out of source build so that they do not need to be re-downloaded and re-compiled every time the CMake build folder is cleared. Should you want to force a re-download and re-compile of a specific external, you can simply remove that directory from the appropriate subfolder in `build/ext`. Should you want to force a re-download and re-compile of all externals, just remove the `build/ext` folder.
If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DGET_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.
@ -37,12 +37,12 @@ Hifi uses CMake to generate build files and project files for your platform.
####Qt
In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your Qt installation.
For example, a Qt5 5.3.2 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
For example, a Qt5 5.4.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
The path it needs to be set to will depend on where and how Qt5 was installed. e.g.
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.3.2/clang_64/lib/cmake/
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.3.2/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/clang_64/lib/cmake/
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.1/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
####Generating build files
@ -57,7 +57,7 @@ Any variables that need to be set for CMake to find dependencies can be set as E
For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation:
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.3.2/lib/cmake
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.4.1/lib/cmake
####Finding Dependencies

View file

@ -10,7 +10,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies
We have a [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas) that you can use/tap to install some of the dependencies. In the code block above qt5 is installed from a formula in this repository.
*Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.3.x stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.*
*Our [qt5 homebrew formula](https://raw.github.com/highfidelity/homebrew-formulas/master/qt5.rb) is for a patched version of Qt 5.4.x stable that removes wireless network scanning that can reduce real-time audio performance. We recommended you use this formula to install Qt.*
###Xcode
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.

View file

@ -29,12 +29,12 @@ NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit
* [Download the online installer](http://qt-project.org/downloads)
* When it asks you to select components, ONLY select the following:
* Qt > Qt 5.3.2 > **msvc2013 32-bit OpenGL**
* Qt > Qt 5.4.1 > **msvc2013 32-bit OpenGL**
* [Download the offline installer](http://download.qt-project.org/official_releases/qt/5.3/5.3.2/qt-opensource-windows-x86-msvc2013_opengl-5.3.2.exe)
* [Download the offline installer](http://download.qt.io/official_releases/qt/5.4/5.4.1/qt-opensource-windows-x86-msvc2013_opengl-5.4.1.exe)
Once Qt is installed, you need to manually configure the following:
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.3.2\msvc2013_opengl\lib\cmake` directory.
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.4.1\msvc2013_opengl\lib\cmake` directory.
* You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New
###External Libraries
@ -71,7 +71,7 @@ Your system may already have several versions of the OpenSSL DLL's (ssleay32.dll
To prevent these problems, install OpenSSL yourself. Download the following binary packages [from this website](http://slproweb.com/products/Win32OpenSSL.html):
* Visual C++ 2008 Redistributables
* Win32 OpenSSL v1.0.1L
* Win32 OpenSSL v1.0.1m
Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version.

View file

@ -45,10 +45,11 @@ if (WIN32)
# Caveats: http://stackoverflow.com/questions/2288728/drawbacks-of-using-largeaddressaware-for-32-bit-windows-executables
# TODO: Remove when building 64-bit.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE")
elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic")
#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fno-strict-aliasing -ggdb")
else ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fno-strict-aliasing -Wno-unused-parameter")
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
endif ()
endif(WIN32)
if (NOT MSVC12)
@ -92,8 +93,17 @@ else ()
if (NOT QT_CMAKE_PREFIX_PATH)
set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
endif ()
if (NOT QT_CMAKE_PREFIX_PATH)
get_filename_component(QT_CMAKE_PREFIX_PATH "${Qt5_DIR}/.." REALPATH)
endif ()
endif ()
if (WIN32)
if (NOT EXISTS ${QT_CMAKE_PREFIX_PATH})
message(FATAL_ERROR "Could not determine QT_CMAKE_PREFIX_PATH.")
endif ()
endif()
# figure out where the qt dir is
get_filename_component(QT_DIR "${QT_CMAKE_PREFIX_PATH}/../../" ABSOLUTE)
@ -168,6 +178,7 @@ option(GET_SOXR "Get Soxr library automatically as external project" 1)
option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1)
option(GET_LIBOVR "Get LibOVR library automatically as external project" 1)
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
option(GET_VHACD "Get V-HACD library automatically as external project" 1)
if (WIN32)
option(GET_GLEW "Get GLEW library automatically as external project" 1)

View file

@ -114,7 +114,23 @@ void AssignmentClient::stopAssignmentClient() {
qDebug() << "Exiting.";
_requestTimer.stop();
_statsTimerACM.stop();
QCoreApplication::quit();
if (_currentAssignment) {
_currentAssignment->aboutToQuit();
QThread* currentAssignmentThread = _currentAssignment->thread();
currentAssignmentThread->quit();
currentAssignmentThread->wait();
}
}
void AssignmentClient::aboutToQuit() {
stopAssignmentClient();
// clear the log handler so that Qt doesn't call the destructor on LogHandler
qInstallMessageHandler(0);
// clear out pointer to the assignment so the destructor gets called. if we don't do this here,
// it will get destroyed along with all the other "static" stuff. various static member variables
// will be destroyed first and things go wrong.
_currentAssignment.clear();
}
@ -197,6 +213,7 @@ void AssignmentClient::readPendingDatagrams() {
// start the deployed assignment
AssignmentThread* workerThread = new AssignmentThread(_currentAssignment, this);
workerThread->setObjectName("worker");
connect(workerThread, &QThread::started, _currentAssignment.data(), &ThreadedAssignment::run);
connect(_currentAssignment.data(), &ThreadedAssignment::finished, workerThread, &QThread::quit);

View file

@ -34,6 +34,9 @@ private slots:
void sendStatsPacketToACM();
void stopAssignmentClient();
public slots:
void aboutToQuit();
private:
void setUpStatsToMonitor(int ppid);
Assignment _requestAssignment;

View file

@ -10,6 +10,7 @@
//
#include <QCommandLineParser>
#include <QThread>
#include <LogHandler.h>
#include <SharedUtil.h>
@ -180,14 +181,19 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
}
}
QThread::currentThread()->setObjectName("main thread");
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
if (numForks || minForks || maxForks) {
AssignmentClientMonitor monitor(numForks, minForks, maxForks, requestAssignmentType, assignmentPool,
walletUUID, assignmentServerHostname, assignmentServerPort);
connect(this, &QCoreApplication::aboutToQuit, &monitor, &AssignmentClientMonitor::aboutToQuit);
exec();
} else {
AssignmentClient client(ppid, requestAssignmentType, assignmentPool,
walletUUID, assignmentServerHostname, assignmentServerPort);
connect(this, &QCoreApplication::aboutToQuit, &client, &AssignmentClient::aboutToQuit);
exec();
}
}

View file

@ -22,6 +22,7 @@
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
const int WAIT_FOR_CHILD_MSECS = 500;
AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmentClientForks,
const unsigned int minAssignmentClientForks,
@ -69,6 +70,17 @@ AssignmentClientMonitor::~AssignmentClientMonitor() {
stopChildProcesses();
}
void AssignmentClientMonitor::waitOnChildren(int msecs) {
QMutableListIterator<QProcess*> i(_childProcesses);
while (i.hasNext()) {
QProcess* childProcess = i.next();
bool finished = childProcess->waitForFinished(msecs);
if (finished) {
i.remove();
}
}
}
void AssignmentClientMonitor::stopChildProcesses() {
auto nodeList = DependencyManager::get<NodeList>();
@ -78,11 +90,41 @@ void AssignmentClientMonitor::stopChildProcesses() {
QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode);
nodeList->writeUnverifiedDatagram(diePacket, *node->getActiveSocket());
});
// try to give all the children time to shutdown
waitOnChildren(WAIT_FOR_CHILD_MSECS);
// ask more firmly
QMutableListIterator<QProcess*> i(_childProcesses);
while (i.hasNext()) {
QProcess* childProcess = i.next();
childProcess->terminate();
}
// try to give all the children time to shutdown
waitOnChildren(WAIT_FOR_CHILD_MSECS);
// ask even more firmly
QMutableListIterator<QProcess*> j(_childProcesses);
while (j.hasNext()) {
QProcess* childProcess = j.next();
childProcess->kill();
}
waitOnChildren(WAIT_FOR_CHILD_MSECS);
}
void AssignmentClientMonitor::aboutToQuit() {
stopChildProcesses();
// clear the log handler so that Qt doesn't call the destructor on LogHandler
qInstallMessageHandler(0);
}
void AssignmentClientMonitor::spawnChildClient() {
QProcess *assignmentClient = new QProcess(this);
_childProcesses.append(assignmentClient);
// unparse the parts of the command-line that the child cares about
QStringList _childArguments;
if (_assignmentPool != "") {
@ -119,8 +161,6 @@ void AssignmentClientMonitor::spawnChildClient() {
qDebug() << "Spawned a child client with PID" << assignmentClient->pid();
}
void AssignmentClientMonitor::checkSpares() {
auto nodeList = DependencyManager::get<NodeList>();
QUuid aSpareId = "";
@ -156,6 +196,8 @@ void AssignmentClientMonitor::checkSpares() {
nodeList->writeUnverifiedDatagram(diePacket, childNode);
}
}
waitOnChildren(0);
}

View file

@ -32,12 +32,16 @@ public:
QString assignmentPool, QUuid walletUUID, QString assignmentServerHostname,
quint16 assignmentServerPort);
~AssignmentClientMonitor();
void waitOnChildren(int msecs);
void stopChildProcesses();
private slots:
void readPendingDatagrams();
void checkSpares();
public slots:
void aboutToQuit();
private:
void spawnChildClient();
QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children
@ -52,6 +56,7 @@ private:
QString _assignmentServerHostname;
quint16 _assignmentServerPort;
QList<QProcess*> _childProcesses;
};
#endif // hifi_AssignmentClientMonitor_h

View file

@ -549,14 +549,18 @@ void AudioMixer::readPendingDatagram(const QByteArray& receivedPacket, const Hif
nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket);
} else if (mixerPacketType == PacketTypeMuteEnvironment) {
QByteArray packet = receivedPacket;
populatePacketHeader(packet, PacketTypeMuteEnvironment);
nodeList->eachNode([&](const SharedNodePointer& node){
if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != nodeList->sendingNodeForPacket(receivedPacket)) {
nodeList->writeDatagram(packet, packet.size(), node);
}
});
SharedNodePointer sendingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (sendingNode->getCanAdjustLocks()) {
QByteArray packet = receivedPacket;
populatePacketHeader(packet, PacketTypeMuteEnvironment);
nodeList->eachNode([&](const SharedNodePointer& node){
if (node->getType() == NodeType::Agent && node->getActiveSocket() &&
node->getLinkedData() && node != sendingNode) {
nodeList->writeDatagram(packet, packet.size(), node);
}
});
}
} else {
// let processNodeData handle it.
nodeList->processNodeData(senderSockAddr, receivedPacket);

View file

@ -45,6 +45,9 @@ AvatarMixer::AvatarMixer(const QByteArray& packet) :
}
AvatarMixer::~AvatarMixer() {
if (_broadcastTimer) {
_broadcastTimer->deleteLater();
}
_broadcastThread.quit();
_broadcastThread.wait();
}
@ -343,13 +346,13 @@ void AvatarMixer::run() {
nodeList->linkedDataCreateCallback = attachAvatarDataToNode;
// setup the timer that will be fired on the broadcast thread
QTimer* broadcastTimer = new QTimer();
broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS);
broadcastTimer->moveToThread(&_broadcastThread);
_broadcastTimer = new QTimer();
_broadcastTimer->setInterval(AVATAR_DATA_SEND_INTERVAL_MSECS);
_broadcastTimer->moveToThread(&_broadcastThread);
// connect appropriate signals and slots
connect(broadcastTimer, &QTimer::timeout, this, &AvatarMixer::broadcastAvatarData, Qt::DirectConnection);
connect(&_broadcastThread, SIGNAL(started()), broadcastTimer, SLOT(start()));
connect(_broadcastTimer, &QTimer::timeout, this, &AvatarMixer::broadcastAvatarData, Qt::DirectConnection);
connect(&_broadcastThread, SIGNAL(started()), _broadcastTimer, SLOT(start()));
// start the broadcastThread
_broadcastThread.start();

View file

@ -47,6 +47,8 @@ private:
int _numStatFrames;
int _sumBillboardPackets;
int _sumIdentityPackets;
QTimer* _broadcastTimer = nullptr;
};
#endif // hifi_AvatarMixer_h

View file

@ -27,6 +27,11 @@ EntityServer::EntityServer(const QByteArray& packet)
}
EntityServer::~EntityServer() {
if (_pruneDeletedEntitiesTimer) {
_pruneDeletedEntitiesTimer->stop();
_pruneDeletedEntitiesTimer->deleteLater();
}
EntityTree* tree = (EntityTree*)_tree;
tree->removeNewlyCreatedHook(this);
}
@ -48,10 +53,10 @@ Octree* EntityServer::createTree() {
}
void EntityServer::beforeRun() {
QTimer* pruneDeletedEntitiesTimer = new QTimer(this);
connect(pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities()));
_pruneDeletedEntitiesTimer = new QTimer();
connect(_pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities()));
const int PRUNE_DELETED_MODELS_INTERVAL_MSECS = 1 * 1000; // once every second
pruneDeletedEntitiesTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS);
_pruneDeletedEntitiesTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS);
}
void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) {

View file

@ -51,6 +51,7 @@ protected:
private:
EntitySimulation* _entitySimulation;
QTimer* _pruneDeletedEntitiesTimer = nullptr;
};
#endif // hifi_EntityServer_h

View file

@ -74,7 +74,7 @@ public:
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; }
void shuttingDown() { _shuttingDown = true;}
virtual void terminating() { _shuttingDown = true; ReceivedPacketProcessor::terminating(); }
protected:

View file

@ -266,16 +266,19 @@ OctreeServer::~OctreeServer() {
}
if (_jurisdictionSender) {
_jurisdictionSender->terminating();
_jurisdictionSender->terminate();
_jurisdictionSender->deleteLater();
}
if (_octreeInboundPacketProcessor) {
_octreeInboundPacketProcessor->terminating();
_octreeInboundPacketProcessor->terminate();
_octreeInboundPacketProcessor->deleteLater();
}
if (_persistThread) {
_persistThread->terminating();
_persistThread->terminate();
_persistThread->deleteLater();
}
@ -1095,8 +1098,6 @@ void OctreeServer::readConfiguration() {
}
void OctreeServer::run() {
qInstallMessageHandler(LogHandler::verboseMessageHandler);
_safeServerName = getMyServerName();
// Before we do anything else, create our tree...
@ -1219,8 +1220,15 @@ void OctreeServer::forceNodeShutdown(SharedNodePointer node) {
void OctreeServer::aboutToFinish() {
qDebug() << qPrintable(_safeServerName) << "server STARTING about to finish...";
qDebug() << qPrintable(_safeServerName) << "inform Octree Inbound Packet Processor that we are shutting down...";
_octreeInboundPacketProcessor->shuttingDown();
if (_octreeInboundPacketProcessor) {
_octreeInboundPacketProcessor->terminating();
}
if (_jurisdictionSender) {
_jurisdictionSender->terminating();
}
DependencyManager::get<NodeList>()->eachNode([this](const SharedNodePointer& node) {
qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node;
forceNodeShutdown(node);
@ -1228,6 +1236,7 @@ void OctreeServer::aboutToFinish() {
if (_persistThread) {
_persistThread->aboutToFinish();
_persistThread->terminating();
}
qDebug() << qPrintable(_safeServerName) << "server ENDING about to finish...";

31
cmake/externals/vhacd/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,31 @@
set(EXTERNAL_NAME vhacd)
if (ANDROID)
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
endif ()
include(ExternalProject)
ExternalProject_Add(
${EXTERNAL_NAME}
URL http://hifi-public.s3.amazonaws.com/dependencies/v-hacd-master.zip
URL_MD5 3bfc94f8dd3dfbfe8f4dc088f4820b3e
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
LOG_DOWNLOAD 1
LOG_CONFIGURE 1
LOG_BUILD 1
)
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
if (WIN32)
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/VHACD_LIB.lib CACHE FILEPATH "Path to V-HACD debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/VHACD_LIB.lib CACHE FILEPATH "Path to V-HACD release library")
else ()
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG "" CACHE FILEPATH "Path to V-HACD debug library")
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libVHACD.a CACHE FILEPATH "Path to V-HACD release library")
endif ()
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to V-HACD include directory")

View file

@ -0,0 +1,40 @@
#
# AddResourcesToOSXBundle.cmake
# cmake/macros
#
# Created by Stephen Birarda on 04/27/15.
# Copyright 2015 High Fidelity, Inc.
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
#
macro(_RECURSIVELY_SET_PACKAGE_LOCATION _PATH)
# enumerate the items
foreach(_ITEM ${ARGN})
if (IS_DIRECTORY "${_ITEM}")
# recurse into the directory and check for more resources
file(GLOB _DIR_CONTENTS "${_ITEM}/*")
# figure out the path for this directory and then call ourselves to recurse into it
get_filename_component(_ITEM_PATH ${_ITEM} NAME)
_recursively_set_package_location("${_PATH}/${_ITEM_PATH}" ${_DIR_CONTENTS})
else ()
# add any files in the root to the resources folder directly
SET_SOURCE_FILES_PROPERTIES(${_ITEM} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources${_PATH}")
list(APPEND DISCOVERED_RESOURCES ${_ITEM})
endif ()
endforeach()
endmacro(_RECURSIVELY_SET_PACKAGE_LOCATION _PATH)
macro(ADD_RESOURCES_TO_OS_X_BUNDLE _RSRC_FOLDER)
# GLOB the resource directory
file(GLOB _ROOT_ITEMS "${_RSRC_FOLDER}/*")
# recursively enumerate from the root items
_recursively_set_package_location("" ${_ROOT_ITEMS})
endmacro(ADD_RESOURCES_TO_OS_X_BUNDLE _RSRC_FOLDER)

View file

@ -22,11 +22,11 @@ macro(SETUP_EXTERNALS_BINARY_DIR)
endif ()
endif ()
set(EXTERNALS_BINARY_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build-ext")
set(EXTERNALS_BINARY_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/ext")
if (ANDROID)
set(EXTERNALS_BINARY_DIR "${EXTERNALS_BINARY_ROOT_DIR}/android/${CMAKE_GENERATOR_FOLDER_NAME}")
else ()
set(EXTERNALS_BINARY_DIR "${EXTERNALS_BINARY_ROOT_DIR}/${CMAKE_GENERATOR_FOLDER_NAME}")
endif ()
endmacro()
endmacro()

View file

@ -19,40 +19,16 @@
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("vhacd")
macro(_FIND_VHACD_LIBRARY _var)
set(_${_var}_NAMES ${ARGN})
find_library(${_var}_LIBRARY_RELEASE
NAMES ${_${_var}_NAMES}
HINTS
${VHACD_SEARCH_DIRS}
$ENV{VHACD_ROOT_DIR}
PATH_SUFFIXES lib lib/Release
)
find_library(${_var}_LIBRARY_DEBUG
NAMES ${_${_var}_NAMES}
HINTS
${VHACD_SEARCH_DIRS}
$ENV{VHACD_ROOT_DIR}
PATH_SUFFIXES lib lib/Debug
)
select_library_configurations(${_var})
mark_as_advanced(${_var}_LIBRARY)
mark_as_advanced(${_var}_LIBRARY)
endmacro()
find_path(VHACD_INCLUDE_DIRS VHACD.h PATH_SUFFIXES include HINTS ${VHACD_SEARCH_DIRS})
find_path(VHACD_INCLUDE_DIRS VHACD.h PATH_SUFFIXES include HINTS ${VHACD_SEARCH_DIRS} $ENV{VHACD_ROOT_DIR})
if(NOT WIN32)
_FIND_VHACD_LIBRARY(VHACD libVHACD.a)
else()
_FIND_VHACD_LIBRARY(VHACD VHACD_LIB)
endif()
find_library(VHACD_LIBRARY_DEBUG NAMES VHACD VHACD_LIB PATH_SUFFIXES lib/Debug HINTS ${VHACD_SEARCH_DIRS})
find_library(VHACD_LIBRARY_RELEASE NAMES VHACD VHACD_LIB PATH_SUFFIXES lib/Release lib HINTS ${VHACD_SEARCH_DIRS})
include(SelectLibraryConfigurations)
select_library_configurations(VHACD)
set(VHACD_LIBRARIES ${VHACD_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(VHACD "Could NOT find VHACD, try to set the path to VHACD root folder in the system variable VHACD_ROOT_DIR or create a directory vhacd in HIFI_LIB_DIR and paste the necessary files there"
VHACD_INCLUDE_DIRS VHACD_LIBRARIES)

View file

@ -1,68 +0,0 @@
#
# FindVisage.cmake
#
# Try to find the Visage controller library
#
# You must provide a VISAGE_ROOT_DIR which contains lib and include directories
#
# Once done this will define
#
# VISAGE_FOUND - system found Visage
# VISAGE_INCLUDE_DIRS - the Visage include directory
# VISAGE_LIBRARIES - Link this to use Visage
#
# Created on 2/11/2014 by Andrzej Kapolka
# Copyright 2014 High Fidelity, Inc.
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("visage")
find_path(VISAGE_BASE_INCLUDE_DIR VisageTracker2.h PATH_SUFFIXES include HINTS ${VISAGE_SEARCH_DIRS})
if (APPLE)
find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h HINTS /usr/include/libxml2 ${VISAGE_SEARCH_DIRS})
find_path(VISAGE_OPENCV_INCLUDE_DIR cv.h PATH_SUFFIXES dependencies/OpenCV_MacOSX/include HINTS ${VISAGE_SEARCH_DIRS})
find_path(VISAGE_OPENCV2_INCLUDE_DIR opencv.hpp PATH_SUFFIXES dependencies/OpenCV_MacOSX/include/opencv2 HINTS ${VISAGE_SEARCH_DIRS})
find_library(VISAGE_CORE_LIBRARY NAME vscore PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS})
find_library(VISAGE_VISION_LIBRARY NAME vsvision PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS})
find_library(VISAGE_OPENCV_LIBRARY NAME OpenCV PATH_SUFFIXES dependencies/OpenCV_MacOSX/lib HINTS ${VISAGE_SEARCH_DIRS})
find_library(CoreVideo CoreVideo)
find_library(QTKit QTKit)
find_library(IOKit IOKit)
elseif (WIN32)
find_path(VISAGE_XML_INCLUDE_DIR libxml/xmlreader.h PATH_SUFFIXES dependencies/libxml2/include HINTS ${VISAGE_SEARCH_DIRS})
find_path(VISAGE_OPENCV_INCLUDE_DIR opencv/cv.h PATH_SUFFIXES dependencies/OpenCV/include HINTS ${VISAGE_SEARCH_DIRS})
find_path(VISAGE_OPENCV2_INCLUDE_DIR cv.h PATH_SUFFIXES dependencies/OpenCV/include/opencv HINTS ${VISAGE_SEARCH_DIRS})
find_library(VISAGE_CORE_LIBRARY NAME vscore PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS})
find_library(VISAGE_VISION_LIBRARY NAME vsvision PATH_SUFFIXES lib HINTS ${VISAGE_SEARCH_DIRS})
find_library(VISAGE_OPENCV_LIBRARY NAME opencv_core243 PATH_SUFFIXES dependencies/OpenCV/lib HINTS ${VISAGE_SEARCH_DIRS})
endif ()
include(FindPackageHandleStandardArgs)
list(APPEND VISAGE_ARGS_LIST VISAGE_BASE_INCLUDE_DIR VISAGE_XML_INCLUDE_DIR
VISAGE_OPENCV_INCLUDE_DIR VISAGE_OPENCV2_INCLUDE_DIR
VISAGE_CORE_LIBRARY VISAGE_VISION_LIBRARY)
if (APPLE)
list(APPEND VISAGE_ARGS_LIST CoreVideo QTKit IOKit)
endif ()
find_package_handle_standard_args(Visage DEFAULT_MSG ${VISAGE_ARGS_LIST})
set(VISAGE_INCLUDE_DIRS "${VISAGE_XML_INCLUDE_DIR}" "${VISAGE_OPENCV_INCLUDE_DIR}" "${VISAGE_OPENCV2_INCLUDE_DIR}" "${VISAGE_BASE_INCLUDE_DIR}")
set(VISAGE_LIBRARIES "${VISAGE_CORE_LIBRARY}" "${VISAGE_VISION_LIBRARY}")
if (APPLE)
list(APPEND VISAGE_LIBRARIES "${CoreVideo}" "${QTKit}" "${IOKit}")
endif ()
mark_as_advanced(VISAGE_INCLUDE_DIRS VISAGE_LIBRARIES)

View file

@ -302,6 +302,9 @@ bool DomainServer::didSetupAccountManagerWithAccessToken() {
<< "at keypath metaverse.access_token or in your ENV at key DOMAIN_SERVER_ACCESS_TOKEN";
return false;
}
} else {
qDebug() << "Using access token from DOMAIN_SERVER_ACCESS_TOKEN in env. This overrides any access token present"
<< " in the user or master config.";
}
// give this access token to the AccountManager
@ -501,7 +504,7 @@ void DomainServer::populateStaticScriptedAssignmentsFromSettings() {
Assignment::AgentType,
scriptPool);
scriptAssignment->setPayload(scriptURL.toUtf8());
// add it to static hash so we know we have to keep giving it back out
addStaticAssignmentToAssignmentHash(scriptAssignment);
}
@ -672,6 +675,10 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID());
nodeData->setWalletUUID(pendingAssigneeData->getWalletUUID());
// always allow assignment clients to create and destroy entities
newNode->setCanAdjustLocks(true);
newNode->setCanRez(true);
// now that we've pulled the wallet UUID and added the node to our list, delete the pending assignee data
delete pendingAssigneeData;
}
@ -2116,10 +2123,10 @@ SharedAssignmentPointer DomainServer::deployableAssignmentForRequest(const Assig
Assignment* assignment = sharedAssignment->data();
bool requestIsAllTypes = requestAssignment.getType() == Assignment::AllTypes;
bool assignmentTypesMatch = assignment->getType() == requestAssignment.getType();
bool nietherHasPool = assignment->getPool().isEmpty() && requestAssignment.getPool().isEmpty();
bool neitherHasPool = assignment->getPool().isEmpty() && requestAssignment.getPool().isEmpty();
bool assignmentPoolsMatch = assignment->getPool() == requestAssignment.getPool();
if ((requestIsAllTypes || assignmentTypesMatch) && (nietherHasPool || assignmentPoolsMatch)) {
if ((requestIsAllTypes || assignmentTypesMatch) && (neitherHasPool || assignmentPoolsMatch)) {
// remove the assignment from the queue
SharedAssignmentPointer deployableAssignment = _unfulfilledAssignments.takeAt(sharedAssignment

View file

@ -33,7 +33,7 @@ DomainServerWebSessionData::DomainServerWebSessionData(const QJsonObject& userOb
}
}
DomainServerWebSessionData::DomainServerWebSessionData(const DomainServerWebSessionData& otherSessionData) {
DomainServerWebSessionData::DomainServerWebSessionData(const DomainServerWebSessionData& otherSessionData) : QObject() {
_username = otherSessionData._username;
_roles = otherSessionData._roles;
}

163
examples/acScripts/rain.js Normal file
View file

@ -0,0 +1,163 @@
//
// rain.js
// examples
//
// Created by David Rowe on 9 Apr 2015.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var RainSquall = function (properties) {
var // Properties
squallOrigin,
squallRadius,
dropsPerMinute = 60,
dropSize = { x: 0.1, y: 0.1, z: 0.1 },
dropFallSpeed = 1, // m/s
dropLifetime = 60, // Seconds
dropSpinMax = 0, // Maximum angular velocity per axis; deg/s
debug = false, // Display origin circle; don't use running on Stack Manager
// Other
squallCircle,
SQUALL_CIRCLE_COLOR = { red: 255, green: 0, blue: 0 },
SQUALL_CIRCLE_ALPHA = 0.5,
SQUALL_CIRCLE_ROTATION = Quat.fromPitchYawRollDegrees(90, 0, 0),
raindropProperties,
RAINDROP_MODEL_URL = "https://s3.amazonaws.com/hifi-public/ozan/Polyworld/Sets/sky/tetrahedron_v1-Faceted2.fbx",
raindropTimer,
raindrops = [], // HACK: Work around raindrops not always getting velocities
raindropVelocities = [], // HACK: Work around raindrops not always getting velocities
DEGREES_TO_RADIANS = Math.PI / 180;
function processProperties() {
if (!properties.hasOwnProperty("origin")) {
print("ERROR: Rain squall origin must be specified");
return;
}
squallOrigin = properties.origin;
if (!properties.hasOwnProperty("radius")) {
print("ERROR: Rain squall radius must be specified");
return;
}
squallRadius = properties.radius;
if (properties.hasOwnProperty("dropsPerMinute")) {
dropsPerMinute = properties.dropsPerMinute;
}
if (properties.hasOwnProperty("dropSize")) {
dropSize = properties.dropSize;
}
if (properties.hasOwnProperty("dropFallSpeed")) {
dropFallSpeed = properties.dropFallSpeed;
}
if (properties.hasOwnProperty("dropLifetime")) {
dropLifetime = properties.dropLifetime;
}
if (properties.hasOwnProperty("dropSpinMax")) {
dropSpinMax = properties.dropSpinMax;
}
if (properties.hasOwnProperty("debug")) {
debug = properties.debug;
}
raindropProperties = {
type: "Model",
modelURL: RAINDROP_MODEL_URL,
lifetime: dropLifetime,
dimensions: dropSize,
velocity: { x: 0, y: -dropFallSpeed, z: 0 },
damping: 0,
angularDamping: 0,
ignoreForCollisions: true
};
}
function createRaindrop() {
var angle,
radius,
offset,
drop,
spin = { x: 0, y: 0, z: 0 },
maxSpinRadians = properties.dropSpinMax * DEGREES_TO_RADIANS,
i;
// HACK: Work around rain drops not always getting velocities set at creation
for (i = 0; i < raindrops.length; i += 1) {
Entities.editEntity(raindrops[i], raindropVelocities[i]);
}
angle = Math.random() * 360;
radius = Math.random() * squallRadius;
offset = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, angle, 0), { x: 0, y: -0.1, z: radius });
raindropProperties.position = Vec3.sum(squallOrigin, offset);
if (properties.dropSpinMax > 0) {
spin = {
x: Math.random() * maxSpinRadians,
y: Math.random() * maxSpinRadians,
z: Math.random() * maxSpinRadians
};
raindropProperties.angularVelocity = spin;
}
drop = Entities.addEntity(raindropProperties);
// HACK: Work around rain drops not always getting velocities set at creation
raindrops.push(drop);
raindropVelocities.push({
velocity: raindropProperties.velocity,
angularVelocity: spin
});
if (raindrops.length > 5) {
raindrops.shift();
raindropVelocities.shift();
}
}
function setUp() {
if (debug) {
squallCircle = Overlays.addOverlay("circle3d", {
size: { x: 2 * squallRadius, y: 2 * squallRadius },
color: SQUALL_CIRCLE_COLOR,
alpha: SQUALL_CIRCLE_ALPHA,
solid: true,
visible: debug,
position: squallOrigin,
rotation: SQUALL_CIRCLE_ROTATION
});
}
raindropTimer = Script.setInterval(createRaindrop, 60000 / dropsPerMinute);
}
function tearDown() {
Script.clearInterval(raindropTimer);
if (debug) {
Overlays.deleteOverlay(squallCircle);
}
}
processProperties();
setUp();
Script.scriptEnding.connect(tearDown);
return {
};
};
var rainSquall1 = new RainSquall({
origin: { x: 1195, y: 1223, z: 1020 },
radius: 25,
dropsPerMinute: 120,
dropSize: { x: 0.1, y: 0.1, z: 0.1 },
dropFallSpeed: 3,
dropLifetime: 30,
dropSpinMax: 180,
debug: false
});

416
examples/avatarSelector.js Normal file
View file

@ -0,0 +1,416 @@
//
// avatarSelector.js
// examples
//
// Created by David Rowe on 21 Apr 2015.
// Copyright 2015 High Fidelity, Inc.
//
// Based on lobby.js created by Stephen Birarda on 17 Oct 2014.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var panelWall = false;
var orbShell = false;
var descriptionText = false;
var showText = false;
// used for formating the description text, in meters
var textWidth = 4;
var textHeight = .5;
var numberOfLines = 2;
var textMargin = 0.0625;
var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines;
var avatarStickPosition = {};
var orbNaturalExtentsMin = { x: -1.230354, y: -1.22077, z: -1.210487 };
var orbNaturalExtentsMax = { x: 1.230353, y: 1.229819, z: 1.210487 };
var panelsNaturalExtentsMin = { x: -1.223182, y: -0.348487, z: 0.0451369 };
var panelsNaturalExtentsMax = { x: 1.223039, y: 0.602978, z: 1.224298 };
var orbNaturalDimensions = Vec3.subtract(orbNaturalExtentsMax, orbNaturalExtentsMin);
var panelsNaturalDimensions = Vec3.subtract(panelsNaturalExtentsMax, panelsNaturalExtentsMin);
var SCALING_FACTOR = 10;
var orbDimensions = Vec3.multiply(orbNaturalDimensions, SCALING_FACTOR);
var panelsDimensions = Vec3.multiply(panelsNaturalDimensions, SCALING_FACTOR);
var orbNaturalCenter = Vec3.sum(orbNaturalExtentsMin, Vec3.multiply(orbNaturalDimensions, 0.5));
var panelsNaturalCenter = Vec3.sum(panelsNaturalExtentsMin, Vec3.multiply(panelsNaturalDimensions, 0.5));
var orbCenter = Vec3.multiply(orbNaturalCenter, SCALING_FACTOR);
var panelsCenter = Vec3.multiply(panelsNaturalCenter, SCALING_FACTOR);
var panelsCenterShift = Vec3.subtract(panelsCenter, orbCenter);
var ORB_SHIFT = { x: 0, y: -1.4, z: -0.8 };
var LOBBY_PANEL_WALL_URL = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/PanelWallForInterface.fbx";
var LOBBY_BLANK_PANEL_TEXTURE_URL = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/Texture.jpg";
var LOBBY_SHELL_URL = HIFI_PUBLIC_BUCKET + "models/sets/Lobby/LobbyShellForInterface.fbx";
var droneSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/drone.stereo.raw")
var currentDrone = null;
var latinSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/latin.stereo.raw")
var latinInjector = null;
var elevatorSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Lobby/elevator.stereo.raw")
var elevatorInjector = null;
var currentMuzakInjector = null;
var currentSound = null;
function textOverlayPosition() {
var TEXT_DISTANCE_OUT = 6;
var TEXT_DISTANCE_DOWN = -2;
return Vec3.sum(Vec3.sum(Camera.position, Vec3.multiply(Quat.getFront(Camera.orientation), TEXT_DISTANCE_OUT)),
Vec3.multiply(Quat.getUp(Camera.orientation), TEXT_DISTANCE_DOWN));
}
var panelPlaceOrder = [
7, 8, 9, 10, 11, 12, 13,
0, 1, 2, 3, 4, 5, 6,
14, 15, 16, 17, 18, 19, 20
];
// Avatar index is 0-based
function avatarIndexToPanelIndex(avatarIndex) {
return panelPlaceOrder.indexOf(avatarIndex) + 1;
}
// Panel index is 1-based
function panelIndexToPlaceIndex(panelIndex) {
return panelPlaceOrder[panelIndex - 1];
}
var MAX_NUM_PANELS = 21;
var DRONE_VOLUME = 0.3;
function drawLobby() {
if (!panelWall) {
print("Adding overlays for the avatar selector panel wall and orb shell.");
var cameraEuler = Quat.safeEulerAngles(Camera.orientation);
var towardsMe = Quat.angleAxis(cameraEuler.y + 180, { x: 0, y: 1, z: 0 });
var orbPosition = Vec3.sum(Camera.position, Vec3.multiplyQbyV(towardsMe, ORB_SHIFT));
var panelWallProps = {
url: LOBBY_PANEL_WALL_URL,
position: Vec3.sum(orbPosition, Vec3.multiplyQbyV(towardsMe, panelsCenterShift)),
rotation: towardsMe,
dimensions: panelsDimensions
};
var orbShellProps = {
url: LOBBY_SHELL_URL,
position: orbPosition,
rotation: towardsMe,
dimensions: orbDimensions,
ignoreRayIntersection: true
};
var windowDimensions = Controller.getViewportDimensions();
var descriptionTextProps = {
position: textOverlayPosition(),
dimensions: { x: textWidth, y: textHeight },
backgroundColor: { red: 0, green: 0, blue: 0 },
color: { red: 255, green: 255, blue: 255 },
topMargin: textMargin,
leftMargin: textMargin,
bottomMargin: textMargin,
rightMargin: textMargin,
text: "",
lineHeight: lineHeight,
alpha: 0.9,
backgroundAlpha: 0.9,
ignoreRayIntersection: true,
visible: false,
isFacingAvatar: true
};
avatarStickPosition = MyAvatar.position;
panelWall = Overlays.addOverlay("model", panelWallProps);
orbShell = Overlays.addOverlay("model", orbShellProps);
descriptionText = Overlays.addOverlay("text3d", descriptionTextProps);
if (droneSound.downloaded) {
// start the drone sound
if (!currentDrone) {
currentDrone = Audio.playSound(droneSound, { stereo: true, loop: true, localOnly: true, volume: DRONE_VOLUME });
} else {
currentDrone.restart();
}
}
// start one of our muzak sounds
playRandomMuzak();
}
}
var avatars = {};
function changeLobbyTextures() {
var req = new XMLHttpRequest();
req.open("GET", "https://metaverse.highfidelity.com/api/v1/marketplace?category=head+%26+body&limit=21", false);
req.send(); // Data returned is randomized.
avatars = JSON.parse(req.responseText).data.items;
var NUM_PANELS = avatars.length;
var textureProp = {
textures: {}
};
for (var j = 0; j < NUM_PANELS; j++) {
var panelIndex = avatarIndexToPanelIndex(j);
textureProp["textures"]["file" + panelIndex] = avatars[j].preview_url;
};
Overlays.editOverlay(panelWall, textureProp);
}
var MUZAK_VOLUME = 0.1;
function playCurrentSound(secondOffset) {
if (currentSound == latinSound) {
if (!latinInjector) {
latinInjector = Audio.playSound(latinSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME });
} else {
latinInjector.restart();
}
currentMuzakInjector = latinInjector;
} else if (currentSound == elevatorSound) {
if (!elevatorInjector) {
elevatorInjector = Audio.playSound(elevatorSound, { localOnly: true, secondOffset: secondOffset, volume: MUZAK_VOLUME });
} else {
elevatorInjector.restart();
}
currentMuzakInjector = elevatorInjector;
}
}
function playNextMuzak() {
if (panelWall) {
if (currentSound == latinSound) {
if (elevatorSound.downloaded) {
currentSound = elevatorSound;
}
} else if (currentSound == elevatorSound) {
if (latinSound.downloaded) {
currentSound = latinSound;
}
}
playCurrentSound(0);
}
}
function playRandomMuzak() {
currentSound = null;
if (latinSound.downloaded && elevatorSound.downloaded) {
currentSound = Math.random() < 0.5 ? latinSound : elevatorSound;
} else if (latinSound.downloaded) {
currentSound = latinSound;
} else if (elevatorSound.downloaded) {
currentSound = elevatorSound;
}
if (currentSound) {
// pick a random number of seconds from 0-10 to offset the muzak
var secondOffset = Math.random() * 10;
playCurrentSound(secondOffset);
} else {
currentMuzakInjector = null;
}
}
function cleanupLobby() {
toggleEnvironmentRendering(true);
// for each of the 21 placeholder textures, set them back to default so the cached model doesn't have changed textures
var panelTexturesReset = {};
panelTexturesReset["textures"] = {};
for (var j = 0; j < MAX_NUM_PANELS; j++) {
panelTexturesReset["textures"]["file" + (j + 1)] = LOBBY_BLANK_PANEL_TEXTURE_URL;
};
Overlays.editOverlay(panelWall, panelTexturesReset);
Overlays.deleteOverlay(panelWall);
Overlays.deleteOverlay(orbShell);
Overlays.deleteOverlay(descriptionText);
panelWall = false;
orbShell = false;
if (currentDrone) {
currentDrone.stop();
currentDrone = null
}
if (currentMuzakInjector) {
currentMuzakInjector.stop();
currentMuzakInjector = null;
}
avatars = {};
}
function actionStartEvent(event) {
if (panelWall) {
// we've got an action event and our panel wall is up
// check if we hit a panel and if we should jump there
var result = Overlays.findRayIntersection(event.actionRay);
if (result.intersects && result.overlayID == panelWall) {
var panelName = result.extraInfo;
var panelStringIndex = panelName.indexOf("Panel");
if (panelStringIndex != -1) {
var panelIndex = parseInt(panelName.slice(5));
var avatarIndex = panelIndexToPlaceIndex(panelIndex);
if (avatarIndex < avatars.length) {
var actionPlace = avatars[avatarIndex];
print("Changing avatar to " + actionPlace.name
+ " after click on panel " + panelIndex + " with avatar index " + avatarIndex);
MyAvatar.useFullAvatarURL(actionPlace.content_url);
maybeCleanupLobby();
}
}
}
}
}
var control = false;
function keyPressEvent(event) {
if (event.text === "CONTROL") {
control = true;
}
if (control && event.text === "a") {
if (!panelWall) {
toggleEnvironmentRendering(false);
drawLobby();
changeLobbyTextures();
} else {
cleanupLobby();
}
}
}
function keyReleaseEvent(event) {
if (event.text === "CONTROL") {
control = false;
}
}
var CLEANUP_EPSILON_DISTANCE = 0.05;
function maybeCleanupLobby() {
if (panelWall && Vec3.length(Vec3.subtract(avatarStickPosition, MyAvatar.position)) > CLEANUP_EPSILON_DISTANCE) {
cleanupLobby();
}
}
function toggleEnvironmentRendering(shouldRender) {
Scene.shouldRenderAvatars = shouldRender;
Scene.shouldRenderEntities = shouldRender;
}
function handleLookAt(pickRay) {
if (panelWall && descriptionText) {
// we've got an action event and our panel wall is up
// check if we hit a panel and if we should jump there
var result = Overlays.findRayIntersection(pickRay);
if (result.intersects && result.overlayID == panelWall) {
var panelName = result.extraInfo;
var panelStringIndex = panelName.indexOf("Panel");
if (panelStringIndex != -1) {
var panelIndex = parseInt(panelName.slice(5));
var avatarIndex = panelIndexToPlaceIndex(panelIndex);
if (avatarIndex < avatars.length) {
var actionPlace = avatars[avatarIndex];
if (actionPlace.description == "") {
Overlays.editOverlay(descriptionText, { text: actionPlace.name, visible: showText });
} else {
// handle line wrapping
var allWords = actionPlace.description.split(" ");
var currentGoodLine = "";
var currentTestLine = "";
var formatedDescription = "";
var wordsFormated = 0;
var currentTestWord = 0;
var wordsOnLine = 0;
while (wordsFormated < allWords.length) {
// first add the "next word" to the line and test it.
currentTestLine = currentGoodLine;
if (wordsOnLine > 0) {
currentTestLine += " " + allWords[currentTestWord];
} else {
currentTestLine = allWords[currentTestWord];
}
var lineLength = Overlays.textSize(descriptionText, currentTestLine).width;
if (lineLength < textWidth || wordsOnLine == 0) {
wordsFormated++;
currentTestWord++;
wordsOnLine++;
currentGoodLine = currentTestLine;
} else {
formatedDescription += currentGoodLine + "\n";
wordsOnLine = 0;
currentGoodLine = "";
currentTestLine = "";
}
}
formatedDescription += currentGoodLine;
Overlays.editOverlay(descriptionText, { text: formatedDescription, visible: showText });
}
} else {
Overlays.editOverlay(descriptionText, { text: "", visible: false });
}
}
}
}
}
function update(deltaTime) {
maybeCleanupLobby();
if (panelWall) {
Overlays.editOverlay(descriptionText, { position: textOverlayPosition() });
// if the reticle is up then we may need to play the next muzak
if (currentMuzakInjector && !currentMuzakInjector.isPlaying) {
playNextMuzak();
}
}
}
function mouseMoveEvent(event) {
if (panelWall) {
var pickRay = Camera.computePickRay(event.x, event.y);
handleLookAt(pickRay);
}
}
Controller.actionStartEvent.connect(actionStartEvent);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.update.connect(update);
Script.scriptEnding.connect(maybeCleanupLobby);
Controller.mouseMoveEvent.connect(mouseMoveEvent);

View file

@ -18,3 +18,4 @@ Script.load("lobby.js");
Script.load("notifications.js");
Script.load("look.js");
Script.load("users.js");
Script.load("grab.js");

View file

@ -28,6 +28,7 @@ Script.include([
"libraries/gridTool.js",
"libraries/entityList.js",
"libraries/lightOverlayManager.js",
"libraries/zoneOverlayManager.js",
]);
var selectionDisplay = SelectionDisplay;
@ -35,6 +36,7 @@ var selectionManager = SelectionManager;
var entityPropertyDialogBox = EntityPropertyDialogBox;
var lightOverlayManager = new LightOverlayManager();
var zoneOverlayManager = new ZoneOverlayManager();
var cameraManager = new CameraManager();
@ -47,6 +49,7 @@ var entityListTool = EntityListTool();
selectionManager.addEventListener(function() {
selectionDisplay.updateHandles();
lightOverlayManager.updatePositions();
zoneOverlayManager.updatePositions();
});
var windowDimensions = Controller.getViewportDimensions();
@ -77,11 +80,13 @@ var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS);
var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select";
var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus";
var MENU_SHOW_LIGHTS_IN_EDIT_MODE = "Show Lights in Edit Mode";
var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Edit Mode";
var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled";
var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect";
var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus";
var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode";
var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode";
var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain."
var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain."
@ -135,6 +140,7 @@ var toolBar = (function () {
newSphereButton,
newLightButton,
newTextButton,
newZoneButton,
browseMarketplaceButton;
function initialize() {
@ -201,6 +207,14 @@ var toolBar = (function () {
alpha: 0.9,
visible: false
});
newZoneButton = toolBar.addTool({
imageURL: toolIconUrl + "zonecube_text.svg",
subImage: { x: 0, y: 128, width: 128, height: 128 },
width: toolWidth,
height: toolHeight,
alpha: 0.9,
visible: false
});
that.setActive(false);
}
@ -232,6 +246,7 @@ var toolBar = (function () {
}
toolBar.selectTool(activeButton, isActive);
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
zoneOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
};
// Sets visibility of tool buttons, excluding the power button
@ -241,6 +256,7 @@ var toolBar = (function () {
toolBar.showTool(newSphereButton, doShow);
toolBar.showTool(newLightButton, doShow);
toolBar.showTool(newTextButton, doShow);
toolBar.showTool(newZoneButton, doShow);
};
var RESIZE_INTERVAL = 50;
@ -412,6 +428,21 @@ var toolBar = (function () {
return true;
}
if (newZoneButton === toolBar.clicked(clickedOverlay)) {
var position = getPositionToCreateEntity();
if (position.x > 0 && position.y > 0 && position.z > 0) {
placingEntityID = Entities.addEntity({
type: "Zone",
position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), DEFAULT_DIMENSIONS),
dimensions: { x: 10, y: 10, z: 10 },
});
} else {
print("Can't create box: Text would be out of bounds.");
}
return true;
}
return false;
};
@ -486,12 +517,22 @@ function rayPlaneIntersection(pickRay, point, normal) {
}
function findClickedEntity(event) {
var pickZones = event.isControl;
if (pickZones) {
Entities.setZonesArePickable(true);
}
var pickRay = Camera.computePickRay(event.x, event.y);
var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking
var lightResult = lightOverlayManager.findRayIntersection(pickRay);
lightResult.accurate = true;
if (pickZones) {
Entities.setZonesArePickable(false);
}
var result;
if (!entityResult.intersects && !lightResult.intersects) {
@ -551,7 +592,11 @@ var idleMouseTimerId = null;
var IDLE_MOUSE_TIMEOUT = 200;
var DEFAULT_ENTITY_DRAG_DROP_DISTANCE = 2.0;
function mouseMoveEvent(event) {
var lastMouseMoveEvent = null;
function mouseMoveEventBuffered(event) {
lastMouseMoveEvent = event;
}
function mouseMove(event) {
mouseHasMovedSincePress = true;
if (placingEntityID) {
@ -620,6 +665,10 @@ function highlightEntityUnderCursor(position, accurateRay) {
function mouseReleaseEvent(event) {
if (lastMouseMoveEvent) {
mouseMove(lastMouseMoveEvent);
lastMouseMoveEvent = null;
}
if (propertyMenu.mouseReleaseEvent(event) || toolBar.mouseReleaseEvent(event)) {
return true;
}
@ -731,7 +780,7 @@ function mouseClickEvent(event) {
}
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mouseMoveEvent.connect(mouseMoveEventBuffered);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
@ -776,6 +825,8 @@ function setupModelMenus() {
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, afterItem: MENU_EASE_ON_FOCUS,
isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true" });
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE,
isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true" });
Entities.setLightsArePickable(false);
}
@ -804,12 +855,14 @@ function cleanupModelMenus() {
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS);
Menu.removeMenuItem("View", MENU_SHOW_LIGHTS_IN_EDIT_MODE);
Menu.removeMenuItem("View", MENU_SHOW_ZONES_IN_EDIT_MODE);
}
Script.scriptEnding.connect(function() {
Settings.setValue(SETTING_AUTO_FOCUS_ON_SELECT, Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT));
Settings.setValue(SETTING_EASE_ON_FOCUS, Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
Settings.setValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
progressDialog.cleanup();
toolBar.cleanup();
@ -837,6 +890,10 @@ Script.update.connect(function (deltaTime) {
lastOrientation = Camera.orientation;
lastPosition = Camera.position;
}
if (lastMouseMoveEvent) {
mouseMove(lastMouseMoveEvent);
lastMouseMoveEvent = null;
}
});
function insideBox(center, dimensions, point) {
@ -942,6 +999,8 @@ function handeMenuEvent(menuItem) {
selectAllEtitiesInCurrentSelectionBox(true);
} else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) {
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
} else if (menuItem == MENU_SHOW_ZONES_IN_EDIT_MODE) {
zoneOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
}
tooltip.show(false);
}
@ -1154,6 +1213,9 @@ PropertiesTool = function(opts) {
data.properties.rotation = Quat.fromPitchYawRollDegrees(rotation.x, rotation.y, rotation.z);
}
Entities.editEntity(selectionManager.selections[0], data.properties);
if (data.properties.name != undefined) {
entityListTool.sendUpdate();
}
}
pushCommandForSelections();
selectionManager._update();

View file

@ -0,0 +1,23 @@
//
// fullDomainZoneEntityExample.js
// examples
//
// Created by Brad Hefta-Gaub on 4/16/15.
// Copyright 2015 High Fidelity, Inc.
//
// This is an example script that demonstrates creating a Zone which is as large as the entire domain
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Entities.addEntity({
type: "Zone",
position: { x: -10000, y: -10000, z: -10000 },
dimensions: { x: 30000, y: 30000, z: 30000 },
keyLightColor: { red: 255, green: 255, blue: 0 },
keyLightDirection: { x: 0, y: -1.0, z: 0 },
keyLightIntensity: 1.0,
keyLightAmbientIntensity: 0.5
});

View file

@ -0,0 +1,72 @@
//
// zoneEntityExample.js
// examples
//
// Created by Brad Hefta-Gaub on 4/16/15.
// Copyright 2015 High Fidelity, Inc.
//
// This is an example script that demonstrates creating and editing a entity
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var count = 0;
var stopAfter = 1000;
var zoneEntityA = Entities.addEntity({
type: "Zone",
position: { x: 5, y: 5, z: 5 },
dimensions: { x: 10, y: 10, z: 10 },
keyLightColor: { red: 255, green: 0, blue: 0 },
stageSunModelEnabled: false,
keyLightDirection: { x: 0, y: -1.0, z: 0 },
shapeType: "sphere"
});
print("zoneEntityA:" + zoneEntityA);
var zoneEntityB = Entities.addEntity({
type: "Zone",
position: { x: 5, y: 5, z: 5 },
dimensions: { x: 2, y: 2, z: 2 },
keyLightColor: { red: 0, green: 255, blue: 0 },
keyLightIntensity: 0.9,
stageLatitude: 37.777,
stageLongitude: 122.407,
stageAltitude: 0.03,
stageDay: 60,
stageHour: 12,
stageSunModelEnabled: true
});
print("zoneEntityB:" + zoneEntityB);
var zoneEntityC = Entities.addEntity({
type: "Zone",
position: { x: 5, y: 10, z: 5 },
dimensions: { x: 10, y: 10, z: 10 },
keyLightColor: { red: 0, green: 0, blue: 255 },
keyLightIntensity: 0.75,
keyLightDirection: { x: 0, y: 0, z: -1 },
stageSunModelEnabled: false,
shapeType: "compound",
compoundShapeURL: "http://headache.hungry.com/~seth/hifi/cube.fbx"
});
print("zoneEntityC:" + zoneEntityC);
// register the call back so it fires before each data send
Script.update.connect(function(deltaTime) {
// stop it...
if (count >= stopAfter) {
print("calling Script.stop()");
Script.stop();
}
count++;
});

View file

@ -39,126 +39,130 @@ hitSounds.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "Collisions-ballhitsandc
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var screenSize = Controller.getViewportDimensions();
var reticle = Overlays.addOverlay("image", {
x: screenSize.x / 2 - 16,
y: screenSize.y / 2 - 16,
width: 32,
height: 32,
imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
x: screenSize.x / 2 - 16,
y: screenSize.y / 2 - 16,
width: 32,
height: 32,
imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
function makeTable(pos) {
// Top
tableParts.push(Entities.addEntity(
// Top
tableParts.push(Entities.addEntity(
{ type: "Box",
position: pos,
dimensions: { x: LENGTH * SCALE, y: HEIGHT, z: WIDTH * SCALE },
color: { red: 0, green: 255, blue: 0 } }));
// Long Bumpers
tableParts.push(Entities.addEntity(
position: pos,
dimensions: { x: LENGTH * SCALE, y: HEIGHT, z: WIDTH * SCALE },
color: { red: 0, green: 255, blue: 0 } }));
// Long Bumpers
tableParts.push(Entities.addEntity(
{ type: "Box",
position: { x: pos.x - LENGTH / 2.0,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
tableParts.push(Entities.addEntity(
position: { x: pos.x - LENGTH / 2.0,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
tableParts.push(Entities.addEntity(
{ type: "Box",
position: { x: pos.x + LENGTH / 2.0,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
position: { x: pos.x + LENGTH / 2.0,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
tableParts.push(Entities.addEntity(
tableParts.push(Entities.addEntity(
{ type: "Box",
position: { x: pos.x - LENGTH / 2.0,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
tableParts.push(Entities.addEntity(
position: { x: pos.x - LENGTH / 2.0,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
tableParts.push(Entities.addEntity(
{ type: "Box",
position: { x: pos.x + LENGTH / 2.0,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
// End bumpers
tableParts.push(Entities.addEntity(
position: { x: pos.x + LENGTH / 2.0,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
// End bumpers
tableParts.push(Entities.addEntity(
{ type: "Box",
position: { x: pos.x + (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z },
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
position: { x: pos.x + (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z },
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
tableParts.push(Entities.addEntity(
tableParts.push(Entities.addEntity(
{ type: "Box",
position: { x: pos.x - (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z },
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
position: { x: pos.x - (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
z: pos.z },
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
color: { red: 237, green: 201, blue: 175 } }));
}
function makeBalls(pos) {
// Object balls
// Object balls
var whichBall = [ 1, 14, 15, 4, 8, 7, 12, 9, 3, 13, 10, 5, 6, 11, 2 ];
var ballNumber = 0;
var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
for (var row = 1; row <= 5; row++) {
ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE);
for (var spot = 0; spot < row; spot++) {
balls.push(Entities.addEntity(
var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
for (var row = 1; row <= 5; row++) {
ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE);
for (var spot = 0; spot < row; spot++) {
balls.push(Entities.addEntity(
{ type: "Model",
modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/ball_" + whichBall[ballNumber].toString() + ".fbx",
position: ballPosition,
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
rotation: Quat.fromPitchYawRollDegrees((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20),
color: { red: 255, green: 255, blue: 255 },
gravity: { x: 0, y: GRAVITY, z: 0 },
ignoreCollisions: false,
damping: 0.50,
shapeType: "sphere",
collisionsWillMove: true }));
ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE;
modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/ball_" +
whichBall[ballNumber].toString() + ".fbx",
position: ballPosition,
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
rotation: Quat.fromPitchYawRollDegrees((Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20),
color: { red: 255, green: 255, blue: 255 },
gravity: { x: 0, y: GRAVITY, z: 0 },
velocity: {x: 0, y: -0.2, z: 0 },
ignoreCollisions: false,
damping: 0.50,
shapeType: "sphere",
collisionsWillMove: true }));
ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE;
ballNumber++;
}
ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE;
}
ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE;
}
// Cue Ball
cuePosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
cueBall = Entities.addEntity(
{ type: "Model",
modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/cue_ball.fbx",
position: cuePosition,
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
color: { red: 255, green: 255, blue: 255 },
gravity: { x: 0, y: GRAVITY, z: 0 },
angularVelocity: { x: 0, y: 0, z: 0 },
velocity: {x: 0, y: 0, z: 0 },
ignoreCollisions: false,
damping: 0.50,
shapeType: "sphere",
collisionsWillMove: true });
{ type: "Model",
modelURL: "https://s3.amazonaws.com/hifi-public/models/props/Pool/cue_ball.fbx",
position: cuePosition,
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
color: { red: 255, green: 255, blue: 255 },
gravity: { x: 0, y: GRAVITY, z: 0 },
angularVelocity: { x: 0, y: 0, z: 0 },
velocity: {x: 0, y: -0.2, z: 0 },
ignoreCollisions: false,
damping: 0.50,
shapeType: "sphere",
collisionsWillMove: true });
}
function isObjectBall(id) {
for (var i; i < balls.length; i++) {
if (balls[i].id == id) {
return true;
}
}
return false;
for (var i; i < balls.length; i++) {
if (balls[i].id == id) {
return true;
}
}
return false;
}
function shootCue(velocity) {
var DISTANCE_FROM_CAMERA = BALL_SIZE * 5.0 * SCALE;
var DISTANCE_FROM_CAMERA = BALL_SIZE * 5.0 * SCALE;
var camera = Camera.getPosition();
var forwardVector = Quat.getFront(Camera.getOrientation());
var cuePosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
@ -180,14 +184,14 @@ function shootCue(velocity) {
density: 8000,
ignoreCollisions: false,
collisionsWillMove: true
});
});
print("Shot, velocity = " + velocity);
}
function keyReleaseEvent(event) {
if ((startStroke > 0) && event.text == "SPACE") {
var endTime = new Date().getTime();
var delta = endTime - startStroke;
if ((startStroke > 0) && event.text == "SPACE") {
var endTime = new Date().getTime();
var delta = endTime - startStroke;
shootCue(delta / 100.0);
startStroke = 0;
}
@ -201,49 +205,49 @@ function keyPressEvent(event) {
}
function cleanup() {
for (var i = 0; i < tableParts.length; i++) {
if (!tableParts[i].isKnownID) {
tableParts[i] = Entities.identifyEntity(tableParts[i]);
}
Entities.deleteEntity(tableParts[i]);
for (var i = 0; i < tableParts.length; i++) {
if (!tableParts[i].isKnownID) {
tableParts[i] = Entities.identifyEntity(tableParts[i]);
}
for (var i = 0; i < balls.length; i++) {
if (!balls[i].isKnownID) {
balls[i] = Entities.identifyEntity(balls[i]);
}
Entities.deleteEntity(balls[i]);
Entities.deleteEntity(tableParts[i]);
}
for (var i = 0; i < balls.length; i++) {
if (!balls[i].isKnownID) {
balls[i] = Entities.identifyEntity(balls[i]);
}
Overlays.deleteOverlay(reticle);
Entities.deleteEntity(cueBall);
Entities.deleteEntity(balls[i]);
}
Overlays.deleteOverlay(reticle);
Entities.deleteEntity(cueBall);
}
function update(deltaTime) {
if (!cueBall.isKnownID) {
cueBall = Entities.identifyEntity(cueBall);
} else {
// Check if cue ball has fallen off table, re-drop if so
var cueProperties = Entities.getEntityProperties(cueBall);
if (cueProperties.position.y < tableCenter.y) {
// Replace the cueball
Entities.editEntity(cueBall, { position: cuePosition } );
if (!cueBall.isKnownID) {
cueBall = Entities.identifyEntity(cueBall);
} else {
// Check if cue ball has fallen off table, re-drop if so
var cueProperties = Entities.getEntityProperties(cueBall);
if (cueProperties.position.y < tableCenter.y) {
// Replace the cueball
Entities.editEntity(cueBall, { position: cuePosition } );
}
}
}
}
function entityCollisionWithEntity(entity1, entity2, collision) {
/*
NOT WORKING YET
if ((entity1.id == cueBall.id) || (entity2.id == cueBall.id)) {
print("Cue ball collision!");
//audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
//Audio.playSound(hitSounds[0], { position: Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())) });
}
/*
NOT WORKING YET
if ((entity1.id == cueBall.id) || (entity2.id == cueBall.id)) {
print("Cue ball collision!");
//audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
//Audio.playSound(hitSounds[0], { position: Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())) });
}
else if (isObjectBall(entity1.id) || isObjectBall(entity2.id)) {
print("Object ball collision");
} */
else if (isObjectBall(entity1.id) || isObjectBall(entity2.id)) {
print("Object ball collision");
} */
}
tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation())));

View file

@ -0,0 +1,41 @@
//
// listAllScripts.js
// examples/example/misc
//
// Created by Thijs Wenker on 7 Apr 2015
// Copyright 2015 High Fidelity, Inc.
//
// Outputs the running, public and local scripts to the console.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var runningScripts = ScriptDiscoveryService.getRunning();
print("Running Scripts:");
for (var i = 0; i < runningScripts.length; i++) {
print(" - " + runningScripts[i].name + (runningScripts[i].local ? "[Local]" : "") + " {" + runningScripts[i].url + "}");
}
var localScripts = ScriptDiscoveryService.getLocal();
print("Local Scripts:");
for (var i = 0; i < localScripts.length; i++) {
print(" - " + localScripts[i].name + " {" + localScripts[i].path + "}");
}
// recursive function to walk through all folders in public scripts
// adding 2 spaces to the prefix per depth level
function displayPublicScriptFolder(nodes, prefix) {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].type == "folder") {
print(prefix + "<" + nodes[i].name + ">");
displayPublicScriptFolder(nodes[i].children, " " + prefix);
continue;
}
print(prefix + nodes[i].name + " {" + nodes[i].url + "}");
}
}
var publicScripts = ScriptDiscoveryService.getPublic();
print("Public Scripts:");
displayPublicScriptFolder(publicScripts, " - ");

View file

@ -41,7 +41,8 @@ panel.newSlider("Year Time", 0, 364,
);
panel.newSlider("Day Time", 0, 24,
function(value) { Scene.setStageDayTime(value); },
function(value) { Scene.setStageDayTime(value); panel.update("Light Direction"); },
function() { return Scene.getStageDayTime(); },
function(value) {
var hour = Math.floor(value);
@ -72,12 +73,36 @@ function runStageTime() {
}
Script.setInterval(runStageTime, tickTackPeriod);
panel.newCheckbox("Enable Sun Model",
function(value) { Scene.setStageSunModelEnable((value != 0)); },
function() { return Scene.isStageSunModelEnabled(); },
function(value) { return (value); }
);
panel.newDirectionBox("Light Direction",
function(value) { Scene.setKeyLightDirection(value); },
function() { return Scene.getKeyLightDirection(); },
function(value) { return value.x.toFixed(2) + "," + value.y.toFixed(2) + "," + value.z.toFixed(2); }
);
panel.newSlider("Light Intensity", 0.0, 5,
function(value) { Scene.setSunIntensity(value); },
function() { return Scene.getSunIntensity(); },
function(value) { Scene.setKeyLightIntensity(value); },
function() { return Scene.getKeyLightIntensity(); },
function(value) { return (value).toFixed(2); }
);
panel.newSlider("Ambient Light Intensity", 0.0, 1.0,
function(value) { Scene.setKeyLightAmbientIntensity(value); },
function() { return Scene.getKeyLightAmbientIntensity(); },
function(value) { return (value).toFixed(2); }
);
panel.newColorBox("Light Color",
function(value) { Scene.setKeyLightColor(value); },
function() { return Scene.getKeyLightColor(); },
function(value) { return (value); } // "(" + value.x + "," = value.y + "," + value.z + ")"; }
);
Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); });
Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); });
Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); });

166
examples/grab.js Normal file
View file

@ -0,0 +1,166 @@
var isGrabbing = false;
var grabbedEntity = null;
var prevMouse = {};
var deltaMouse = {
z: 0
}
var entityProps;
var targetPosition;
var moveUpDown = false;
var currentPosition, currentVelocity;
var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav");
var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav");
var DROP_DISTANCE = 5.0;
var DROP_COLOR = {
red: 200,
green: 200,
blue: 200
};
var DROP_WIDTH = 2;
var dropLine = Overlays.addOverlay("line3d", {
start: {
x: 0,
y: 0,
z: 0
},
end: {
x: 0,
y: 0,
z: 0
},
color: DROP_COLOR,
alpha: 1,
visible: false,
lineWidth: DROP_WIDTH
});
function mousePressEvent(event) {
if (!event.isLeftButton) {
return;
}
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay);
if (intersection.intersects && intersection.properties.collisionsWillMove) {
grabbedEntity = intersection.entityID;
var props = Entities.getEntityProperties(grabbedEntity)
isGrabbing = true;
targetPosition = props.position;
currentPosition = props.position;
currentVelocity = props.velocity;
updateDropLine(targetPosition);
Audio.playSound(grabSound, {
position: props.position,
volume: 0.4
});
}
}
function updateDropLine(position) {
Overlays.editOverlay(dropLine, {
visible: true,
start: position,
end: Vec3.sum(position, {
x: 0,
y: -DROP_DISTANCE,
z: 0
})
})
}
function mouseReleaseEvent() {
if (isGrabbing) {
isGrabbing = false;
Overlays.editOverlay(dropLine, {
visible: false
});
targetPosition = null;
Audio.playSound(grabSound, {
position: entityProps.position,
volume: 0.25
});
}
}
function mouseMoveEvent(event) {
if (isGrabbing) {
deltaMouse.x = event.x - prevMouse.x;
if (!moveUpDown) {
deltaMouse.z = event.y - prevMouse.y;
deltaMouse.y = 0;
} else {
deltaMouse.y = (event.y - prevMouse.y) * -1;
deltaMouse.z = 0;
}
// Update the target position by the amount the mouse moved
var camYaw = Quat.safeEulerAngles(Camera.getOrientation()).y;
var dPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), deltaMouse);
// Adjust target position for the object by the mouse move
var avatarEntityDistance = Vec3.distance(Camera.getPosition(), currentPosition);
// Scale distance we want to move by the distance from the camera to the grabbed object
// TODO: Correct SCREEN_TO_METERS to be correct for the actual FOV, resolution
var SCREEN_TO_METERS = 0.001;
targetPosition = Vec3.sum(targetPosition, Vec3.multiply(dPosition, avatarEntityDistance * SCREEN_TO_METERS));
}
prevMouse.x = event.x;
prevMouse.y = event.y;
}
function keyReleaseEvent(event) {
if (event.text === "SHIFT") {
moveUpDown = false;
}
}
function keyPressEvent(event) {
if (event.text === "SHIFT") {
moveUpDown = true;
}
}
function update(deltaTime) {
if (isGrabbing) {
entityProps = Entities.getEntityProperties(grabbedEntity);
currentPosition = entityProps.position;
currentVelocity = entityProps.velocity;
var dPosition = Vec3.subtract(targetPosition, currentPosition);
var CLOSE_ENOUGH = 0.001;
if (Vec3.length(dPosition) > CLOSE_ENOUGH) {
// compute current velocity in the direction we want to move
var velocityTowardTarget = Vec3.dot(currentVelocity, Vec3.normalize(dPosition));
// compute the speed we would like to be going toward the target position
var SPRING_RATE = 0.35;
var DAMPING_RATE = 0.55;
var desiredVelocity = Vec3.multiply(dPosition, (1.0 / deltaTime) * SPRING_RATE);
// compute how much we want to add to the existing velocity
var addedVelocity = Vec3.subtract(desiredVelocity, velocityTowardTarget);
var newVelocity = Vec3.sum(currentVelocity, addedVelocity);
// Add Damping
newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE));
// Update entity
Entities.editEntity(grabbedEntity, {
velocity: newVelocity
})
}
updateDropLine(currentPosition);
}
}
Controller.mouseMoveEvent.connect(mouseMoveEvent);
Controller.mousePressEvent.connect(mousePressEvent);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.update.connect(update);

View file

@ -13,7 +13,7 @@
var DESC_STRING = '&nbsp;&#x25B4;';
function loaded() {
entityList = new List('entity-list', { valueNames: ['type', 'url']});
entityList = new List('entity-list', { valueNames: ['name', 'type', 'url']});
entityList.clear();
elEntityTable = document.getElementById("entity-table");
elEntityTableBody = document.getElementById("entity-table-body");
@ -22,6 +22,9 @@
elTeleport = document.getElementById("teleport");
elNoEntitiesMessage = document.getElementById("no-entities");
document.getElementById("entity-name").onclick = function() {
setSortColumn('name');
};
document.getElementById("entity-type").onclick = function() {
setSortColumn('type');
};
@ -56,31 +59,34 @@
}));
}
function addEntity(id, type, url) {
function addEntity(id, name, type, url) {
if (entities[id] === undefined) {
var urlParts = url.split('/');
var filename = urlParts[urlParts.length - 1];
entityList.add([{ id: id, type: type, url: filename }], function(items) {
entityList.add([{ id: id, name: name, type: type, url: filename }], function(items) {
var el = items[0].elm;
var id = items[0]._values.id;
entities[id] = {
id: id,
name: id,
name: name,
el: el,
item: items[0],
};
el.setAttribute('id', 'entity_' + id);
el.setAttribute('title', url);
el.dataset.entityId = id;
el.onclick = onRowClicked;
el.ondblclick = onRowDoubleClicked;
el.innerHTML
});
if (refreshEntityListTimer) {
clearTimeout(refreshEntityListTimer);
}
refreshEntityListTimer = setTimeout(refreshEntityListObject, 50);
} else {
var item = entities[id].item;
item.values({ name: name, url: url });
}
}
@ -90,6 +96,7 @@
}
var elSortOrder = {
name: document.querySelector('#entity-name .sort-order'),
type: document.querySelector('#entity-type .sort-order'),
url: document.querySelector('#entity-url .sort-order'),
}
@ -164,7 +171,7 @@
elNoEntitiesMessage.style.display = "none";
for (var i = 0; i < newEntities.length; i++) {
var id = newEntities[i].id;
addEntity(id, newEntities[i].type, newEntities[i].url);
addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url);
}
updateSelectedEntities(data.selectedIDs);
}
@ -190,6 +197,7 @@
<thead>
<tr>
<th id="entity-type" data-sort="type">Type <span class="sort-order" style="display: inline">&nbsp;&#x25BE;</span></th>
<th id="entity-name" data-sort="type">Name <span class="sort-order" style="display: inline">&nbsp;&#x25BE;</span></th>
<th id="entity-url" data-sort="url">URL <span class="sort-order" style="display: none">&nbsp;&#x25BE;</span></th>
</tr>
</thead>
@ -197,6 +205,7 @@
<tr>
<td class="id" style="display: none">Type</td>
<td class="type">Type</td>
<td class="name">Name</td>
<td class="url"><div class='outer'><div class='inner'>URL</div></div></td>
</tr>
</tbody>

View file

@ -95,8 +95,10 @@
};
function loaded() {
var allSections = [];
var elID = document.getElementById("property-id");
var elType = document.getElementById("property-type");
var elName = document.getElementById("property-name");
var elLocked = document.getElementById("property-locked");
var elVisible = document.getElementById("property-visible");
var elPositionX = document.getElementById("property-pos-x");
@ -145,12 +147,13 @@
var elScriptURL = document.getElementById("property-script-url");
var elUserData = document.getElementById("property-user-data");
var elBoxSections = document.querySelectorAll(".box-section");
var elBoxColorRed = document.getElementById("property-box-red");
var elBoxColorGreen = document.getElementById("property-box-green");
var elBoxColorBlue = document.getElementById("property-box-blue");
var elColorSection = document.getElementById("color-section");
var elColorRed = document.getElementById("property-color-red");
var elColorGreen = document.getElementById("property-color-green");
var elColorBlue = document.getElementById("property-color-blue");
var elLightSections = document.querySelectorAll(".light-section");
allSections.push(elLightSections);
var elLightSpotLight = document.getElementById("property-light-spot-light");
var elLightColorRed = document.getElementById("property-light-color-red");
var elLightColorGreen = document.getElementById("property-light-color-green");
@ -161,8 +164,10 @@
var elLightCutoff = document.getElementById("property-light-cutoff");
var elModelSections = document.querySelectorAll(".model-section");
allSections.push(elModelSections);
var elModelURL = document.getElementById("property-model-url");
var elCollisionModelURL = document.getElementById("property-collision-model-url");
var elShapeType = document.getElementById("property-shape-type");
var elCompoundShapeURL = document.getElementById("property-compound-shape-url");
var elModelAnimationURL = document.getElementById("property-model-animation-url");
var elModelAnimationPlaying = document.getElementById("property-model-animation-playing");
var elModelAnimationFPS = document.getElementById("property-model-animation-fps");
@ -170,9 +175,9 @@
var elModelAnimationSettings = document.getElementById("property-model-animation-settings");
var elModelTextures = document.getElementById("property-model-textures");
var elModelOriginalTextures = document.getElementById("property-model-original-textures");
var elModelShapeType = document.getElementById("property-model-shape");
var elTextSections = document.querySelectorAll(".text-section");
allSections.push(elTextSections);
var elTextText = document.getElementById("property-text-text");
var elTextLineHeight = document.getElementById("property-text-line-height");
var elTextTextColorRed = document.getElementById("property-text-text-color-red");
@ -182,6 +187,24 @@
var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green");
var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue");
var elZoneSections = document.querySelectorAll(".zone-section");
allSections.push(elZoneSections);
var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled");
var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red");
var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green");
var elZoneKeyLightColorBlue = document.getElementById("property-zone-key-light-color-blue");
var elZoneKeyLightIntensity = document.getElementById("property-zone-key-intensity");
var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity");
var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x");
var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y");
var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z");
var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude");
var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude");
var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude");
var elZoneStageDay = document.getElementById("property-zone-stage-day");
var elZoneStageHour = document.getElementById("property-zone-stage-hour");
if (window.EventBridge !== undefined) {
EventBridge.scriptEventReceived.connect(function(data) {
data = JSON.parse(data);
@ -239,6 +262,8 @@
enableChildren(document.getElementById("properties-list"), 'input');
}
elName.value = properties.name;
elVisible.checked = properties.visible;
elPositionX.value = properties.position.x.toFixed(2);
@ -282,31 +307,29 @@
elScriptURL.value = properties.script;
elUserData.value = properties.userData;
if (properties.type != "Box") {
for (var i = 0; i < elBoxSections.length; i++) {
elBoxSections[i].style.display = 'none';
for (var i = 0; i < allSections.length; i++) {
for (var j = 0; j < allSections[i].length; j++) {
allSections[i][j].style.display = 'none';
}
} else {
for (var i = 0; i < elBoxSections.length; i++) {
elBoxSections[i].style.display = 'block';
}
elBoxColorRed.value = properties.color.red;
elBoxColorGreen.value = properties.color.green;
elBoxColorBlue.value = properties.color.blue;
}
if (properties.type != "Model") {
for (var i = 0; i < elModelSections.length; i++) {
elModelSections[i].style.display = 'none';
}
if (properties.type == "Box" || properties.type == "Sphere") {
elColorSection.style.display = 'block';
elColorRed.value = properties.color.red;
elColorGreen.value = properties.color.green;
elColorBlue.value = properties.color.blue;
} else {
elColorSection.style.display = 'none';
}
if (properties.type == "Model") {
for (var i = 0; i < elModelSections.length; i++) {
elModelSections[i].style.display = 'block';
}
elModelURL.value = properties.modelURL;
elCollisionModelURL.value = properties.collisionModelURL;
elShapeType.value = properties.shapeType;
elCompoundShapeURL.value = properties.compoundShapeURL;
elModelAnimationURL.value = properties.animationURL;
elModelAnimationPlaying.checked = properties.animationIsPlaying;
elModelAnimationFPS.value = properties.animationFPS;
@ -314,14 +337,7 @@
elModelAnimationSettings.value = properties.animationSettings;
elModelTextures.value = properties.textures;
elModelOriginalTextures.value = properties.originalTextures;
elModelShapeType.value = properties.shapeType;
}
if (properties.type != "Text") {
for (var i = 0; i < elTextSections.length; i++) {
elTextSections[i].style.display = 'none';
}
} else {
} else if (properties.type == "Text") {
for (var i = 0; i < elTextSections.length; i++) {
elTextSections[i].style.display = 'block';
}
@ -334,13 +350,7 @@
elTextBackgroundColorRed.value = properties.backgroundColor.red;
elTextBackgroundColorGreen.value = properties.backgroundColor.green;
elTextBackgroundColorBlue.value = properties.backgroundColor.blue;
}
if (properties.type != "Light") {
for (var i = 0; i < elLightSections.length; i++) {
elLightSections[i].style.display = 'none';
}
} else {
} else if (properties.type == "Light") {
for (var i = 0; i < elLightSections.length; i++) {
elLightSections[i].style.display = 'block';
}
@ -354,6 +364,28 @@
elLightIntensity.value = properties.intensity;
elLightExponent.value = properties.exponent;
elLightCutoff.value = properties.cutoff;
} else if (properties.type == "Zone") {
for (var i = 0; i < elZoneSections.length; i++) {
elZoneSections[i].style.display = 'block';
}
elZoneStageSunModelEnabled.checked = properties.stageSunModelEnabled;
elZoneKeyLightColorRed.value = properties.keyLightColor.red;
elZoneKeyLightColorGreen.value = properties.keyLightColor.green;
elZoneKeyLightColorBlue.value = properties.keyLightColor.blue;
elZoneKeyLightIntensity.value = properties.keyLightIntensity.toFixed(2);
elZoneKeyLightAmbientIntensity.value = properties.keyLightAmbientIntensity.toFixed(2);
elZoneKeyLightDirectionX.value = properties.keyLightDirection.x.toFixed(2);
elZoneKeyLightDirectionY.value = properties.keyLightDirection.y.toFixed(2);
elZoneKeyLightDirectionZ.value = properties.keyLightDirection.z.toFixed(2);
elZoneStageLatitude.value = properties.stageLatitude.toFixed(2);
elZoneStageLongitude.value = properties.stageLongitude.toFixed(2);
elZoneStageAltitude.value = properties.stageAltitude.toFixed(2);
elZoneStageDay.value = properties.stageDay;
elZoneStageHour.value = properties.stageHour;
elShapeType.value = properties.shapeType;
elCompoundShapeURL.value = properties.compoundShapeURL;
}
if (selected) {
@ -366,6 +398,7 @@
}
elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked'));
elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name'));
elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible'));
var positionChangeFunction = createEmitVec3PropertyUpdateFunction(
@ -425,11 +458,11 @@
elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script'));
elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData'));
var boxColorChangeFunction = createEmitColorPropertyUpdateFunction(
'color', elBoxColorRed, elBoxColorGreen, elBoxColorBlue);
elBoxColorRed.addEventListener('change', boxColorChangeFunction);
elBoxColorGreen.addEventListener('change', boxColorChangeFunction);
elBoxColorBlue.addEventListener('change', boxColorChangeFunction);
var colorChangeFunction = createEmitColorPropertyUpdateFunction(
'color', elColorRed, elColorGreen, elColorBlue);
elColorRed.addEventListener('change', colorChangeFunction);
elColorGreen.addEventListener('change', colorChangeFunction);
elColorBlue.addEventListener('change', colorChangeFunction);
elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight'));
@ -444,14 +477,14 @@
elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff'));
elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
elCollisionModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionModelURL'));
elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL'));
elModelAnimationURL.addEventListener('change', createEmitTextPropertyUpdateFunction('animationURL'));
elModelAnimationPlaying.addEventListener('change', createEmitCheckedPropertyUpdateFunction('animationIsPlaying'));
elModelAnimationFPS.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFPS'));
elModelAnimationFrame.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFrameIndex'));
elModelAnimationSettings.addEventListener('change', createEmitTextPropertyUpdateFunction('animationSettings'));
elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));
elModelShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType'));
elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text'));
elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight'));
@ -468,6 +501,26 @@
elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction);
elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
elZoneStageSunModelEnabled.addEventListener('change', createEmitCheckedPropertyUpdateFunction('stageSunModelEnabled'));
var zoneKeyLightColorChangeFunction = createEmitColorPropertyUpdateFunction(
'keyLightColor', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue);
elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('keyLightIntensity'));
elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('keyLightAmbientIntensity'));
var zoneKeyLightDirectionChangeFunction = createEmitVec3PropertyUpdateFunction(
'keyLightDirection', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY, elZoneKeyLightDirectionZ);
elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneKeyLightDirectionZ.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneStageLatitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLatitude'));
elZoneStageLongitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLongitude'));
elZoneStageAltitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageAltitude'));
elZoneStageDay.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageDay'));
elZoneStageHour.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageHour'));
elMoveSelectionToGrid.addEventListener("click", function() {
EventBridge.emitWebEvent(JSON.stringify({
type: "action",
@ -508,18 +561,22 @@
// To make this work we block the first mouseup event after the elements
// received focus. If we block all mouseup events the user will not
// be able to click within the selected text.
// We also check to see if the value has changed to make sure we aren't
// blocking a mouse-up event when clicking on an input spinner.
var els = document.querySelectorAll("input, textarea");
for (var i = 0; i < els.length; i++) {
var clicked = false;
var originalText;
els[i].onfocus = function() {
originalText = this.value;
this.select();
clicked = false;
};
els[i].onmouseup = function(e) {
if (!clicked) {
if (!clicked && originalText == this.value) {
e.preventDefault();
clicked = true;
}
clicked = true;
};
}
}
@ -537,6 +594,12 @@
<label id="property-id" class="selectable"></label>
</div>
</div>
<div class="property">
<span class="label" style="float: left; margin-right: 6px">Name</span>
<div class="value" style="overflow: hidden;">
<input type="text" id="property-name"></input>
</div>
</div>
<div class="property">
<span class="label">Locked</span>
<span class="value">
@ -690,12 +753,12 @@
</div>
<div class="box-section property">
<div id="color-section" class="property">
<div class="label">Color</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-box-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-box-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-box-blue"></input></div>
<div class="input-area">R <input class="coord" type='number' id="property-color-red"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-color-green"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-color-blue"></input></div>
</div>
</div>
@ -707,10 +770,21 @@
<input type="text" id="property-model-url" class="url"></input>
</div>
</div>
<div class="model-section property">
<div class="label">Collision Model URL</div>
<div class="model-section zone-section property">
<div class="label">Shape Type</div>
<div class="value">
<input type="text" id="property-collision-model-url" class="url"></input>
<select name="SelectShapeType" id="property-shape-type" name="SelectShapeType">
<option value='none'>none</option>
<option value='box'>box</option>
<option value='sphere'>sphere</option>
<option value='compound'>compound</option>
</select>
</div>
</div>
<div class="model-section zone-section property">
<div class="label">Compound Shape URL</div>
<div class="value">
<input type="text" id="property-compound-shape-url" class="url"></input>
</div>
</div>
<div class="model-section property">
@ -756,18 +830,6 @@
</div>
</div>
<div class="model-section property">
<div class="label">Shape Type</div>
<div class="value">
<select name="SelectShapeType" id="property-model-shape" name="SelectShapeType">
<option value='none'>none</option>
<option value='box'>box</option>
<option value='sphere'>sphere</option>
</select>
</div>
</div>
<div class="text-section property">
<div class="label">Text</div>
<div class="value">
@ -829,6 +891,73 @@
<input class="coord" type='number' id="property-light-cutoff"></input>
</div>
</div>
<div class="zone-section property">
<span class="label">Stage Sun Model Enabled</span>
<span class="value">
<input type='checkbox' id="property-zone-stage-sun-model-enabled">
</span>
</div>
<div class="zone-section property">
<div class="label">Key Light Color</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-zone-key-light-color-red" min="0" max="255" step="1"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-zone-key-light-color-green" min="0" max="255" step="1"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-zone-key-light-color-blue" min="0" max="255" step="1"></input></div>
</div>
</div>
<div class="zone-section property">
<div class="label">Key Light Intensity</div>
<div class="value">
<input class="coord" type='number' id="property-zone-key-intensity" min="0" max="10" step="0.1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Key Light Ambient Intensity</div>
<div class="value">
<input class="coord" type='number' id="property-zone-key-ambient-intensity" min="0" max="10" step="0.1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Key Light Direction</div>
<div class="value">
<div class="input-area">Pitch <input class="coord" type='number' id="property-zone-key-light-direction-x"></input></div>
<div class="input-area">Yaw <input class="coord" type='number' id="property-zone-key-light-direction-y"></input></div>
<div class="input-area">Roll <input class="coord" type='number' id="property-zone-key-light-direction-z"></input></div>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Latitude</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-latitude" min="-90" max="90" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Longitude</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-longitude" min="-180" max="180" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Altitude</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-altitude" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Day</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-day" min="0" max="365" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Hour</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-hour" min="0" max="24" step="0.5"></input>
</div>
</div>
</div>
</body>

View file

@ -301,3 +301,7 @@ input[type="number"]::-webkit-inner-spin-button:hover,
input[type="number"]::-webkit-inner-spin-button:active{
opacity: .8;
}
input#property-name {
width: 100%;
}

View file

@ -53,7 +53,8 @@ function Tooltip() {
text += "ID: " + properties.id + "\n"
if (properties.type == "Model") {
text += "Model URL: " + properties.modelURL + "\n"
text += "Collision Model URL: " + properties.collisionModelURL + "\n"
text += "Shape Type: " + properties.shapeType + "\n"
text += "Compound Shape URL: " + properties.compoundShapeURL + "\n"
text += "Animation URL: " + properties.animationURL + "\n"
text += "Animation is playing: " + properties.animationIsPlaying + "\n"
if (properties.sittingPoints && properties.sittingPoints.length > 0) {

View file

@ -31,7 +31,7 @@ EntityListTool = function(opts) {
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
});
function sendUpdate() {
that.sendUpdate = function() {
var entities = [];
var ids = Entities.findEntities(MyAvatar.position, 100);
for (var i = 0; i < ids.length; i++) {
@ -39,6 +39,7 @@ EntityListTool = function(opts) {
var properties = Entities.getEntityProperties(id);
entities.push({
id: id.id,
name: properties.name,
type: properties.type,
url: properties.type == "Model" ? properties.modelURL : "",
});
@ -76,7 +77,7 @@ EntityListTool = function(opts) {
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
}
} else if (data.type == "refresh") {
sendUpdate();
that.sendUpdate();
} else if (data.type == "teleport") {
if (selectionManager.hasSelection()) {
MyAvatar.position = selectionManager.worldPosition;

View file

@ -52,7 +52,9 @@ EntityPropertyDialogBox = (function () {
if (properties.type == "Model") {
array.push({ label: "Model URL:", value: properties.modelURL });
index++;
array.push({ label: "Collision Model URL:", value: properties.collisionModelURL });
array.push({ label: "Shape Type:", value: properties.shapeType });
index++;
array.push({ label: "Compound Shape URL:", value: properties.compoundShapeURL });
index++;
array.push({ label: "Animation URL:", value: properties.animationURL });
index++;
@ -284,7 +286,8 @@ EntityPropertyDialogBox = (function () {
properties.locked = array[index++].value;
if (properties.type == "Model") {
properties.modelURL = array[index++].value;
properties.collisionModelURL = array[index++].value;
properties.shapeType = array[index++].value;
properties.compoundShapeURL = array[index++].value;
properties.animationURL = array[index++].value;
var newAnimationIsPlaying = array[index++].value;

View file

@ -122,7 +122,7 @@ LightOverlayManager = function() {
Entities.clearingEntities.connect(clearEntities);
// Add existing entities
var ids = Entities.findEntities(MyAvatar.position, 100);
var ids = Entities.findEntities(MyAvatar.position, 64000);
for (var i = 0; i < ids.length; i++) {
addEntity(ids[i]);
}

View file

@ -0,0 +1,146 @@
ZoneOverlayManager = function(isEntityFunc, entityAddedFunc, entityRemovedFunc, entityMovedFunc) {
var self = this;
var visible = false;
// List of all created overlays
var allOverlays = [];
// List of overlays not currently being used
var unusedOverlays = [];
// Map from EntityItemID.id to overlay id
var entityOverlays = {};
// Map from EntityItemID.id to EntityItemID object
var entityIDs = {};
this.updatePositions = function(ids) {
for (var id in entityIDs) {
var entityID = entityIDs[id];
var properties = Entities.getEntityProperties(entityID);
Overlays.editOverlay(entityOverlays[entityID.id].solid, {
position: properties.position,
rotation: properties.rotation,
dimensions: properties.dimensions,
});
Overlays.editOverlay(entityOverlays[entityID.id].outline, {
position: properties.position,
rotation: properties.rotation,
dimensions: properties.dimensions,
});
}
};
this.setVisible = function(isVisible) {
if (visible != isVisible) {
visible = isVisible;
for (var id in entityOverlays) {
Overlays.editOverlay(entityOverlays[id].solid, { visible: visible });
Overlays.editOverlay(entityOverlays[id].outline, { visible: visible });
}
}
};
// Allocate or get an unused overlay
function getOverlay() {
if (unusedOverlays.length == 0) {
var overlay = Overlays.addOverlay("cube", {
});
allOverlays.push(overlay);
} else {
var overlay = unusedOverlays.pop();
};
return overlay;
}
function releaseOverlay(overlay) {
unusedOverlays.push(overlay);
Overlays.editOverlay(overlay, { visible: false });
}
function addEntity(entityID) {
var properties = Entities.getEntityProperties(entityID);
if (properties.type == "Zone" && !(entityID.id in entityOverlays)) {
var overlaySolid = getOverlay();
var overlayOutline = getOverlay();
entityOverlays[entityID.id] = {
solid: overlaySolid,
outline: overlayOutline,
}
entityIDs[entityID.id] = entityID;
var color = {
red: Math.round(Math.random() * 255),
green: Math.round(Math.random() * 255),
blue: Math.round(Math.random() * 255)
};
Overlays.editOverlay(overlaySolid, {
position: properties.position,
rotation: properties.rotation,
dimensions: properties.dimensions,
visible: visible,
solid: true,
alpha: 0.1,
color: color,
ignoreRayIntersection: true,
});
Overlays.editOverlay(overlayOutline, {
position: properties.position,
rotation: properties.rotation,
dimensions: properties.dimensions,
visible: visible,
solid: false,
dashed: false,
lineWidth: 2.0,
alpha: 1.0,
color: color,
ignoreRayIntersection: true,
});
}
}
function deleteEntity(entityID) {
if (entityID.id in entityOverlays) {
releaseOverlay(entityOverlays[entityID.id].outline);
releaseOverlay(entityOverlays[entityID.id].solid);
delete entityIDs[entityID.id];
delete entityOverlays[entityID.id];
}
}
function changeEntityID(oldEntityID, newEntityID) {
entityOverlays[newEntityID.id] = entityOverlays[oldEntityID.id];
entityIDs[newEntityID.id] = newEntityID;
delete entityOverlays[oldEntityID.id];
delete entityIDs[oldEntityID.id];
}
function clearEntities() {
for (var id in entityOverlays) {
releaseOverlay(entityOverlays[id].outline);
releaseOverlay(entityOverlays[id].solid);
}
entityOverlays = {};
entityIDs = {};
}
Entities.addingEntity.connect(addEntity);
Entities.changingEntityID.connect(changeEntityID);
Entities.deletingEntity.connect(deleteEntity);
Entities.clearingEntities.connect(clearEntities);
// Add existing entities
var ids = Entities.findEntities(MyAvatar.position, 64000);
for (var i = 0; i < ids.length; i++) {
addEntity(ids[i]);
}
Script.scriptEnding.connect(function() {
for (var i = 0; i < allOverlays.length; i++) {
Overlays.deleteOverlay(allOverlays[i]);
}
});
};

View file

@ -31,7 +31,9 @@ var yawFromTouch = 0;
var pitchFromTouch = 0;
// Touch Data
var TIME_BEFORE_GENERATED_END_TOUCH_EVENT = 50; // ms
var startedTouching = false;
var lastTouchEvent = 0;
var lastMouseX = 0;
var lastMouseY = 0;
var yawFromMouse = 0;
@ -80,11 +82,17 @@ function touchBeginEvent(event) {
yawFromTouch = 0;
pitchFromTouch = 0;
startedTouching = true;
var d = new Date();
lastTouchEvent = d.getTime();
}
function touchEndEvent(event) {
if (wantDebugging) {
print("touchEndEvent event.x,y=" + event.x + ", " + event.y);
if (event) {
print("touchEndEvent event.x,y=" + event.x + ", " + event.y);
} else {
print("touchEndEvent generated");
}
}
startedTouching = false;
}
@ -96,16 +104,17 @@ function touchUpdateEvent(event) {
}
if (!startedTouching) {
// handle Qt 5.4.x bug where we get touch update without a touch begin event
startedTouching = true;
lastTouchX = event.x;
lastTouchY = event.y;
// handle Qt 5.4.x bug where we get touch update without a touch begin event
touchBeginEvent(event);
return;
}
yawFromTouch += ((event.x - lastTouchX) * TOUCH_YAW_SCALE * FIXED_TOUCH_TIMESTEP);
pitchFromTouch += ((event.y - lastTouchY) * TOUCH_PITCH_SCALE * FIXED_TOUCH_TIMESTEP);
lastTouchX = event.x;
lastTouchY = event.y;
var d = new Date();
lastTouchEvent = d.getTime();
}
@ -113,6 +122,14 @@ function update(deltaTime) {
if (wantDebugging) {
print("update()...");
}
if (startedTouching) {
var d = new Date();
var sinceLastTouch = d.getTime() - lastTouchEvent;
if (sinceLastTouch > TIME_BEFORE_GENERATED_END_TOUCH_EVENT) {
touchEndEvent();
}
}
if (yawFromTouch != 0 || yawFromMouse != 0) {
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollRadians(0, yawFromTouch + yawFromMouse, 0));

63
examples/lotsoBlocks.js Normal file
View file

@ -0,0 +1,63 @@
var NUM_BLOCKS = 200;
var size;
var SPAWN_RANGE = 10;
var boxes = [];
var basePosition, avatarRot;
var isAssignmentScript = false;
if(isAssignmentScript){
basePosition = {x: 8000, y: 8000, z: 8000};
}
else {
avatarRot = Quat.fromPitchYawRollDegrees(0, MyAvatar.bodyYaw, 0.0);
basePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(SPAWN_RANGE * 3, Quat.getFront(avatarRot)));
}
basePosition.y -= SPAWN_RANGE;
var ground = Entities.addEntity({
type: "Model",
modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/woodFloor.fbx",
dimensions: {
x: 100,
y: 2,
z: 100
},
position: basePosition,
shapeType: 'box'
});
basePosition.y += SPAWN_RANGE + 2;
for (var i = 0; i < NUM_BLOCKS; i++) {
size = randFloat(-.2, 0.7);
boxes.push(Entities.addEntity({
type: 'Box',
dimensions: {
x: size,
y: size,
z: size
},
position: {
x: basePosition.x + randFloat(-SPAWN_RANGE, SPAWN_RANGE),
y: basePosition.y - randFloat(-SPAWN_RANGE, SPAWN_RANGE),
z: basePosition.z + randFloat(-SPAWN_RANGE, SPAWN_RANGE)
},
color: {red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255},
collisionsWillMove: true,
gravity: {x: 0, y: 0, z: 0}
}));
}
function cleanup() {
Entities.deleteEntity(ground);
boxes.forEach(function(box){
Entities.deleteEntity(box);
});
}
Script.scriptEnding.connect(cleanup);
function randFloat(low, high) {
return low + Math.random() * ( high - low );
}

View file

@ -12,6 +12,129 @@
// The Slider class
Slider = function(x,y,width,thumbSize) {
this.background = Overlays.addOverlay("text", {
backgroundColor: { red: 125, green: 125, blue: 255 },
x: x,
y: y,
width: width,
height: thumbSize,
alpha: 1.0,
backgroundAlpha: 0.5,
visible: true
});
this.thumb = Overlays.addOverlay("text", {
backgroundColor: { red: 255, green: 255, blue: 255 },
x: x,
y: y,
width: thumbSize,
height: thumbSize,
alpha: 1.0,
backgroundAlpha: 1.0,
visible: true
});
this.thumbSize = thumbSize;
this.thumbHalfSize = 0.5 * thumbSize;
this.minThumbX = x + this.thumbHalfSize;
this.maxThumbX = x + width - this.thumbHalfSize;
this.thumbX = this.minThumbX;
this.minValue = 0.0;
this.maxValue = 1.0;
this.clickOffsetX = 0;
this.isMoving = false;
this.updateThumb = function() {
thumbTruePos = this.thumbX - 0.5 * this.thumbSize;
Overlays.editOverlay(this.thumb, { x: thumbTruePos } );
};
this.isClickableOverlayItem = function(item) {
return (item == this.thumb) || (item == this.background);
};
this.onMouseMoveEvent = function(event) {
if (this.isMoving) {
newThumbX = event.x - this.clickOffsetX;
if (newThumbX < this.minThumbX) {
newThumbX = this.minThumbX;
}
if (newThumbX > this.maxThumbX) {
newThumbX = this.maxThumbX;
}
this.thumbX = newThumbX;
this.updateThumb();
this.onValueChanged(this.getValue());
}
};
this.onMousePressEvent = function(event, clickedOverlay) {
if (!this.isClickableOverlayItem(clickedOverlay)) {
this.isMoving = false;
return;
}
this.isMoving = true;
var clickOffset = event.x - this.thumbX;
if ((clickOffset > -this.thumbHalfSize) && (clickOffset < this.thumbHalfSize)) {
this.clickOffsetX = clickOffset;
} else {
this.clickOffsetX = 0;
this.thumbX = event.x;
this.updateThumb();
this.onValueChanged(this.getValue());
}
};
this.onMouseReleaseEvent = function(event) {
this.isMoving = false;
};
// Public members:
this.setNormalizedValue = function(value) {
if (value < 0.0) {
this.thumbX = this.minThumbX;
} else if (value > 1.0) {
this.thumbX = this.maxThumbX;
} else {
this.thumbX = value * (this.maxThumbX - this.minThumbX) + this.minThumbX;
}
this.updateThumb();
};
this.getNormalizedValue = function() {
return (this.thumbX - this.minThumbX) / (this.maxThumbX - this.minThumbX);
};
this.setValue = function(value) {
var normValue = (value - this.minValue) / (this.maxValue - this.minValue);
this.setNormalizedValue(normValue);
};
this.getValue = function() {
return this.getNormalizedValue() * (this.maxValue - this.minValue) + this.minValue;
};
this.onValueChanged = function(value) {};
this.destroy = function() {
Overlays.deleteOverlay(this.background);
Overlays.deleteOverlay(this.thumb);
};
this.setThumbColor = function(color) {
Overlays.editOverlay(this.thumb, {backgroundColor: { red: color.x*255, green: color.y*255, blue: color.z*255 }});
};
this.setBackgroundColor = function(color) {
Overlays.editOverlay(this.background, {backgroundColor: { red: color.x*255, green: color.y*255, blue: color.z*255 }});
};
}
// The Checkbox class
Checkbox = function(x,y,width,thumbSize) {
this.thumb = Overlays.addOverlay("text", {
backgroundColor: { red: 255, green: 255, blue: 255 },
@ -52,6 +175,10 @@ Slider = function(x,y,width,thumbSize) {
Overlays.editOverlay(this.thumb, { x: thumbTruePos } );
};
this.isClickableOverlayItem = function(item) {
return item == this.background;
};
this.onMouseMoveEvent = function(event) {
if (this.isMoving) {
newThumbX = event.x - this.clickOffsetX;
@ -67,7 +194,11 @@ Slider = function(x,y,width,thumbSize) {
}
};
this.onMousePressEvent = function(event) {
this.onMousePressEvent = function(event, clickedOverlay) {
if (this.background != clickedOverlay) {
this.isMoving = false;
return;
}
this.isMoving = true;
var clickOffset = event.x - this.thumbX;
if ((clickOffset > -this.thumbHalfSize) && (clickOffset < this.thumbHalfSize)) {
@ -118,6 +249,167 @@ Slider = function(x,y,width,thumbSize) {
};
}
// The ColorBox class
ColorBox = function(x,y,width,thumbSize) {
var self = this;
var slideHeight = thumbSize / 3;
var sliderWidth = width;
this.red = new Slider(x, y, width, slideHeight);
this.green = new Slider(x, y + slideHeight, width, slideHeight);
this.blue = new Slider(x, y + 2 * slideHeight, width, slideHeight);
this.red.setBackgroundColor({x: 1, y: 0, z: 0});
this.green.setBackgroundColor({x: 0, y: 1, z: 0});
this.blue.setBackgroundColor({x: 0, y: 0, z: 1});
this.isClickableOverlayItem = function(item) {
return this.red.isClickableOverlayItem(item)
|| this.green.isClickableOverlayItem(item)
|| this.blue.isClickableOverlayItem(item);
};
this.onMouseMoveEvent = function(event) {
this.red.onMouseMoveEvent(event);
this.green.onMouseMoveEvent(event);
this.blue.onMouseMoveEvent(event);
};
this.onMousePressEvent = function(event, clickedOverlay) {
this.red.onMousePressEvent(event, clickedOverlay);
if (this.red.isMoving) {
return;
}
this.green.onMousePressEvent(event, clickedOverlay);
if (this.green.isMoving) {
return;
}
this.blue.onMousePressEvent(event, clickedOverlay);
};
this.onMouseReleaseEvent = function(event) {
this.red.onMouseReleaseEvent(event);
this.green.onMouseReleaseEvent(event);
this.blue.onMouseReleaseEvent(event);
};
this.setterFromWidget = function(value) {
var color = self.getValue();
self.onValueChanged(color);
self.updateRGBSliders(color);
};
this.red.onValueChanged = this.setterFromWidget;
this.green.onValueChanged = this.setterFromWidget;
this.blue.onValueChanged = this.setterFromWidget;
this.updateRGBSliders = function(color) {
this.red.setThumbColor({x: color.x, y: 0, z: 0});
this.green.setThumbColor({x: 0, y: color.y, z: 0});
this.blue.setThumbColor({x: 0, y: 0, z: color.z});
};
// Public members:
this.setValue = function(value) {
this.red.setValue(value.x);
this.green.setValue(value.y);
this.blue.setValue(value.z);
this.updateRGBSliders(value);
};
this.getValue = function() {
var value = {x:this.red.getValue(), y:this.green.getValue(),z:this.blue.getValue()};
return value;
};
this.destroy = function() {
this.red.destroy();
this.green.destroy();
this.blue.destroy();
};
this.onValueChanged = function(value) {};
}
// The DirectionBox class
DirectionBox = function(x,y,width,thumbSize) {
var self = this;
var slideHeight = thumbSize / 2;
var sliderWidth = width;
this.yaw = new Slider(x, y, width, slideHeight);
this.pitch = new Slider(x, y + slideHeight, width, slideHeight);
this.yaw.setThumbColor({x: 1, y: 0, z: 0});
this.yaw.minValue = -180;
this.yaw.maxValue = +180;
this.pitch.setThumbColor({x: 0, y: 0, z: 1});
this.pitch.minValue = -1;
this.pitch.maxValue = +1;
this.isClickableOverlayItem = function(item) {
return this.yaw.isClickableOverlayItem(item)
|| this.pitch.isClickableOverlayItem(item);
};
this.onMouseMoveEvent = function(event) {
this.yaw.onMouseMoveEvent(event);
this.pitch.onMouseMoveEvent(event);
};
this.onMousePressEvent = function(event, clickedOverlay) {
this.yaw.onMousePressEvent(event, clickedOverlay);
if (this.yaw.isMoving) {
return;
}
this.pitch.onMousePressEvent(event, clickedOverlay);
};
this.onMouseReleaseEvent = function(event) {
this.yaw.onMouseReleaseEvent(event);
this.pitch.onMouseReleaseEvent(event);
};
this.setterFromWidget = function(value) {
var yawPitch = self.getValue();
self.onValueChanged(yawPitch);
};
this.yaw.onValueChanged = this.setterFromWidget;
this.pitch.onValueChanged = this.setterFromWidget;
// Public members:
this.setValue = function(direction) {
var flatXZ = Math.sqrt(direction.x * direction.x + direction.z * direction.z);
if (flatXZ > 0.0) {
var flatX = direction.x / flatXZ;
var flatZ = direction.z / flatXZ;
var yaw = Math.acos(flatX) * 180 / Math.PI;
if (flatZ < 0) {
yaw = -yaw;
}
this.yaw.setValue(yaw);
}
this.pitch.setValue(direction.y);
};
this.getValue = function() {
var dirZ = this.pitch.getValue();
var yaw = this.yaw.getValue() * Math.PI / 180;
var cosY = Math.sqrt(1 - dirZ*dirZ);
var value = {x:cosY * Math.cos(yaw), y:dirZ, z: cosY * Math.sin(yaw)};
return value;
};
this.destroy = function() {
this.yaw.destroy();
this.pitch.destroy();
};
this.onValueChanged = function(value) {};
}
var textFontSize = 16;
@ -167,7 +459,12 @@ function PanelItem(name, setter, getter, displayer, x, y, textWidth, valueWidth,
};
this.setterFromWidget = function(value) {
setter(value);
Overlays.editOverlay(this.value, {text: this.displayer(getter())});
// ANd loop back the value after the final setter has been called
var value = getter();
if (this.widget) {
this.widget.setValue(value);
}
Overlays.editOverlay(this.value, {text: this.displayer(value)});
};
@ -219,9 +516,9 @@ Panel = function(x, y) {
for (var i in this.items) {
var widget = this.items[i].widget;
if (clickedOverlay == widget.background) {
if (widget.isClickableOverlayItem(clickedOverlay)) {
this.activeWidget = widget;
this.activeWidget.onMousePressEvent(event);
this.activeWidget.onMousePressEvent(event, clickedOverlay);
// print("clicked... widget=" + i);
break;
}
@ -237,21 +534,63 @@ Panel = function(x, y) {
this.newSlider = function(name, minValue, maxValue, setValue, getValue, displayValue) {
var sliderItem = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
var slider = new Slider(this.widgetX, this.nextY, widgetWidth, rawHeight);
slider.minValue = minValue;
slider.maxValue = maxValue;
slider.onValueChanged = function(value) { sliderItem.setterFromWidget(value); };
sliderItem.widget = slider;
sliderItem.setter(getValue());
this.items[name] = sliderItem;
item.widget = slider;
item.widget.onValueChanged = function(value) { item.setterFromWidget(value); };
item.setter(getValue());
this.items[name] = item;
this.nextY += rawYDelta;
// print("created Item... slider=" + name);
};
this.newCheckbox = function(name, setValue, getValue, displayValue) {
var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
var checkbox = new Checkbox(this.widgetX, this.nextY, widgetWidth, rawHeight);
item.widget = checkbox;
item.widget.onValueChanged = function(value) { item.setterFromWidget(value); };
item.setter(getValue());
this.items[name] = item;
this.nextY += rawYDelta;
// print("created Item... slider=" + name);
};
this.newColorBox = function(name, setValue, getValue, displayValue) {
var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
var colorBox = new ColorBox(this.widgetX, this.nextY, widgetWidth, rawHeight);
item.widget = colorBox;
item.widget.onValueChanged = function(value) { item.setterFromWidget(value); };
item.setter(getValue());
this.items[name] = item;
this.nextY += rawYDelta;
// print("created Item... colorBox=" + name);
};
this.newDirectionBox= function(name, setValue, getValue, displayValue) {
var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight);
var directionBox = new DirectionBox(this.widgetX, this.nextY, widgetWidth, rawHeight);
item.widget = directionBox;
item.widget.onValueChanged = function(value) { item.setterFromWidget(value); };
item.setter(getValue());
this.items[name] = item;
this.nextY += rawYDelta;
// print("created Item... directionBox=" + name);
};
this.destroy = function() {
for (var i in this.items) {
this.items[i].destroy();
@ -273,6 +612,15 @@ Panel = function(x, y) {
}
return null;
}
this.update = function(name) {
var item = this.items[name];
if (item != null) {
return item.setter(item.getter());
}
return null;
}
};

View file

@ -12,17 +12,29 @@
//
var createdRenderMenu = false;
var createdGeneratedAudioMenu = false;
var createdStereoInputMenuItem = false;
var ENTITIES_MENU = "Developer > Entities";
var DEVELOPER_MENU = "Developer";
var ENTITIES_MENU = DEVELOPER_MENU + " > Entities";
var COLLISION_UPDATES_TO_SERVER = "Don't send collision updates to server";
var RENDER_MENU = "Developer > Render";
var RENDER_MENU = DEVELOPER_MENU + " > Render";
var ENTITIES_ITEM = "Entities";
var AVATARS_ITEM = "Avatars";
var AUDIO_MENU = DEVELOPER_MENU + " > Audio";
var AUDIO_SOURCE_INJECT = "Generated Audio";
var AUDIO_SOURCE_MENU = AUDIO_MENU + " > Generated Audio Source";
var AUDIO_SOURCE_PINK_NOISE = "Pink Noise";
var AUDIO_SOURCE_SINE_440 = "Sine 440hz";
var AUDIO_STEREO_INPUT = "Stereo Input";
function setupMenus() {
if (!Menu.menuExists("Developer")) {
Menu.addMenu("Developer");
if (!Menu.menuExists(DEVELOPER_MENU)) {
Menu.addMenu(DEVELOPER_MENU);
}
if (!Menu.menuExists(ENTITIES_MENU)) {
Menu.addMenu(ENTITIES_MENU);
@ -54,6 +66,24 @@ function setupMenus() {
if (!Menu.menuItemExists(RENDER_MENU, AVATARS_ITEM)) {
Menu.addMenuItem({ menuName: RENDER_MENU, menuItemName: AVATARS_ITEM, isCheckable: true, isChecked: Scene.shouldRenderAvatars })
}
if (!Menu.menuExists(AUDIO_MENU)) {
Menu.addMenu(AUDIO_MENU);
}
if (!Menu.menuItemExists(AUDIO_MENU, AUDIO_SOURCE_INJECT)) {
Menu.addMenuItem({ menuName: AUDIO_MENU, menuItemName: AUDIO_SOURCE_INJECT, isCheckable: true, isChecked: false });
Menu.addMenu(AUDIO_SOURCE_MENU);
Menu.addMenuItem({ menuName: AUDIO_SOURCE_MENU, menuItemName: AUDIO_SOURCE_PINK_NOISE, isCheckable: true, isChecked: false });
Menu.addMenuItem({ menuName: AUDIO_SOURCE_MENU, menuItemName: AUDIO_SOURCE_SINE_440, isCheckable: true, isChecked: false });
Menu.setIsOptionChecked(AUDIO_SOURCE_PINK_NOISE, true);
Audio.selectPinkNoise();
createdGeneratedAudioMenu = true;
}
if (!Menu.menuItemExists(AUDIO_MENU, AUDIO_STEREO_INPUT)) {
Menu.addMenuItem({ menuName: AUDIO_MENU, menuItemName: AUDIO_STEREO_INPUT, isCheckable: true, isChecked: false });
createdStereoInputMenuItem = true;
}
}
Menu.menuItemEvent.connect(function (menuItem) {
@ -67,7 +97,17 @@ Menu.menuItemEvent.connect(function (menuItem) {
Scene.shouldRenderEntities = Menu.isOptionChecked(ENTITIES_ITEM);
} else if (menuItem == AVATARS_ITEM) {
Scene.shouldRenderAvatars = Menu.isOptionChecked(AVATARS_ITEM);
}
} else if (menuItem == AUDIO_SOURCE_INJECT && !createdGeneratedAudioMenu) {
Audio.injectGeneratedNoise(Menu.isOptionChecked(AUDIO_SOURCE_INJECT));
} else if (menuItem == AUDIO_SOURCE_PINK_NOISE && !createdGeneratedAudioMenu) {
Audio.selectPinkNoise();
Menu.setIsOptionChecked(AUDIO_SOURCE_SINE_440, false);
} else if (menuItem == AUDIO_SOURCE_SINE_440 && !createdGeneratedAudioMenu) {
Audio.selectSine440();
Menu.setIsOptionChecked(AUDIO_SOURCE_PINK_NOISE, false);
} else if (menuItem == AUDIO_STEREO_INPUT) {
Audio.setStereoInput(Menu.isOptionChecked(AUDIO_STEREO_INPUT))
}
});
Scene.shouldRenderAvatarsChanged.connect(function(shouldRenderAvatars) {
@ -87,6 +127,16 @@ function scriptEnding() {
Menu.removeMenuItem(RENDER_MENU, ENTITIES_ITEM);
Menu.removeMenuItem(RENDER_MENU, AVATARS_ITEM);
}
if (createdGeneratedAudioMenu) {
Audio.injectGeneratedNoise(false);
Menu.removeMenuItem(AUDIO_MENU, AUDIO_SOURCE_INJECT);
Menu.removeMenu(AUDIO_SOURCE_MENU);
}
if (createdStereoInputMenuItem) {
Menu.removeMenuItem(AUDIO_MENU, AUDIO_STEREO_INPUT);
}
}
setupMenus();

View file

@ -54,7 +54,7 @@ else ()
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
endif ()
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Script Svg WebKitWidgets)
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets)
# grab the ui files in resources/ui
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
@ -88,18 +88,15 @@ if (APPLE)
# set where in the bundle to put the resources file
SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/icon/${ICON_FILENAME} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
# grab the directories in resources and put them in the right spot in Resources
file(GLOB RESOURCE_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/resources" "${CMAKE_CURRENT_SOURCE_DIR}/resources/*")
foreach(DIR ${RESOURCE_SUBDIRS})
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/resources/${DIR}")
FILE(GLOB DIR_CONTENTS "resources/${DIR}/*")
SET_SOURCE_FILES_PROPERTIES(${DIR_CONTENTS} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources/${DIR}")
set(DISCOVERED_RESOURCES "")
SET(INTERFACE_SRCS ${INTERFACE_SRCS} "${DIR_CONTENTS}")
endif()
endforeach()
# use the add_resources_to_os_x_bundle macro to recurse into resources
add_resources_to_os_x_bundle("${CMAKE_CURRENT_SOURCE_DIR}/resources")
SET(INTERFACE_SRCS ${INTERFACE_SRCS} "${CMAKE_CURRENT_SOURCE_DIR}/icon/${ICON_FILENAME}")
# append the discovered resources to our list of interface sources
list(APPEND INTERFACE_SRCS ${DISCOVERED_RESOURCES})
set(INTERFACE_SRCS ${INTERFACE_SRCS} "${CMAKE_CURRENT_SOURCE_DIR}/icon/${ICON_FILENAME}")
endif()
# create the executable, make it a bundle on OS X
@ -128,7 +125,7 @@ target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
# link required hifi libraries
link_hifi_libraries(shared octree environment gpu model fbx networking entities avatars
audio audio-client animation script-engine physics
render-utils entities-renderer)
render-utils entities-renderer ui)
add_dependency_external_projects(sdl2)
@ -170,15 +167,6 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
endif ()
endforeach()
# special APPLE modifications for Visage library
if (VISAGE_FOUND AND NOT DISABLE_VISAGE AND APPLE)
add_definitions(-DMAC_OS_X)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment")
find_library(AVFoundation AVFoundation)
find_library(CoreMedia CoreMedia)
target_link_libraries(${TARGET_NAME} ${AVFoundation} ${CoreMedia} ${NEW_STD_LIBRARY})
endif ()
# special OS X modifications for RtMidi library
if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI AND APPLE)
find_library(CoreMIDI CoreMIDI)

View file

@ -1,14 +0,0 @@
Instructions for adding the Visage driver to Interface
Andrzej Kapolka, February 11, 2014
1. Copy the Visage sdk folders (lib, include, dependencies) into the interface/external/visage folder.
This readme.txt should be there as well.
2. Copy the contents of the Visage configuration data folder (visageSDK-MacOS/Samples/MacOSX/data/) to
interface/resources/visage (i.e., so that interface/resources/visage/candide3.wfm is accessible)
3. Copy the Visage license file to interface/resources/visage/license.vlc.
4. Delete your build directory, run cmake and build, and you should be all set.

Binary file not shown.

View file

@ -0,0 +1,102 @@
import Hifi 1.0
import QtQuick 2.3
import "controls"
import "styles"
Dialog {
id: root
HifiConstants { id: hifi }
title: "Go to..."
objectName: "AddressBarDialog"
contentImplicitWidth: addressBarDialog.implicitWidth
contentImplicitHeight: addressBarDialog.implicitHeight
destroyOnCloseButton: false
onVisibleChanged: {
if (!visible) {
reset();
}
}
onEnabledChanged: {
if (enabled) {
addressLine.forceActiveFocus();
}
}
onParentChanged: {
if (enabled && visible) {
addressLine.forceActiveFocus();
}
}
function reset() {
addressLine.text = ""
goButton.source = "../images/address-bar-submit.svg"
}
AddressBarDialog {
id: addressBarDialog
// The client area
x: root.clientX
y: root.clientY
implicitWidth: 512
implicitHeight: border.height + hifi.layout.spacing * 4
Border {
id: border
height: 64
anchors.left: parent.left
anchors.leftMargin: hifi.layout.spacing * 2
anchors.right: goButton.left
anchors.rightMargin: hifi.layout.spacing
anchors.verticalCenter: parent.verticalCenter
TextInput {
id: addressLine
anchors.fill: parent
helperText: "domain, location, @user, /x,y,z"
anchors.margins: hifi.layout.spacing
onAccepted: {
event.accepted
addressBarDialog.loadAddress(addressLine.text)
}
}
}
Image {
id: goButton
width: 32
height: 32
anchors.right: parent.right
anchors.rightMargin: hifi.layout.spacing * 2
source: "../images/address-bar-submit.svg"
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
onClicked: {
parent.source = "../images/address-bar-submit-active.svg"
addressBarDialog.loadAddress(addressLine.text)
}
}
}
}
Keys.onEscapePressed: {
enabled = false;
}
function toggleOrGo() {
if (addressLine.text == "") {
enabled = false
} else {
addressBarDialog.loadAddress(addressLine.text)
}
}
Keys.onReturnPressed: toggleOrGo()
Keys.onEnterPressed: toggleOrGo()
}

View file

@ -0,0 +1,30 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtWebKit 3.0
import "controls"
Dialog {
title: "Browser Window"
id: testDialog
objectName: "Browser"
width: 1280
height: 720
Item {
id: clientArea
// The client area
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
ScrollView {
anchors.fill: parent
WebView {
id: webview
url: "http://slashdot.org"
anchors.fill: parent
}
}
}
}

View file

@ -0,0 +1,198 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls.Styles 1.3
import "controls"
import "styles"
Dialog {
HifiConstants { id: hifi }
title: "Login"
objectName: "LoginDialog"
height: 512
width: 384
onVisibleChanged: {
if (!visible) {
reset()
}
}
onEnabledChanged: {
if (enabled) {
username.forceActiveFocus();
}
}
function reset() {
username.text = ""
password.text = ""
loginDialog.statusText = ""
}
LoginDialog {
id: loginDialog
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
Column {
anchors.topMargin: 8
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.top: parent.top
spacing: 8
Image {
height: 64
anchors.horizontalCenter: parent.horizontalCenter
width: 64
source: "../images/hifi-logo.svg"
}
Border {
width: 304
height: 64
anchors.horizontalCenter: parent.horizontalCenter
TextInput {
id: username
anchors.fill: parent
helperText: "Username or Email"
anchors.margins: 8
KeyNavigation.tab: password
KeyNavigation.backtab: password
}
}
Border {
width: 304
height: 64
anchors.horizontalCenter: parent.horizontalCenter
TextInput {
id: password
anchors.fill: parent
echoMode: TextInput.Password
helperText: "Password"
anchors.margins: 8
KeyNavigation.tab: username
KeyNavigation.backtab: username
onFocusChanged: {
if (password.focus) {
password.selectAll()
}
}
}
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
textFormat: Text.StyledText
width: parent.width
height: 96
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: loginDialog.statusText
}
}
Column {
anchors.bottomMargin: 5
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.bottom: parent.bottom
Rectangle {
width: 192
height: 64
anchors.horizontalCenter: parent.horizontalCenter
color: hifi.colors.hifiBlue
border.width: 0
radius: 10
MouseArea {
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.left
onClicked: {
loginDialog.login(username.text, password.text)
}
}
Row {
anchors.centerIn: parent
anchors.verticalCenter: parent.verticalCenter
spacing: 8
Image {
id: loginIcon
height: 32
width: 32
source: "../images/login.svg"
}
Text {
text: "Login"
color: "white"
width: 64
height: parent.height
}
}
}
Text {
width: parent.width
height: 24
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text:"Create Account"
font.pointSize: 12
font.bold: true
color: hifi.colors.hifiBlue
MouseArea {
anchors.fill: parent
onClicked: {
loginDialog.openUrl(loginDialog.rootUrl + "/signup")
}
}
}
Text {
width: parent.width
height: 24
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pointSize: 12
text: "Recover Password"
color: hifi.colors.hifiBlue
MouseArea {
anchors.fill: parent
onClicked: {
loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new")
}
}
}
}
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
if (username.activeFocus) {
event.accepted = true
password.forceActiveFocus()
} else if (password.activeFocus) {
event.accepted = true
if (username.text == "") {
username.forceActiveFocus()
} else {
loginDialog.login(username.text, password.text)
}
}
break;
}
}
}

View file

@ -0,0 +1,50 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.3
import QtWebKit 3.0
import "controls"
Dialog {
title: "Test Dlg"
id: testDialog
objectName: "Browser"
width: 720
height: 720
resizable: true
MarketplaceDialog {
id: marketplaceDialog
}
Item {
id: clientArea
// The client area
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
ScrollView {
anchors.fill: parent
WebView {
objectName: "WebView"
id: webview
url: "https://metaverse.highfidelity.com/marketplace"
anchors.fill: parent
onNavigationRequested: {
console.log(request.url)
if (!marketplaceDialog.navigationRequested(request.url)) {
console.log("Application absorbed the request")
request.action = WebView.IgnoreRequest;
return;
}
console.log("Application passed on the request")
request.action = WebView.AcceptRequest;
return;
}
}
}
}
}

View file

@ -0,0 +1,333 @@
import Hifi 1.0 as Hifi
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import "controls"
import "styles"
Dialog {
id: root
HifiConstants { id: hifi }
property real spacing: hifi.layout.spacing
property real outerSpacing: hifi.layout.spacing * 2
destroyOnCloseButton: true
destroyOnInvisible: true
contentImplicitWidth: content.implicitWidth
contentImplicitHeight: content.implicitHeight
Component.onCompleted: {
enabled = true
}
onParentChanged: {
if (visible && enabled) {
forceActiveFocus();
}
}
Hifi.MessageDialog {
id: content
clip: true
x: root.clientX
y: root.clientY
implicitHeight: contentColumn.implicitHeight + outerSpacing * 2
implicitWidth: mainText.implicitWidth + outerSpacing * 2
Component.onCompleted: {
root.title = title
}
onTitleChanged: {
root.title = title
}
Column {
anchors.fill: parent
anchors.margins: 8
id: contentColumn
spacing: root.outerSpacing
Item {
width: parent.width
height: Math.max(icon.height, mainText.height + informativeText.height + root.spacing)
FontAwesome {
id: icon
width: content.icon ? 48 : 0
height: content.icon ? 48 : 0
visible: content.icon ? true : false
font.pixelSize: 48
verticalAlignment: Text.AlignTop
horizontalAlignment: Text.AlignLeft
color: iconColor()
text: iconSymbol()
function iconSymbol() {
switch (content.icon) {
case Hifi.MessageDialog.Information:
return "\uF05A"
case Hifi.MessageDialog.Question:
return "\uF059"
case Hifi.MessageDialog.Warning:
return "\uF071"
case Hifi.MessageDialog.Critical:
return "\uF057"
default:
break;
}
return content.icon;
}
function iconColor() {
switch (content.icon) {
case Hifi.MessageDialog.Information:
case Hifi.MessageDialog.Question:
return "blue"
case Hifi.MessageDialog.Warning:
case Hifi.MessageDialog.Critical:
return "red"
default:
break
}
return "black"
}
}
Text {
id: mainText
anchors {
left: icon.right
leftMargin: root.spacing
right: parent.right
}
text: content.text
font.pointSize: 14
font.weight: Font.Bold
wrapMode: Text.WordWrap
}
Text {
id: informativeText
anchors {
left: icon.right
right: parent.right
top: mainText.bottom
leftMargin: root.spacing
topMargin: root.spacing
}
text: content.informativeText
font.pointSize: 14
wrapMode: Text.WordWrap
}
}
Flow {
id: buttons
spacing: root.spacing
layoutDirection: Qt.RightToLeft
width: parent.width
Button {
id: okButton
text: qsTr("OK")
onClicked: content.click(StandardButton.Ok)
visible: content.standardButtons & StandardButton.Ok
}
Button {
id: openButton
text: qsTr("Open")
onClicked: content.click(StandardButton.Open)
visible: content.standardButtons & StandardButton.Open
}
Button {
id: saveButton
text: qsTr("Save")
onClicked: content.click(StandardButton.Save)
visible: content.standardButtons & StandardButton.Save
}
Button {
id: saveAllButton
text: qsTr("Save All")
onClicked: content.click(StandardButton.SaveAll)
visible: content.standardButtons & StandardButton.SaveAll
}
Button {
id: retryButton
text: qsTr("Retry")
onClicked: content.click(StandardButton.Retry)
visible: content.standardButtons & StandardButton.Retry
}
Button {
id: ignoreButton
text: qsTr("Ignore")
onClicked: content.click(StandardButton.Ignore)
visible: content.standardButtons & StandardButton.Ignore
}
Button {
id: applyButton
text: qsTr("Apply")
onClicked: content.click(StandardButton.Apply)
visible: content.standardButtons & StandardButton.Apply
}
Button {
id: yesButton
text: qsTr("Yes")
onClicked: content.click(StandardButton.Yes)
visible: content.standardButtons & StandardButton.Yes
}
Button {
id: yesAllButton
text: qsTr("Yes to All")
onClicked: content.click(StandardButton.YesToAll)
visible: content.standardButtons & StandardButton.YesToAll
}
Button {
id: noButton
text: qsTr("No")
onClicked: content.click(StandardButton.No)
visible: content.standardButtons & StandardButton.No
}
Button {
id: noAllButton
text: qsTr("No to All")
onClicked: content.click(StandardButton.NoToAll)
visible: content.standardButtons & StandardButton.NoToAll
}
Button {
id: discardButton
text: qsTr("Discard")
onClicked: content.click(StandardButton.Discard)
visible: content.standardButtons & StandardButton.Discard
}
Button {
id: resetButton
text: qsTr("Reset")
onClicked: content.click(StandardButton.Reset)
visible: content.standardButtons & StandardButton.Reset
}
Button {
id: restoreDefaultsButton
text: qsTr("Restore Defaults")
onClicked: content.click(StandardButton.RestoreDefaults)
visible: content.standardButtons & StandardButton.RestoreDefaults
}
Button {
id: cancelButton
text: qsTr("Cancel")
onClicked: content.click(StandardButton.Cancel)
visible: content.standardButtons & StandardButton.Cancel
}
Button {
id: abortButton
text: qsTr("Abort")
onClicked: content.click(StandardButton.Abort)
visible: content.standardButtons & StandardButton.Abort
}
Button {
id: closeButton
text: qsTr("Close")
onClicked: content.click(StandardButton.Close)
visible: content.standardButtons & StandardButton.Close
}
Button {
id: moreButton
text: qsTr("Show Details...")
onClicked: content.state = (content.state === "" ? "expanded" : "")
visible: content.detailedText.length > 0
}
Button {
id: helpButton
text: qsTr("Help")
onClicked: content.click(StandardButton.Help)
visible: content.standardButtons & StandardButton.Help
}
}
}
Item {
id: details
width: parent.width
implicitHeight: detailedText.implicitHeight + root.spacing
height: 0
clip: true
anchors {
left: parent.left
right: parent.right
top: contentColumn.bottom
topMargin: root.spacing
leftMargin: root.outerSpacing
rightMargin: root.outerSpacing
}
Flickable {
id: flickable
contentHeight: detailedText.height
anchors.fill: parent
anchors.topMargin: root.spacing
anchors.bottomMargin: root.outerSpacing
TextEdit {
id: detailedText
text: content.detailedText
width: details.width
wrapMode: Text.WordWrap
readOnly: true
selectByMouse: true
}
}
}
states: [
State {
name: "expanded"
PropertyChanges {
target: details
height: root.height - contentColumn.height - root.spacing - root.outerSpacing
}
PropertyChanges {
target: content
implicitHeight: contentColumn.implicitHeight + root.spacing * 2 +
detailedText.implicitHeight + root.outerSpacing * 2
}
PropertyChanges {
target: moreButton
text: qsTr("Hide Details")
}
}
]
}
Keys.onPressed: {
if (event.modifiers === Qt.ControlModifier)
switch (event.key) {
case Qt.Key_A:
event.accepted = true
detailedText.selectAll()
break
case Qt.Key_C:
event.accepted = true
detailedText.copy()
break
case Qt.Key_Period:
if (Qt.platform.os === "osx") {
event.accepted = true
content.reject()
}
break
} else switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
event.accepted = true
content.reject()
break
case Qt.Key_Enter:
case Qt.Key_Return:
event.accepted = true
content.accept()
break
}
}
}

View file

@ -0,0 +1,150 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
Rectangle {
color: "teal"
height: 512
width: 192
SystemPalette { id: sp; colorGroup: SystemPalette.Active }
SystemPalette { id: spi; colorGroup: SystemPalette.Inactive }
SystemPalette { id: spd; colorGroup: SystemPalette.Disabled }
Column {
anchors.margins: 8
anchors.fill: parent
spacing: 8
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "base" }
Rectangle { height: parent.height; width: 16; color: sp.base }
Rectangle { height: parent.height; width: 16; color: spi.base }
Rectangle { height: parent.height; width: 16; color: spd.base }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "alternateBase" }
Rectangle { height: parent.height; width: 16; color: sp.alternateBase }
Rectangle { height: parent.height; width: 16; color: spi.alternateBase }
Rectangle { height: parent.height; width: 16; color: spd.alternateBase }
}
Item {
height: 16
width:parent.width
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "dark" }
Rectangle { height: parent.height; width: 16; color: sp.dark }
Rectangle { height: parent.height; width: 16; color: spi.dark }
Rectangle { height: parent.height; width: 16; color: spd.dark }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "mid" }
Rectangle { height: parent.height; width: 16; color: sp.mid }
Rectangle { height: parent.height; width: 16; color: spi.mid }
Rectangle { height: parent.height; width: 16; color: spd.mid }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "mid light" }
Rectangle { height: parent.height; width: 16; color: sp.midlight }
Rectangle { height: parent.height; width: 16; color: spi.midlight }
Rectangle { height: parent.height; width: 16; color: spd.midlight }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "light" }
Rectangle { height: parent.height; width: 16; color: sp.light}
Rectangle { height: parent.height; width: 16; color: spi.light}
Rectangle { height: parent.height; width: 16; color: spd.light}
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "shadow" }
Rectangle { height: parent.height; width: 16; color: sp.shadow}
Rectangle { height: parent.height; width: 16; color: spi.shadow}
Rectangle { height: parent.height; width: 16; color: spd.shadow}
}
Item {
height: 16
width:parent.width
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "text" }
Rectangle { height: parent.height; width: 16; color: sp.text }
Rectangle { height: parent.height; width: 16; color: spi.text }
Rectangle { height: parent.height; width: 16; color: spd.text }
}
Item {
height: 16
width:parent.width
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "window" }
Rectangle { height: parent.height; width: 16; color: sp.window }
Rectangle { height: parent.height; width: 16; color: spi.window }
Rectangle { height: parent.height; width: 16; color: spd.window }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "window text" }
Rectangle { height: parent.height; width: 16; color: sp.windowText }
Rectangle { height: parent.height; width: 16; color: spi.windowText }
Rectangle { height: parent.height; width: 16; color: spd.windowText }
}
Item {
height: 16
width:parent.width
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "button" }
Rectangle { height: parent.height; width: 16; color: sp.button }
Rectangle { height: parent.height; width: 16; color: spi.button }
Rectangle { height: parent.height; width: 16; color: spd.button }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "buttonText" }
Rectangle { height: parent.height; width: 16; color: sp.buttonText }
Rectangle { height: parent.height; width: 16; color: spi.buttonText }
Rectangle { height: parent.height; width: 16; color: spd.buttonText }
}
Item {
height: 16
width:parent.width
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "highlight" }
Rectangle { height: parent.height; width: 16; color: sp.highlight }
Rectangle { height: parent.height; width: 16; color: spi.highlight }
Rectangle { height: parent.height; width: 16; color: spd.highlight }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "highlighted text" }
Rectangle { height: parent.height; width: 16; color: sp.highlightedText}
Rectangle { height: parent.height; width: 16; color: spi.highlightedText}
Rectangle { height: parent.height; width: 16; color: spd.highlightedText}
}
}
}

View file

@ -0,0 +1,14 @@
import Hifi 1.0
import QtQuick 2.3
// This is our primary 'window' object to which all dialogs and controls will
// be childed.
Root {
id: root
anchors.fill: parent
onParentChanged: {
forceActiveFocus();
}
}

View file

@ -0,0 +1,9 @@
import QtQuick 2.4
import QtQuick.Controls 1.3
Item {
Menu {
id: root
objectName: "rootMenu"
}
}

View file

@ -0,0 +1,94 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.3
import "controls"
Dialog {
title: "Test Dialog"
id: testDialog
objectName: "TestDialog"
width: 512
height: 512
animationDuration: 200
onEnabledChanged: {
if (enabled) {
edit.forceActiveFocus();
}
}
Item {
id: clientArea
// The client area
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
Rectangle {
property int d: 100
id: square
objectName: "testRect"
width: d
height: d
anchors.centerIn: parent
color: "red"
NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
}
TextEdit {
id: edit
anchors.left: parent.left
anchors.leftMargin: 12
anchors.right: parent.right
anchors.rightMargin: 12
clip: true
text: "test edit"
anchors.top: parent.top
anchors.topMargin: 12
}
Button {
x: 128
y: 192
text: "Test"
anchors.bottom: parent.bottom
anchors.bottomMargin: 12
anchors.right: parent.right
anchors.rightMargin: 12
onClicked: {
console.log("Click");
if (square.visible) {
square.visible = false
} else {
square.visible = true
}
}
}
Button {
id: customButton2
y: 192
text: "Move"
anchors.left: parent.left
anchors.leftMargin: 12
anchors.bottom: parent.bottom
anchors.bottomMargin: 12
onClicked: {
onClicked: testDialog.x == 0 ? testDialog.x = 200 : testDialog.x = 0
}
}
Keys.onPressed: {
console.log("Key " + event.key);
switch (event.key) {
case Qt.Key_Q:
if (Qt.ControlModifier == event.modifiers) {
event.accepted = true;
break;
}
}
}
}
}

View file

@ -0,0 +1,115 @@
import QtQuick 2.4
import QtQuick.Controls 1.3
import Hifi 1.0
// Currently for testing a pure QML replacement menu
Item {
Item {
objectName: "AllActions"
Action {
id: aboutApp
objectName: "HifiAction_" + MenuConstants.AboutApp
text: qsTr("About Interface")
}
//
// File Menu
//
Action {
id: login
objectName: "HifiAction_" + MenuConstants.Login
text: qsTr("Login")
}
Action {
id: quit
objectName: "HifiAction_" + MenuConstants.Quit
text: qsTr("Quit")
//shortcut: StandardKey.Quit
shortcut: "Ctrl+Q"
}
//
// Edit menu
//
Action {
id: undo
text: "Undo"
shortcut: StandardKey.Undo
}
Action {
id: redo
text: "Redo"
shortcut: StandardKey.Redo
}
Action {
id: animations
objectName: "HifiAction_" + MenuConstants.Animations
text: qsTr("Animations...")
}
Action {
id: attachments
text: qsTr("Attachments...")
}
Action {
id: explode
text: qsTr("Explode on quit")
checkable: true
checked: true
}
Action {
id: freeze
text: qsTr("Freeze on quit")
checkable: true
checked: false
}
ExclusiveGroup {
Action {
id: visibleToEveryone
objectName: "HifiAction_" + MenuConstants.VisibleToEveryone
text: qsTr("Everyone")
checkable: true
checked: true
}
Action {
id: visibleToFriends
objectName: "HifiAction_" + MenuConstants.VisibleToFriends
text: qsTr("Friends")
checkable: true
}
Action {
id: visibleToNoOne
objectName: "HifiAction_" + MenuConstants.VisibleToNoOne
text: qsTr("No one")
checkable: true
}
}
}
Menu {
objectName: "rootMenu";
Menu {
title: "File"
MenuItem { action: login }
MenuItem { action: explode }
MenuItem { action: freeze }
MenuItem { action: quit }
}
Menu {
title: "Tools"
Menu {
title: "I Am Visible To"
MenuItem { action: visibleToEveryone }
MenuItem { action: visibleToFriends }
MenuItem { action: visibleToNoOne }
}
MenuItem { action: animations }
}
Menu {
title: "Help"
MenuItem { action: aboutApp }
}
}
}

View file

@ -0,0 +1,43 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls 1.3
// Import local folder last so that our own control customizations override
// the built in ones
import "controls"
Root {
id: root
anchors.fill: parent
onParentChanged: {
forceActiveFocus();
}
Button {
id: messageBox
anchors.right: createDialog.left
anchors.rightMargin: 24
anchors.bottom: parent.bottom
anchors.bottomMargin: 24
text: "Message"
onClicked: {
console.log("Foo")
root.information("a")
console.log("Bar")
}
}
Button {
id: createDialog
anchors.right: parent.right
anchors.rightMargin: 24
anchors.bottom: parent.bottom
anchors.bottomMargin: 24
text: "Create"
onClicked: {
root.loadChild("MenuTest.qml");
}
}
Keys.onPressed: {
console.log("Key press root")
}
}

View file

@ -0,0 +1,326 @@
import Hifi 1.0 as Hifi
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
import "controls"
import "styles"
Hifi.VrMenu {
id: root
HifiConstants { id: hifi }
anchors.fill: parent
objectName: "VrMenu"
enabled: false
opacity: 0.0
property int animationDuration: 200
property var models: []
property var columns: []
z: 10000
onEnabledChanged: {
if (enabled && columns.length == 0) {
pushColumn(rootMenu.items);
}
opacity = enabled ? 1.0 : 0.0
if (enabled) {
forceActiveFocus()
}
}
// The actual animator
Behavior on opacity {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.InOutBounce
}
}
onOpacityChanged: {
visible = (opacity != 0.0);
}
onVisibleChanged: {
if (!visible) reset();
}
property var menuBuilder: Component {
Border {
HifiConstants { id: hifi }
Component.onCompleted: {
menuDepth = root.models.length - 1
if (menuDepth == 0) {
x = lastMousePosition.x - 20
y = lastMousePosition.y - 20
} else {
var lastColumn = root.columns[menuDepth - 1]
x = lastColumn.x + 64;
y = lastMousePosition.y - height / 2;
}
}
border.color: hifi.colors.hifiBlue
color: hifi.colors.window
property int menuDepth
implicitHeight: listView.implicitHeight + 16
implicitWidth: listView.implicitWidth + 16
Column {
id: listView
property real minWidth: 0
anchors {
top: parent.top
topMargin: 8
left: parent.left
leftMargin: 8
right: parent.right
rightMargin: 8
}
Repeater {
model: root.models[menuDepth]
delegate: Loader {
id: loader
sourceComponent: root.itemBuilder
Binding {
target: loader.item
property: "root"
value: root
when: loader.status == Loader.Ready
}
Binding {
target: loader.item
property: "source"
value: modelData
when: loader.status == Loader.Ready
}
Binding {
target: loader.item
property: "border"
value: listView.parent
when: loader.status == Loader.Ready
}
Binding {
target: loader.item
property: "listView"
value: listView
when: loader.status == Loader.Ready
}
}
}
}
}
}
property var itemBuilder: Component {
Item {
property var source
property var root
property var listView
property var border
implicitHeight: row.implicitHeight + 4
implicitWidth: row.implicitWidth + label.height
// FIXME uncommenting this line results in menus that have blank spots
// rather than having the correct size
// visible: source.visible
Row {
id: row
spacing: 2
anchors {
top: parent.top
topMargin: 2
}
Spacer { size: 4 }
FontAwesome {
id: check
verticalAlignment: Text.AlignVCenter
y: 2
size: label.height
text: checkText()
color: label.color
function checkText() {
if (!source || source.type != 1 || !source.checkable) {
return "";
}
// FIXME this works for native QML menus but I don't think it will
// for proxied QML menus
if (source.exclusiveGroup) {
return source.checked ? "\uF05D" : "\uF10C"
}
return source.checked ? "\uF046" : "\uF096"
}
}
Text {
id: label
text: typedText()
color: source.enabled ? hifi.colors.text : hifi.colors.disabledText
enabled: source.enabled && source.visible
function typedText() {
if (source) {
switch(source.type) {
case 2:
return source.title;
case 1:
return source.text;
case 0:
return "-----"
}
}
return ""
}
}
} // row
FontAwesome {
anchors {
top: row.top
}
id: tag
size: label.height
width: implicitWidth
visible: source.type == 2
x: listView.width - width - 4
text: "\uF0DA"
color: label.color
}
MouseArea {
anchors {
top: parent.top
bottom: parent.bottom
left: parent.left
right: tag.right
rightMargin: -4
}
acceptedButtons: Qt.LeftButton
hoverEnabled: true
Rectangle {
id: highlight
visible: false
anchors.fill: parent
color: "#7f0e7077"
}
Timer {
id: timer
interval: 1000
onTriggered: parent.select();
}
onEntered: {
/*
* Uncomment below to have menus auto-popup
*
* FIXME if we enabled timer based menu popup, either the timer has
* to be very very short or after auto popup there has to be a small
* amount of time, or a test if the mouse has moved before a click
* will be accepted, otherwise it's too easy to accidently click on
* something immediately after the auto-popup appears underneath your
* cursor
*
*/
//if (source.type == 2 && enabled) {
// timer.start()
//}
highlight.visible = source.enabled
}
onExited: {
timer.stop()
highlight.visible = false
}
onClicked: {
select();
}
function select() {
//timer.stop();
var popped = false;
while (columns.length - 1 > listView.parent.menuDepth) {
popColumn();
popped = true;
}
if (!popped || source.type != 1) {
parent.root.selectItem(parent.source);
}
}
}
}
}
function lastColumn() {
return columns[root.columns.length - 1];
}
function pushColumn(items) {
models.push(items)
if (columns.length) {
var oldColumn = lastColumn();
//oldColumn.enabled = false
}
var newColumn = menuBuilder.createObject(root);
columns.push(newColumn);
newColumn.forceActiveFocus();
}
function popColumn() {
if (columns.length > 0) {
var curColumn = columns.pop();
curColumn.visible = false;
curColumn.destroy();
models.pop();
}
if (columns.length == 0) {
enabled = false;
return;
}
curColumn = lastColumn();
curColumn.enabled = true;
curColumn.opacity = 1.0;
curColumn.forceActiveFocus();
}
function selectItem(source) {
switch (source.type) {
case 2:
pushColumn(source.items)
break;
case 1:
source.trigger()
enabled = false
break;
case 0:
break;
}
}
function reset() {
while (columns.length > 0) {
popColumn();
}
}
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Escape:
root.popColumn()
event.accepted = true;
}
}
MouseArea {
anchors.fill: parent
id: mouseArea
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button == Qt.RightButton) {
root.popColumn();
} else {
root.enabled = false;
}
}
}
}

View file

@ -0,0 +1,9 @@
import QtQuick 2.3
import QtQuick.Controls 1.3 as Original
import QtQuick.Controls.Styles 1.3
import "."
import "../styles"
Original.Button {
style: ButtonStyle { }
}

View file

@ -0,0 +1,16 @@
import QtQuick.Controls 1.3 as Original
import QtQuick.Controls.Styles 1.3
import "../styles"
import "."
Original.CheckBox {
text: "Check Box"
style: CheckBoxStyle {
indicator: FontAwesome {
text: control.checked ? "\uf046" : "\uf096"
}
label: Text {
text: control.text
}
}
}

View file

@ -0,0 +1,158 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import "."
import "../styles"
/*
* FIXME Need to create a client property here so that objects can be
* placed in it without having to think about positioning within the outer
* window.
*
* Examine the QML ApplicationWindow.qml source for how it does this
*
*/
DialogBase {
id: root
HifiConstants { id: hifi }
// FIXME better placement via a window manager
x: parent ? parent.width / 2 - width / 2 : 0
y: parent ? parent.height / 2 - height / 2 : 0
property bool destroyOnInvisible: false
property bool destroyOnCloseButton: true
property bool resizable: false
property int animationDuration: hifi.effects.fadeInDuration
property int minX: 256
property int minY: 256
readonly property int topMargin: root.height - clientBorder.height + hifi.layout.spacing
/*
* Support for animating the dialog in and out.
*/
enabled: false
scale: 0.0
// The offscreen UI will enable an object, rather than manipulating it's
// visibility, so that we can do animations in both directions. Because
// visibility and enabled are boolean flags, they cannot be animated. So when
// enabled is change, we modify a property that can be animated, like scale or
// opacity, and then when the target animation value is reached, we can
// modify the visibility
onEnabledChanged: {
scale = enabled ? 1.0 : 0.0
}
// The actual animator
Behavior on scale {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.InOutBounce
}
}
// Once we're scaled to 0, disable the dialog's visibility
onScaleChanged: {
visible = (scale != 0.0);
}
// Some dialogs should be destroyed when they become invisible,
// so handle that
onVisibleChanged: {
if (!visible && destroyOnInvisible) {
destroy();
}
}
// our close function performs the same way as the OffscreenUI class:
// don't do anything but manipulate the enabled flag and let the other
// mechanisms decide if the window should be destroyed after the close
// animation completes
function close() {
if (destroyOnCloseButton) {
destroyOnInvisible = true
}
enabled = false;
}
/*
* Resize support
*/
function deltaSize(dx, dy) {
width = Math.max(width + dx, minX)
height = Math.max(height + dy, minY)
}
MouseArea {
id: sizeDrag
enabled: root.resizable
property int startX
property int startY
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 16
height: 16
z: 1000
hoverEnabled: true
onPressed: {
startX = mouseX
startY = mouseY
}
onPositionChanged: {
if (pressed) {
root.deltaSize((mouseX - startX), (mouseY - startY))
startX = mouseX
startY = mouseY
}
}
}
MouseArea {
id: titleDrag
x: root.titleX
y: root.titleY
width: root.titleWidth
height: root.titleHeight
drag {
target: root
minimumX: 0
minimumY: 0
maximumX: root.parent ? root.parent.width - root.width : 0
maximumY: root.parent ? root.parent.height - root.height : 0
}
Row {
id: windowControls
anchors.bottom: parent.bottom
anchors.top: parent.top
anchors.right: parent.right
anchors.rightMargin: hifi.layout.spacing
FontAwesome {
id: icon
anchors.verticalCenter: parent.verticalCenter
size: root.titleHeight - hifi.layout.spacing * 2
color: "red"
text: "\uf00d"
MouseArea {
anchors.margins: hifi.layout.spacing / 2
anchors.fill: parent
onClicked: {
root.close();
}
}
}
}
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_W:
if (event.modifiers == Qt.ControlModifier) {
event.accepted = true
enabled = false
}
break;
}
}
}

View file

@ -0,0 +1,98 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import "."
import "../styles"
Item {
id: root
HifiConstants { id: hifi }
implicitHeight: contentImplicitHeight + titleBorder.height + hifi.styles.borderWidth
implicitWidth: contentImplicitWidth + hifi.styles.borderWidth * 2
property string title
property int titleSize: titleBorder.height + 12
property string frameColor: hifi.colors.hifiBlue
property string backgroundColor: hifi.colors.dialogBackground
property bool active: false
property real contentImplicitWidth: 800
property real contentImplicitHeight: 800
property alias titleBorder: titleBorder
readonly property alias titleX: titleBorder.x
readonly property alias titleY: titleBorder.y
readonly property alias titleWidth: titleBorder.width
readonly property alias titleHeight: titleBorder.height
// readonly property real borderWidth: hifi.styles.borderWidth
readonly property real borderWidth: 0
property alias clientBorder: clientBorder
readonly property real clientX: clientBorder.x + borderWidth
readonly property real clientY: clientBorder.y + borderWidth
readonly property real clientWidth: clientBorder.width - borderWidth * 2
readonly property real clientHeight: clientBorder.height - borderWidth * 2
/*
* Window decorations, with a title bar and frames
*/
Border {
id: windowBorder
anchors.fill: parent
border.color: root.frameColor
border.width: 0
color: "#00000000"
Border {
id: titleBorder
height: hifi.layout.windowTitleHeight
anchors.right: parent.right
anchors.left: parent.left
border.color: root.frameColor
border.width: 0
clip: true
color: root.active ?
hifi.colors.activeWindow.headerBackground :
hifi.colors.inactiveWindow.headerBackground
Text {
id: titleText
color: root.active ?
hifi.colors.activeWindow.headerText :
hifi.colors.inactiveWindow.headerText
text: root.title
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
}
} // header border
// These two rectangles hide the curves between the title area
// and the client area
Rectangle {
y: titleBorder.height - titleBorder.radius
height: titleBorder.radius
width: titleBorder.width
color: titleBorder.color
visible: borderWidth == 0
}
Rectangle {
y: titleBorder.height
width: clientBorder.width
height: clientBorder.radius
color: clientBorder.color
}
Border {
id: clientBorder
border.width: 0
border.color: root.frameColor
color: root.backgroundColor
anchors.bottom: parent.bottom
anchors.top: titleBorder.bottom
anchors.topMargin: -titleBorder.border.width
anchors.right: parent.right
anchors.left: parent.left
} // client border
} // window border
}

View file

@ -0,0 +1,16 @@
import QtQuick 2.3
import QtQuick.Controls 1.3
import QtQuick.Controls.Styles 1.3
Text {
id: root
FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; }
property int size: 32
width: size
height: size
font.pixelSize: size
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
font.family: iconFont.name
}

View file

@ -0,0 +1,2 @@
These are our own custom controls with the same names as existing controls, but
customized for readability / usability in VR.

View file

@ -0,0 +1,8 @@
import QtQuick.Controls 1.3 as Original
import QtQuick.Controls.Styles 1.3
import "../styles"
import "."
Original.Slider {
}

View file

@ -0,0 +1,11 @@
import QtQuick 2.4
import "../styles"
Item {
id: root
HifiConstants { id: hifi }
property real size: hifi.layout.spacing
property real multiplier: 1.0
height: size * multiplier
width: size * multiplier
}

View file

@ -0,0 +1,15 @@
import QtQuick.Controls 1.3 as Original
import QtQuick.Controls.Styles 1.3
import "../styles"
import "."
Original.SpinBox {
style: SpinBoxStyle {
HifiConstants { id: hifi }
font.family: hifi.fonts.fontFamily
font.pointSize: hifi.fonts.fontSize
}
}

View file

@ -0,0 +1,9 @@
import QtQuick 2.3 as Original
import "../styles"
Original.Text {
HifiConstants { id: hifi }
font.family: hifi.fonts.fontFamily
font.pointSize: hifi.fonts.fontSize
}

View file

@ -0,0 +1,24 @@
import QtQuick 2.3 as Original
import "../styles"
import "."
Original.Item {
property alias text: label.text
property alias value: slider.value
Text {
id: label
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
verticalAlignment: Original.Text.AlignVCenter
}
Slider {
id: slider
width: 120
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
}
}

View file

@ -0,0 +1,26 @@
import QtQuick 2.3 as Original
import "../styles"
import "."
Original.Item {
property alias text: label.text
property alias value: spinBox.value
Text {
id: label
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
verticalAlignment: Original.Text.AlignVCenter
text: "Minimum HMD FPS"
}
SpinBox {
id: spinBox
width: 120
maximumValue: 240
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
}
}

View file

@ -0,0 +1,9 @@
import QtQuick.Controls 2.3 as Original
import "../styles"
Original.TextArea {
HifiConstants { id: hifi }
font.family: hifi.fonts.fontFamily
font.pointSize: hifi.fonts.fontSize
}

View file

@ -0,0 +1,9 @@
import QtQuick 2.3 as Original
import "../styles"
Original.TextEdit {
HifiConstants { id: hifi }
font.family: hifi.fonts.fontFamily
font.pointSize: hifi.fonts.fontSize
}

View file

@ -0,0 +1,9 @@
import "."
import "../styles"
Text {
HifiConstants { id: hifi }
color: hifi.colors.hifiBlue
font.pointSize: hifi.fonts.headerPointSize
font.bold: true
}

View file

@ -0,0 +1,36 @@
import QtQuick 2.3 as Original
import "../styles"
import "."
Original.TextInput {
id: root
HifiConstants { id: hifi }
property string helperText
height: hifi.layout.rowHeight
clip: true
color: hifi.colors.text
verticalAlignment: Original.TextInput.AlignVCenter
font.family: hifi.fonts.fontFamily
font.pointSize: hifi.fonts.fontSize
/*
Original.Rectangle {
// Render the rectangle as background
z: -1
anchors.fill: parent
color: hifi.colors.inputBackground
}
*/
Text {
anchors.fill: parent
font.pointSize: parent.font.pointSize
font.family: parent.font.family
verticalAlignment: parent.verticalAlignment
horizontalAlignment: parent.horizontalAlignment
text: root.helperText
color: hifi.colors.hintText
visible: !root.text
}
}

View file

@ -0,0 +1,40 @@
import QtQuick 2.3 as Original
import "../styles"
import "."
Original.Item {
id: root
HifiConstants { id: hifi }
height: hifi.layout.rowHeight
property string text
property string helperText
property string buttonText
property int buttonWidth: 0
property alias input: input
property alias button: button
signal clicked()
TextInput {
id: input
text: root.text
helperText: root.helperText
anchors.left: parent.left
anchors.right: button.left
anchors.rightMargin: 8
anchors.bottom: parent.bottom
anchors.top: parent.top
}
Button {
id: button
clip: true
width: root.buttonWidth ? root.buttonWidth : implicitWidth
text: root.buttonText
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.top: parent.top
onClicked: root.clicked()
}
}

View file

@ -0,0 +1,12 @@
import QtQuick 2.3
Rectangle {
HifiConstants { id: hifi }
implicitHeight: 64
implicitWidth: 64
color: hifi.colors.window
border.color: hifi.colors.hifiBlue
border.width: hifi.styles.borderWidth
radius: hifi.styles.borderRadius
}

View file

@ -0,0 +1,23 @@
import QtQuick 2.4 as Original
import QtQuick.Controls.Styles 1.3 as OriginalStyles
import "."
import "../controls"
OriginalStyles.ButtonStyle {
HifiConstants { id: hifi }
padding {
top: 8
left: 12
right: 12
bottom: 8
}
background: Border {
anchors.fill: parent
}
label: Text {
verticalAlignment: Original.Text.AlignVCenter
horizontalAlignment: Original.Text.AlignHCenter
text: control.text
color: control.enabled ? hifi.colors.text : hifi.colors.disabledText
}
}

View file

@ -0,0 +1,61 @@
import QtQuick 2.4
Item {
SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active }
readonly property alias colors: colors
readonly property alias layout: layout
readonly property alias fonts: fonts
readonly property alias styles: styles
readonly property alias effects: effects
Item {
id: colors
readonly property color hifiBlue: "#0e7077"
readonly property color window: sysPalette.window
readonly property color dialogBackground: sysPalette.window
//readonly property color dialogBackground: "#00000000"
readonly property color inputBackground: "white"
readonly property color background: sysPalette.dark
readonly property color text: sysPalette.text
readonly property color disabledText: "gray"
readonly property color hintText: sysPalette.dark
readonly property color light: sysPalette.light
readonly property alias activeWindow: activeWindow
readonly property alias inactiveWindow: inactiveWindow
QtObject {
id: activeWindow
readonly property color headerBackground: "white"
readonly property color headerText: "black"
}
QtObject {
id: inactiveWindow
readonly property color headerBackground: "gray"
readonly property color headerText: "black"
}
}
QtObject {
id: fonts
readonly property real headerPointSize: 24
readonly property string fontFamily: "Helvetica"
readonly property real fontSize: 18
}
QtObject {
id: layout
property int spacing: 8
property int rowHeight: 40
property int windowTitleHeight: 48
}
QtObject {
id: styles
readonly property int borderWidth: 5
readonly property int borderRadius: borderWidth * 2
}
QtObject {
id: effects
readonly property int fadeInDuration: 400
}
}

View file

@ -0,0 +1,11 @@
import QtQuick 2.4
Item {
property string hifiBlue: "#0e7077"
property alias colors: colorsObj
Item {
id: colorsObj
property string hifiRed: "red"
}
}

View file

@ -0,0 +1,10 @@
ButtonStyle {
background: Item { anchors.fill: parent }
label: FontAwesome {
id: icon
font.pointSize: 18
property alias unicode: text
text: control.text
color: control.enabled ? hifi.colors.text : hifi.colors.disabledText
}
}

View file

@ -35,7 +35,6 @@
#include <QMouseEvent>
#include <QNetworkReply>
#include <QNetworkDiskCache>
#include <QOpenGLFramebufferObject>
#include <QObject>
#include <QWheelEvent>
#include <QScreen>
@ -64,6 +63,7 @@
#include <GlowEffect.h>
#include <HFActionEvent.h>
#include <HFBackEvent.h>
#include <VrMenu.h>
#include <LogHandler.h>
#include <MainWindow.h>
#include <ModelEntityItem.h>
@ -83,6 +83,7 @@
#include <TextRenderer.h>
#include <UserActivityLogger.h>
#include <UUID.h>
#include <MessageDialog.h>
#include <SceneScriptingInterface.h>
@ -109,7 +110,6 @@
#include "devices/MIDIManager.h"
#include "devices/OculusManager.h"
#include "devices/TV3DManager.h"
#include "devices/Visage.h"
#include "gpu/Batch.h"
#include "gpu/GLBackend.h"
@ -137,6 +137,7 @@
#include "ui/Snapshot.h"
#include "ui/StandAloneJSConsole.h"
#include "ui/Stats.h"
#include "ui/AddressBarDialog.h"
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
#if defined(Q_OS_WIN)
@ -145,6 +146,23 @@ extern "C" {
}
#endif
enum CustomEventTypes {
Lambda = QEvent::User + 1
};
class LambdaEvent : public QEvent {
std::function<void()> _fun;
public:
LambdaEvent(const std::function<void()> & fun) :
QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) {
}
LambdaEvent(std::function<void()> && fun) :
QEvent(static_cast<QEvent::Type>(Lambda)), _fun(fun) {
}
void call() { _fun(); }
};
using namespace std;
// Starfield information
@ -165,8 +183,6 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D
const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js";
bool renderCollisionHulls = false;
#ifdef Q_OS_WIN
class MyNativeEventFilter : public QAbstractNativeEventFilter {
public:
@ -209,8 +225,12 @@ public:
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message);
if (!logMessage.isEmpty()) {
#ifdef Q_OS_WIN
OutputDebugStringA(logMessage.toLocal8Bit().constData());
OutputDebugStringA("\n");
#endif
Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage + "\n"));
}
}
@ -243,7 +263,6 @@ bool setupEssentials(int& argc, char** argv) {
auto ambientOcclusionEffect = DependencyManager::set<AmbientOcclusionEffect>();
auto textureCache = DependencyManager::set<TextureCache>();
auto animationCache = DependencyManager::set<AnimationCache>();
auto visage = DependencyManager::set<Visage>();
auto ddeFaceTracker = DependencyManager::set<DdeFaceTracker>();
auto modelBlender = DependencyManager::set<ModelBlender>();
auto audioToolBox = DependencyManager::set<AudioToolBox>();
@ -260,6 +279,7 @@ bool setupEssentials(int& argc, char** argv) {
#endif
auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>();
auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>();
auto offscreenUi = DependencyManager::set<OffscreenUi>();
return true;
}
@ -316,8 +336,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
#ifdef Q_OS_WIN
installNativeEventFilter(&MyNativeEventFilter::getInstance());
#endif
_logger = new FileLogger(this); // After setting organization name in order to get correct directory
qInstallMessageHandler(messageHandler);
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf");
@ -335,9 +357,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_bookmarks = new Bookmarks(); // Before setting up the menu
// call Menu getInstance static method to set up the menu
_window->setMenuBar(Menu::getInstance());
_runningScriptsWidget = new RunningScriptsWidget(_window);
// start the nodeThread so its event loop is running
@ -444,6 +463,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
addressManager->setOrientationGetter(getOrientationForPath);
connect(addressManager.data(), &AddressManager::rootPlaceNameChanged, this, &Application::updateWindowTitle);
connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
#ifdef _WIN32
WSADATA WsaData;
@ -562,8 +582,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
#endif
this->installEventFilter(this);
// The offscreen UI needs to intercept the mouse and keyboard
// events coming from the onscreen window
_glWidget->installEventFilter(DependencyManager::get<OffscreenUi>().data());
}
void Application::aboutToQuit() {
emit beforeAboutToQuit();
@ -612,6 +636,10 @@ void Application::cleanupBeforeQuit() {
// destroy the AudioClient so it and its thread have a chance to go down safely
DependencyManager::destroy<AudioClient>();
#ifdef HAVE_DDE
DependencyManager::destroy<DdeFaceTracker>();
#endif
}
Application::~Application() {
@ -633,6 +661,7 @@ Application::~Application() {
// stop the glWidget frame timer so it doesn't call paintGL
_glWidget->stopFrameTimer();
DependencyManager::destroy<OffscreenUi>();
DependencyManager::destroy<AvatarManager>();
DependencyManager::destroy<AnimationCache>();
DependencyManager::destroy<TextureCache>();
@ -693,6 +722,17 @@ void Application::initializeGL() {
initDisplay();
qCDebug(interfaceapp, "Initialized Display.");
// The UI can't be created until the primary OpenGL
// context is created, because it needs to share
// texture resources
initializeUi();
qCDebug(interfaceapp, "Initialized Offscreen UI.");
_glWidget->makeCurrent();
// call Menu getInstance static method to set up the menu
// Needs to happen AFTER the QML UI initialization
_window->setMenuBar(Menu::getInstance());
init();
qCDebug(interfaceapp, "init() complete.");
@ -720,10 +760,45 @@ void Application::initializeGL() {
// update before the first render
update(1.0f / _fps);
InfoView::showFirstTime(INFO_HELP_PATH);
}
void Application::initializeUi() {
AddressBarDialog::registerType();
LoginDialog::registerType();
MessageDialog::registerType();
VrMenu::registerType();
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_glWidget->context()->contextHandle());
offscreenUi->resize(_glWidget->size());
offscreenUi->setProxyWindow(_window->windowHandle());
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
offscreenUi->load("Root.qml");
offscreenUi->load("RootMenu.qml");
VrMenu::load();
VrMenu::executeQueuedLambdas();
offscreenUi->setMouseTranslator([this](const QPointF& p){
if (OculusManager::isConnected()) {
glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p));
return QPointF(pos.x, pos.y);
}
return QPointF(p);
});
offscreenUi->resume();
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect & r){
static qreal oldDevicePixelRatio = 0;
qreal devicePixelRatio = _glWidget->devicePixelRatio();
if (devicePixelRatio != oldDevicePixelRatio) {
oldDevicePixelRatio = devicePixelRatio;
qDebug() << "Device pixel ratio changed, triggering GL resize";
resizeGL(_glWidget->width(),
_glWidget->height());
}
});
}
void Application::paintGL() {
PROFILE_RANGE(__FUNCTION__);
PerformanceTimer perfTimer("paintGL");
@ -798,7 +873,7 @@ void Application::paintGL() {
DependencyManager::get<GlowEffect>()->prepare();
// Viewport is assigned to the size of the framebuffer
QSize size = DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject()->size();
QSize size = DependencyManager::get<TextureCache>()->getFrameBufferSize();
glViewport(0, 0, size.width(), size.height());
glMatrixMode(GL_MODELVIEW);
@ -818,7 +893,7 @@ void Application::paintGL() {
{
PerformanceTimer perfTimer("renderOverlay");
_applicationOverlay.renderOverlay(true);
_applicationOverlay.renderOverlay();
_applicationOverlay.displayOverlayTexture();
}
}
@ -863,6 +938,9 @@ void Application::resizeGL(int width, int height) {
updateProjectionMatrix();
glLoadIdentity();
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->resize(_glWidget->size());
// update Stats width
// let's set horizontal offset to give stats some margin to mirror
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
@ -911,6 +989,48 @@ bool Application::importSVOFromURL(const QString& urlString) {
}
bool Application::event(QEvent* event) {
switch (event->type()) {
case Lambda:
((LambdaEvent*)event)->call();
return true;
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
return true;
case QEvent::MouseButtonPress:
mousePressEvent((QMouseEvent*)event);
return true;
case QEvent::MouseButtonRelease:
mouseReleaseEvent((QMouseEvent*)event);
return true;
case QEvent::KeyPress:
keyPressEvent((QKeyEvent*)event);
return true;
case QEvent::KeyRelease:
keyReleaseEvent((QKeyEvent*)event);
return true;
case QEvent::FocusOut:
focusOutEvent((QFocusEvent*)event);
return true;
case QEvent::TouchBegin:
touchBeginEvent(static_cast<QTouchEvent*>(event));
event->accept();
return true;
case QEvent::TouchEnd:
touchEndEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::TouchUpdate:
touchUpdateEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::Wheel:
wheelEvent(static_cast<QWheelEvent*>(event));
return true;
case QEvent::Drop:
dropEvent(static_cast<QDropEvent*>(event));
return true;
default:
break;
}
// handle custom URL
if (event->type() == QEvent::FileOpen) {
@ -931,13 +1051,18 @@ bool Application::event(QEvent* event) {
if (HFActionEvent::types().contains(event->type())) {
_controllerScriptingInterface.handleMetaEvent(static_cast<HFMetaEvent*>(event));
}
return QApplication::event(event);
}
bool Application::eventFilter(QObject* object, QEvent* event) {
if (event->type() == QEvent::ShortcutOverride) {
if (DependencyManager::get<OffscreenUi>()->shouldSwallowShortcut(event)) {
event->accept();
return true;
}
// Filter out captured keys before they're used for shortcut actions.
if (_controllerScriptingInterface.isKeyCaptured(static_cast<QKeyEvent*>(event))) {
event->accept();
@ -948,8 +1073,10 @@ bool Application::eventFilter(QObject* object, QEvent* event) {
return false;
}
void Application::keyPressEvent(QKeyEvent* event) {
static bool _altPressed{ false };
void Application::keyPressEvent(QKeyEvent* event) {
_altPressed = event->key() == Qt::Key_Alt;
_keysPressed.insert(event->key());
_controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
@ -965,12 +1092,19 @@ void Application::keyPressEvent(QKeyEvent* event) {
bool isOption = event->modifiers().testFlag(Qt::AltModifier);
switch (event->key()) {
break;
case Qt::Key_Enter:
case Qt::Key_Return:
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
break;
case Qt::Key_L:
if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::LodTools);
} else if (isMeta) {
if (isShifted && isMeta) {
Menu::getInstance()->triggerOption(MenuOption::Log);
}
} else if (isMeta) {
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
} else if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::LodTools);
}
break;
case Qt::Key_E:
@ -1019,7 +1153,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_A:
if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::Atmosphere);
} else {
} else if (!isMeta) {
_myAvatar->setDriveKeys(ROT_LEFT, 1.0f);
}
break;
@ -1030,11 +1164,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
case Qt::Key_Return:
case Qt::Key_Enter:
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
break;
case Qt::Key_Backslash:
Menu::getInstance()->triggerOption(MenuOption::Chat);
break;
@ -1188,11 +1317,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
}
case Qt::Key_Comma: {
renderCollisionHulls = !renderCollisionHulls;
break;
}
default:
event->ignore();
break;
@ -1200,7 +1324,19 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
}
//#define VR_MENU_ONLY_IN_HMD
void Application::keyReleaseEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Alt && _altPressed && _window->isActiveWindow()) {
#ifdef VR_MENU_ONLY_IN_HMD
if (OculusManager::isConnected()) {
#endif
VrMenu::toggle();
#ifdef VR_MENU_ONLY_IN_HMD
}
#endif
}
_keysPressed.remove(event->key());
@ -1211,6 +1347,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
return;
}
switch (event->key()) {
case Qt::Key_E:
case Qt::Key_PageUp:
@ -1330,6 +1467,9 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
}
void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
// Inhibit the menu if the user is using alt-mouse dragging
_altPressed = false;
if (!_aboutToQuit) {
_entities.mousePressEvent(event, deviceID);
}
@ -1404,9 +1544,12 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
}
void Application::touchUpdateEvent(QTouchEvent* event) {
TouchEvent thisEvent(*event, _lastTouchEvent);
_controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent;
_altPressed = false;
if (event->type() == QEvent::TouchUpdate) {
TouchEvent thisEvent(*event, _lastTouchEvent);
_controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent;
}
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface.isTouchCaptured()) {
@ -1437,6 +1580,7 @@ void Application::touchUpdateEvent(QTouchEvent* event) {
}
void Application::touchBeginEvent(QTouchEvent* event) {
_altPressed = false;
TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event
_controllerScriptingInterface.emitTouchBeginEvent(thisEvent); // send events to any registered scripts
@ -1451,6 +1595,7 @@ void Application::touchBeginEvent(QTouchEvent* event) {
}
void Application::touchEndEvent(QTouchEvent* event) {
_altPressed = false;
TouchEvent thisEvent(*event, _lastTouchEvent);
_controllerScriptingInterface.emitTouchEndEvent(thisEvent); // send events to any registered scripts
_lastTouchEvent = thisEvent;
@ -1467,7 +1612,7 @@ void Application::touchEndEvent(QTouchEvent* event) {
}
void Application::wheelEvent(QWheelEvent* event) {
_altPressed = false;
_controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts
// if one of our scripts have asked to capture this event, then stop processing it
@ -1494,6 +1639,17 @@ void Application::dropEvent(QDropEvent *event) {
}
}
void Application::dragEnterEvent(QDragEnterEvent* event) {
const QMimeData* mimeData = event->mimeData();
foreach(QUrl url, mimeData->urls()) {
auto urlString = url.toString();
if (canAcceptURL(urlString)) {
event->acceptProposedAction();
break;
}
}
}
bool Application::acceptSnapshot(const QString& urlString) {
QUrl url(urlString);
QString snapshotPath = url.toLocalFile();
@ -1732,21 +1888,21 @@ int Application::getMouseDragStartedY() const {
FaceTracker* Application::getActiveFaceTracker() {
auto faceshift = DependencyManager::get<Faceshift>();
auto visage = DependencyManager::get<Visage>();
auto dde = DependencyManager::get<DdeFaceTracker>();
return (dde->isActive() ? static_cast<FaceTracker*>(dde.data()) :
(faceshift->isActive() ? static_cast<FaceTracker*>(faceshift.data()) :
(visage->isActive() ? static_cast<FaceTracker*>(visage.data()) : NULL)));
(faceshift->isActive() ? static_cast<FaceTracker*>(faceshift.data()) : NULL));
}
void Application::setActiveFaceTracker() {
#ifdef HAVE_FACESHIFT
DependencyManager::get<Faceshift>()->setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift));
#endif
DependencyManager::get<DdeFaceTracker>()->setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::DDEFaceRegression));
#ifdef HAVE_VISAGE
DependencyManager::get<Visage>()->updateEnabled();
#endif
#ifdef HAVE_DDE
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera);
Menu::getInstance()->getActionForOption(MenuOption::UseAudioForMouth)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE);
DependencyManager::get<DdeFaceTracker>()->setEnabled(isUsingDDE);
#endif
}
@ -1921,7 +2077,6 @@ void Application::init() {
// initialize our face trackers after loading the menu settings
DependencyManager::get<Faceshift>()->init();
DependencyManager::get<DdeFaceTracker>()->init();
DependencyManager::get<Visage>()->init();
Leapmotion::init();
RealSense::init();
@ -2226,6 +2381,7 @@ void Application::update(float deltaTime) {
PerformanceTimer perfTimer("physics");
_myAvatar->relayDriveKeysToCharacterController();
_physicsEngine.stepSimulation();
_physicsEngine.dumpStatsIfNecessary();
}
if (!_aboutToQuit) {
@ -2636,8 +2792,9 @@ void Application::updateShadowMap() {
activeRenderingThread = QThread::currentThread();
PerformanceTimer perfTimer("shadowMap");
QOpenGLFramebufferObject* fbo = DependencyManager::get<TextureCache>()->getShadowFramebufferObject();
fbo->bind();
auto shadowFramebuffer = DependencyManager::get<TextureCache>()->getShadowFramebuffer();
glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(shadowFramebuffer));
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT);
@ -2653,16 +2810,18 @@ void Application::updateShadowMap() {
loadViewFrustum(_myCamera, _viewFrustum);
int matrixCount = 1;
int targetSize = fbo->width();
//int targetSize = fbo->width();
int sourceSize = shadowFramebuffer->getWidth();
int targetSize = shadowFramebuffer->getWidth();
float targetScale = 1.0f;
if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) {
matrixCount = CASCADED_SHADOW_MATRIX_COUNT;
targetSize = fbo->width() / 2;
targetSize = sourceSize / 2;
targetScale = 0.5f;
}
for (int i = 0; i < matrixCount; i++) {
const glm::vec2& coord = MAP_COORDS[i];
glViewport(coord.s * fbo->width(), coord.t * fbo->height(), targetSize, targetSize);
glViewport(coord.s * sourceSize, coord.t * sourceSize, targetSize, targetSize);
// if simple shadow then since the resolution is twice as much as with cascaded, cover 2 regions with the map, not just one
int regionIncrement = (matrixCount == 1 ? 2 : 1);
@ -2785,7 +2944,7 @@ void Application::updateShadowMap() {
glMatrixMode(GL_MODELVIEW);
}
fbo->release();
// fbo->release();
glViewport(0, 0, _glWidget->getDeviceWidth(), _glWidget->getDeviceHeight());
activeRenderingThread = nullptr;
@ -2831,7 +2990,8 @@ PickRay Application::computePickRay(float x, float y) {
}
QImage Application::renderAvatarBillboard() {
DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject()->bind();
auto primaryFramebuffer = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer();
glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer));
// the "glow" here causes an alpha of one
Glower glower;
@ -2844,7 +3004,7 @@ QImage Application::renderAvatarBillboard() {
QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32);
glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
DependencyManager::get<TextureCache>()->getPrimaryFramebufferObject()->release();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return image;
}
@ -3001,13 +3161,21 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
PerformanceTimer perfTimer("entities");
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::displaySide() ... entities...");
if (renderCollisionHulls) {
_entities.render(RenderArgs::DEBUG_RENDER_MODE, renderSide);
} else if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
_entities.render(RenderArgs::MIRROR_RENDER_MODE, renderSide);
} else {
_entities.render(RenderArgs::DEFAULT_RENDER_MODE, renderSide);
RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE;
RenderArgs::RenderMode renderMode = RenderArgs::DEFAULT_RENDER_MODE;
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {
renderDebugFlags = (RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_HULLS);
}
if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowOwned)) {
renderDebugFlags =
(RenderArgs::DebugFlags) (renderDebugFlags | (int) RenderArgs::RENDER_DEBUG_SIMULATION_OWNERSHIP);
}
if (theCamera.getMode() == CAMERA_MODE_MIRROR) {
renderMode = RenderArgs::MIRROR_RENDER_MODE;
}
_entities.render(renderMode, renderSide, renderDebugFlags);
}
// render JS/scriptable overlays
@ -3036,7 +3204,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
{
DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity());
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity());
DependencyManager::get<DeferredLightingEffect>()->setGlobalAtmosphere(skyStage->getAtmosphere());
PROFILE_RANGE("DeferredLighting");
@ -3251,7 +3419,6 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
void Application::resetSensors() {
DependencyManager::get<Faceshift>()->reset();
DependencyManager::get<Visage>()->reset();
DependencyManager::get<DdeFaceTracker>()->reset();
OculusManager::reset();
@ -3617,6 +3784,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());
scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget());
#ifdef HAVE_RTMIDI
scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance());
#endif
@ -3711,17 +3880,7 @@ bool Application::askToSetAvatarUrl(const QString& url) {
}
// Download the FST file, to attempt to determine its model type
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest = QNetworkRequest(url);
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(networkRequest);
qCDebug(interfaceapp) << "Downloading avatar file at " << url;
QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
QByteArray fstContents = reply->readAll();
delete reply;
QVariantHash fstMapping = FSTReader::readMapping(fstContents);
QVariantHash fstMapping = FSTReader::downloadMapping(url);
FSTReader::ModelType modelType = FSTReader::predictModelType(fstMapping);
@ -3732,26 +3891,27 @@ bool Application::askToSetAvatarUrl(const QString& url) {
QPushButton* bodyButton = NULL;
QPushButton* bodyAndHeadButton = NULL;
QString modelName = fstMapping["name"].toString();
QString message;
QString typeInfo;
switch (modelType) {
case FSTReader::HEAD_MODEL:
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar head?");
message = QString("Would you like to use '") + modelName + QString("' for your avatar head?");
headButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
break;
case FSTReader::BODY_ONLY_MODEL:
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar body?");
message = QString("Would you like to use '") + modelName + QString("' for your avatar body?");
bodyButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
break;
case FSTReader::HEAD_AND_BODY_MODEL:
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar?");
message = QString("Would you like to use '") + modelName + QString("' for your avatar?");
bodyAndHeadButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
break;
default:
message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for some part of your avatar head?");
message = QString("Would you like to use '") + modelName + QString("' for some part of your avatar head?");
headButton = msgBox.addButton(tr("Use for Head"), QMessageBox::ActionRole);
bodyButton = msgBox.addButton(tr("Use for Body"), QMessageBox::ActionRole);
bodyAndHeadButton = msgBox.addButton(tr("Use for Body and Head"), QMessageBox::ActionRole);
@ -3764,34 +3924,18 @@ bool Application::askToSetAvatarUrl(const QString& url) {
msgBox.exec();
if (msgBox.clickedButton() == headButton) {
qCDebug(interfaceapp) << "Chose to use for head: " << url;
_myAvatar->setFaceModelURL(url);
UserActivityLogger::getInstance().changedModel("head", url);
_myAvatar->sendIdentityPacket();
emit faceURLChanged(url);
_myAvatar->useHeadURL(url, modelName);
emit headURLChanged(url, modelName);
} else if (msgBox.clickedButton() == bodyButton) {
qCDebug(interfaceapp) << "Chose to use for body: " << url;
_myAvatar->setSkeletonModelURL(url);
// if the head is empty, reset it to the default head.
if (_myAvatar->getFaceModelURLString().isEmpty()) {
_myAvatar->setFaceModelURL(DEFAULT_HEAD_MODEL_URL);
emit faceURLChanged(DEFAULT_HEAD_MODEL_URL.toString());
UserActivityLogger::getInstance().changedModel("head", DEFAULT_HEAD_MODEL_URL.toString());
}
UserActivityLogger::getInstance().changedModel("skeleton", url);
_myAvatar->sendIdentityPacket();
emit skeletonURLChanged(url);
_myAvatar->useBodyURL(url, modelName);
emit bodyURLChanged(url, modelName);
} else if (msgBox.clickedButton() == bodyAndHeadButton) {
qCDebug(interfaceapp) << "Chose to use for body + head: " << url;
_myAvatar->setFaceModelURL(QString());
_myAvatar->setSkeletonModelURL(url);
UserActivityLogger::getInstance().changedModel("skeleton", url);
_myAvatar->sendIdentityPacket();
emit faceURLChanged(QString());
emit skeletonURLChanged(url);
_myAvatar->useFullAvatarURL(url, modelName);
emit fullAvatarURLChanged(url, modelName);
} else {
qCDebug(interfaceapp) << "Declined to use the avatar: " << url;
}
return true;
}
@ -3881,6 +4025,9 @@ void Application::stopAllScripts(bool restart) {
// stops all current running scripts
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
it != _scriptEnginesHash.constEnd(); it++) {
if (it.value()->isFinished()) {
continue;
}
if (restart && it.value()->isUserLoaded()) {
connect(it.value(), SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&)));
}
@ -4303,10 +4450,8 @@ void Application::checkSkeleton() {
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
_myAvatar->setSkeletonModelURL(DEFAULT_BODY_MODEL_URL);
_myAvatar->sendIdentityPacket();
_myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default");
} else {
_myAvatar->updateCharacterController();
_physicsEngine.setCharacterController(_myAvatar->getCharacterController());
}
}
@ -4328,3 +4473,7 @@ void Application::friendsWindowClosed() {
delete _friendsWindow;
_friendsWindow = NULL;
}
void Application::postLambdaEvent(std::function<void()> f) {
QCoreApplication::postEvent(this, new LambdaEvent(f));
}

View file

@ -21,6 +21,7 @@
#include <QSet>
#include <QStringList>
#include <QUndoStack>
#include <functional>
#include <AbstractScriptingServicesInterface.h>
#include <AbstractViewStateInterface.h>
@ -30,6 +31,7 @@
#include <NetworkPacket.h>
#include <NodeList.h>
#include <OctreeQuery.h>
#include <OffscreenUi.h>
#include <PacketHeaders.h>
#include <PhysicsEngine.h>
#include <ScriptEngine.h>
@ -117,9 +119,9 @@ static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html";
#ifdef Q_OS_WIN
static const UINT UWM_IDENTIFY_INSTANCES =
RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}");
RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME"));
static const UINT UWM_SHOW_APPLICATION =
RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}");
RegisterWindowMessage("UWM_SHOW_APPLICATION_{71123FD6-3DA8-4DC1-9C27-8A12A6250CBA}_" + qgetenv("USERNAME"));
#endif
class Application;
@ -146,11 +148,14 @@ public:
Application(int& argc, char** argv, QElapsedTimer &startup_time);
~Application();
void postLambdaEvent(std::function<void()> f);
void loadScripts();
QString getPreviousScriptLocation();
void setPreviousScriptLocation(const QString& previousScriptLocation);
void clearScriptsBeforeRunning();
void initializeGL();
void initializeUi();
void paintGL();
void resizeGL(int width, int height);
@ -169,7 +174,8 @@ public:
void touchUpdateEvent(QTouchEvent* event);
void wheelEvent(QWheelEvent* event);
void dropEvent(QDropEvent *event);
void dropEvent(QDropEvent* event);
void dragEnterEvent(QDragEnterEvent* event);
bool event(QEvent* event);
bool eventFilter(QObject* object, QEvent* event);
@ -210,6 +216,7 @@ public:
bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; }
FaceTracker* getActiveFaceTracker();
QSystemTrayIcon* getTrayIcon() { return _trayIcon; }
ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; }
Overlays& getOverlays() { return _overlays; }
@ -335,8 +342,9 @@ signals:
void checkBackgroundDownloads();
void domainConnectionRefused(const QString& reason);
void faceURLChanged(const QString& newValue);
void skeletonURLChanged(const QString& newValue);
void headURLChanged(const QString& newValue, const QString& modelName);
void bodyURLChanged(const QString& newValue, const QString& modelName);
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
void beforeAboutToQuit();
@ -382,6 +390,8 @@ public slots:
void setVSyncEnabled();
void resetSensors();
void setActiveFaceTracker();
void aboutApp();
void showEditEntitiesHelp();
@ -390,8 +400,6 @@ public slots:
void notifyPacketVersionMismatch();
void setActiveFaceTracker();
void domainConnectionDenied(const QString& reason);
private slots:

View file

@ -84,7 +84,7 @@ void Bookmarks::persistToFile() {
saveFile.write(data);
}
void Bookmarks::setupMenus(Menu* menubar, QMenu* menu) {
void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
// Add menus/actions
menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation, 0,
this, SLOT(bookmarkLocation()));
@ -192,7 +192,7 @@ void Bookmarks::enableMenuItems(bool enabled) {
}
void Bookmarks::addLocationToMenu(Menu* menubar, QString& name, QString& address) {
QAction* teleportAction = new QAction(_bookmarksMenu);
QAction* teleportAction = _bookmarksMenu->newAction();
teleportAction->setData(address);
connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));

View file

@ -19,6 +19,7 @@
class QAction;
class QMenu;
class Menu;
class MenuWrapper;
class Bookmarks: public QObject {
Q_OBJECT
@ -26,7 +27,7 @@ class Bookmarks: public QObject {
public:
Bookmarks();
void setupMenus(Menu* menubar, QMenu* menu);
void setupMenus(Menu* menubar, MenuWrapper* menu);
private slots:
void bookmarkLocation();
@ -36,7 +37,7 @@ private slots:
private:
QVariantMap _bookmarks; // { name: address, ... }
QPointer<QMenu> _bookmarksMenu;
QPointer<MenuWrapper> _bookmarksMenu;
QPointer<QAction> _deleteBookmarksAction;
const QString BOOKMARKS_FILENAME = "bookmarks.json";

View file

@ -55,7 +55,6 @@ Camera::Camera() :
_farClip(DEFAULT_FAR_CLIP), // default
_hmdPosition(),
_hmdRotation(),
_scale(1.0f),
_isKeepLookingAt(false),
_lookingAt(0.0f, 0.0f, 0.0f)
{
@ -94,8 +93,8 @@ void Camera::setHmdRotation(const glm::quat& hmdRotation) {
}
float Camera::getFarClip() const {
return (_scale * _farClip < std::numeric_limits<int16_t>::max())
? _scale * _farClip
return (_farClip < std::numeric_limits<int16_t>::max())
? _farClip
: std::numeric_limits<int16_t>::max() - 1;
}

View file

@ -54,7 +54,6 @@ public:
void setFarClip(float f);
void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; }
void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; }
void setScale(const float s) { _scale = s; }
glm::quat getRotation() const { return _rotation * _hmdRotation; }
const glm::vec3& getHmdPosition() const { return _hmdPosition; }
@ -63,11 +62,10 @@ public:
CameraMode getMode() const { return _mode; }
float getFieldOfView() const { return _fieldOfView; }
float getAspectRatio() const { return _aspectRatio; }
float getNearClip() const { return _scale * _nearClip; }
float getNearClip() const { return _nearClip; }
float getFarClip() const;
const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; }
const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; }
float getScale() const { return _scale; }
public slots:
QString getModeString() const;
void setModeString(const QString& mode);
@ -107,7 +105,6 @@ private:
glm::quat _rotation;
glm::vec3 _hmdPosition;
glm::quat _hmdRotation;
float _scale;
bool _isKeepLookingAt;
glm::vec3 _lookingAt;
};

View file

@ -18,6 +18,7 @@
#include <UUID.h>
#include "DiscoverabilityManager.h"
#include "Menu.h"
const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::All;
@ -96,4 +97,32 @@ void DiscoverabilityManager::setDiscoverabilityMode(Discoverability::Mode discov
emit discoverabilityModeChanged(discoverabilityMode);
}
}
}
void DiscoverabilityManager::setVisibility() {
Menu* menu = Menu::getInstance();
if (menu->isOptionChecked(MenuOption::VisibleToEveryone)) {
this->setDiscoverabilityMode(Discoverability::All);
} else if (menu->isOptionChecked(MenuOption::VisibleToFriends)) {
this->setDiscoverabilityMode(Discoverability::Friends);
} else if (menu->isOptionChecked(MenuOption::VisibleToNoOne)) {
this->setDiscoverabilityMode(Discoverability::None);
} else {
qDebug() << "ERROR DiscoverabilityManager::setVisibility() called with unrecognized value.";
}
}
void DiscoverabilityManager::visibilityChanged(Discoverability::Mode discoverabilityMode) {
Menu* menu = Menu::getInstance();
if (discoverabilityMode == Discoverability::All) {
menu->setIsOptionChecked(MenuOption::VisibleToEveryone, true);
} else if (discoverabilityMode == Discoverability::Friends) {
menu->setIsOptionChecked(MenuOption::VisibleToFriends, true);
} else if (discoverabilityMode == Discoverability::None) {
menu->setIsOptionChecked(MenuOption::VisibleToNoOne, true);
} else {
qDebug() << "ERROR DiscoverabilityManager::visibilityChanged() called with unrecognized value.";
}
}

View file

@ -36,6 +36,9 @@ public slots:
Discoverability::Mode getDiscoverabilityMode() { return static_cast<Discoverability::Mode>(_mode.get()); }
void setDiscoverabilityMode(Discoverability::Mode discoverabilityMode);
void setVisibility();
void visibilityChanged(Discoverability::Mode discoverabilityMode);
signals:
void discoverabilityModeChanged(Discoverability::Mode discoverabilityMode);

View file

@ -82,30 +82,6 @@ void GLCanvas::resizeGL(int width, int height) {
Application::getInstance()->resizeGL(width, height);
}
void GLCanvas::keyPressEvent(QKeyEvent* event) {
Application::getInstance()->keyPressEvent(event);
}
void GLCanvas::keyReleaseEvent(QKeyEvent* event) {
Application::getInstance()->keyReleaseEvent(event);
}
void GLCanvas::focusOutEvent(QFocusEvent* event) {
Application::getInstance()->focusOutEvent(event);
}
void GLCanvas::mouseMoveEvent(QMouseEvent* event) {
Application::getInstance()->mouseMoveEvent(event);
}
void GLCanvas::mousePressEvent(QMouseEvent* event) {
Application::getInstance()->mousePressEvent(event);
}
void GLCanvas::mouseReleaseEvent(QMouseEvent* event) {
Application::getInstance()->mouseReleaseEvent(event);
}
void GLCanvas::activeChanged(Qt::ApplicationState state) {
switch (state) {
case Qt::ApplicationActive:
@ -139,6 +115,7 @@ void GLCanvas::throttleRender() {
OculusManager::beginFrameTiming();
}
makeCurrent();
Application::getInstance()->paintGL();
swapBuffers();
@ -151,40 +128,37 @@ void GLCanvas::throttleRender() {
int updateTime = 0;
bool GLCanvas::event(QEvent* event) {
switch (event->type()) {
case QEvent::MouseMove:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::FocusIn:
case QEvent::FocusOut:
case QEvent::Resize:
case QEvent::TouchBegin:
Application::getInstance()->touchBeginEvent(static_cast<QTouchEvent*>(event));
event->accept();
return true;
case QEvent::TouchEnd:
Application::getInstance()->touchEndEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::TouchUpdate:
Application::getInstance()->touchUpdateEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::Wheel:
case QEvent::DragEnter:
case QEvent::Drop:
if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) {
return true;
}
break;
case QEvent::Paint:
// Ignore paint events that occur after we've decided to quit
if (Application::getInstance()->isAboutToQuit()) {
return true;
}
break;
default:
break;
}
return QGLWidget::event(event);
}
void GLCanvas::wheelEvent(QWheelEvent* event) {
Application::getInstance()->wheelEvent(event);
}
void GLCanvas::dragEnterEvent(QDragEnterEvent* event) {
const QMimeData* mimeData = event->mimeData();
foreach (QUrl url, mimeData->urls()) {
auto urlString = url.toString();
if (Application::getInstance()->canAcceptURL(urlString)) {
event->acceptProposedAction();
break;
}
}
}
void GLCanvas::dropEvent(QDropEvent* event) {
Application::getInstance()->dropEvent(event);
}
// Pressing Alt (and Meta) key alone activates the menubar because its style inherits the
// SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to

View file

@ -40,23 +40,8 @@ protected:
virtual void initializeGL();
virtual void paintGL();
virtual void resizeGL(int width, int height);
virtual void keyPressEvent(QKeyEvent* event);
virtual void keyReleaseEvent(QKeyEvent* event);
virtual void focusOutEvent(QFocusEvent* event);
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
virtual bool event(QEvent* event);
virtual void wheelEvent(QWheelEvent* event);
virtual void dragEnterEvent(QDragEnterEvent *event);
virtual void dropEvent(QDropEvent* event);
private slots:
void activeChanged(Qt::ApplicationState state);
void throttleRender();

View file

@ -55,6 +55,10 @@ void MainWindow::saveGeometry() {
}
}
void MainWindow::closeEvent(QCloseEvent* event) {
qApp->quit();
}
void MainWindow::moveEvent(QMoveEvent* event) {
emit windowGeometryChanged(QRect(event->pos(), size()));
QMainWindow::moveEvent(event);

View file

@ -30,6 +30,7 @@ signals:
void windowShown(bool shown);
protected:
virtual void closeEvent(QCloseEvent* event);
virtual void moveEvent(QMoveEvent* event);
virtual void resizeEvent(QResizeEvent* event);
virtual void showEvent(QShowEvent* event);

View file

@ -20,6 +20,7 @@
#include <PathUtils.h>
#include <SettingHandle.h>
#include <UserActivityLogger.h>
#include <VrMenu.h>
#include "Application.h"
#include "AccountManager.h"
@ -30,7 +31,6 @@
#include "devices/Faceshift.h"
#include "devices/RealSense.h"
#include "devices/SixenseManager.h"
#include "devices/Visage.h"
#include "MainWindow.h"
#include "scripting/MenuScriptingInterface.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
@ -53,7 +53,6 @@ Menu* Menu::getInstance() {
if (!_instance) {
qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu.");
_instance = new Menu();
}
@ -63,8 +62,7 @@ Menu* Menu::getInstance() {
}
Menu::Menu() {
QMenu* fileMenu = addMenu("File");
MenuWrapper * fileMenu = addMenu("File");
#ifdef Q_OS_MAC
addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole);
#endif
@ -97,7 +95,7 @@ Menu::Menu() {
addActionToQMenuAndActionHash(fileMenu,
MenuOption::AddressBar,
Qt::Key_Enter,
Qt::CTRL | Qt::Key_L,
dialogsManager.data(),
SLOT(toggleAddressBar()));
auto addressManager = DependencyManager::get<AddressManager>();
@ -114,7 +112,7 @@ Menu::Menu() {
QAction::QuitRole);
QMenu* editMenu = addMenu("Edit");
MenuWrapper* editMenu = addMenu("Edit");
QUndoStack* undoStack = qApp->getUndoStack();
QAction* undoAction = undoStack->createUndoAction(editMenu);
@ -137,7 +135,7 @@ Menu::Menu() {
addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0,
dialogsManager.data(), SLOT(editAnimations()));
QMenu* toolsMenu = addMenu("Tools");
MenuWrapper* toolsMenu = addMenu("Tools");
addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S,
dialogsManager.data(), SLOT(showScriptEditor()));
@ -151,33 +149,34 @@ Menu::Menu() {
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
#endif
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, Qt::Key_Backslash,
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
0, // QML Qt::Key_Backslash,
dialogsManager.data(), SLOT(showIRCLink()));
addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0,
qApp, SLOT(showFriendsWindow()));
QMenu* visibilityMenu = toolsMenu->addMenu("I Am Visible To");
MenuWrapper* visibilityMenu = toolsMenu->addMenu("I Am Visible To");
{
QActionGroup* visibilityGroup = new QActionGroup(toolsMenu);
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
QAction* visibleToEveryone = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToEveryone,
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::All,
this, SLOT(setVisibility()));
discoverabilityManager.data(), SLOT(setVisibility()));
visibilityGroup->addAction(visibleToEveryone);
QAction* visibleToFriends = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToFriends,
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends,
this, SLOT(setVisibility()));
discoverabilityManager.data(), SLOT(setVisibility()));
visibilityGroup->addAction(visibleToFriends);
QAction* visibleToNoOne = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToNoOne,
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::None,
this, SLOT(setVisibility()));
discoverabilityManager.data(), SLOT(setVisibility()));
visibilityGroup->addAction(visibleToNoOne);
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
this, &Menu::visibilityChanged);
discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged);
}
addActionToQMenuAndActionHash(toolsMenu,
@ -194,30 +193,30 @@ Menu::Menu() {
addActionToQMenuAndActionHash(toolsMenu,
MenuOption::ResetSensors,
Qt::Key_Apostrophe,
0, // QML Qt::Key_Apostrophe,
qApp,
SLOT(resetSensors()));
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
qApp, SLOT(packageModel()));
QMenu* avatarMenu = addMenu("Avatar");
MenuWrapper* avatarMenu = addMenu("Avatar");
QObject* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
QMenu* avatarSizeMenu = avatarMenu->addMenu("Size");
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::IncreaseAvatarSize,
Qt::Key_Plus,
0, // QML Qt::Key_Plus,
avatar,
SLOT(increaseSize()));
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::DecreaseAvatarSize,
Qt::Key_Minus,
0, // QML Qt::Key_Minus,
avatar,
SLOT(decreaseSize()));
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::ResetAvatarSize,
Qt::Key_Equal,
0, // QML Qt::Key_Equal,
avatar,
SLOT(resetSize()));
@ -233,35 +232,39 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false,
avatar, SLOT(updateMotionBehavior()));
QMenu* viewMenu = addMenu("View");
MenuWrapper* viewMenu = addMenu("View");
addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::Fullscreen,
#ifdef Q_OS_MAC
addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::Fullscreen,
Qt::CTRL | Qt::META | Qt::Key_F,
false,
qApp,
SLOT(setFullscreen(bool)));
#else
addCheckableActionToQMenuAndActionHash(viewMenu,
MenuOption::Fullscreen,
Qt::CTRL | Qt::Key_F,
#endif
false,
qApp,
SLOT(setFullscreen(bool)));
#endif
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true,
qApp,SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false,
qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, Qt::META | Qt::Key_H,
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson,
0, // QML Qt::Key_P,
true, qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
0, //QML Qt::SHIFT | Qt::Key_H,
true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror,
0, // QML Qt::Key_H,
false, qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools,
#ifdef Q_OS_MAC
Qt::META | Qt::Key_H,
#else
Qt::CTRL | Qt::Key_H,
#endif
false,
dialogsManager.data(),
SLOT(hmdTools(bool)));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0,
false,
qApp,
@ -273,7 +276,7 @@ Menu::Menu() {
SLOT(setEnable3DTVMode(bool)));
QMenu* nodeBordersMenu = viewMenu->addMenu("Server Borders");
MenuWrapper* nodeBordersMenu = viewMenu->addMenu("Server Borders");
NodeBounds& nodeBounds = qApp->getNodeBoundsDisplay();
addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersEntityNodes,
Qt::CTRL | Qt::SHIFT | Qt::Key_1, false,
@ -282,23 +285,26 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, qApp, SLOT(toggleLogDialog()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats);
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
qApp, SLOT(toggleLogDialog()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0,
dialogsManager.data(), SLOT(bandwidthDetails()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0,
dialogsManager.data(), SLOT(octreeStatsDetails()));
QMenu* developerMenu = addMenu("Developer");
MenuWrapper* developerMenu = addMenu("Developer");
QMenu* renderOptionsMenu = developerMenu->addMenu("Render");
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true);
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
0, // QML Qt::SHIFT | Qt::Key_A,
true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges);
QMenu* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu);
ambientLightGroup->setExclusive(true);
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLightGlobal, 0, true));
@ -313,14 +319,14 @@ Menu::Menu() {
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false));
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false));
QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows");
MenuWrapper* shadowMenu = renderOptionsMenu->addMenu("Shadows");
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false));
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false));
{
QMenu* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate);
MenuWrapper* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate);
QActionGroup* framerateGroup = new QActionGroup(framerateMenu);
framerateGroup->setExclusive(true);
framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true));
@ -337,7 +343,7 @@ Menu::Menu() {
}
QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu);
resolutionGroup->setExclusive(true);
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true));
@ -346,17 +352,20 @@ Menu::Menu() {
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
0, // QML Qt::Key_Asterisk,
true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true,
DependencyManager::get<GlowEffect>().data(), SLOT(toggleGlowEffect(bool)));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false);
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L,
dialogsManager.data(), SLOT(lodTools()));
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
0, // QML Qt::SHIFT | Qt::Key_L,
dialogsManager.data(), SLOT(lodTools()));
QMenu* avatarDebugMenu = developerMenu->addMenu("Avatar");
MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar");
QMenu* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking");
MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking");
{
QActionGroup* faceTrackerGroup = new QActionGroup(avatarDebugMenu);
@ -371,19 +380,24 @@ Menu::Menu() {
qApp, SLOT(setActiveFaceTracker()));
faceTrackerGroup->addAction(faceshiftFaceTracker);
#endif
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::DDEFaceRegression,
#ifdef HAVE_DDE
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera,
0, false,
qApp, SLOT(setActiveFaceTracker()));
faceTrackerGroup->addAction(ddeFaceTracker);
#ifdef HAVE_VISAGE
QAction* visageFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::Visage,
0, false,
qApp, SLOT(setActiveFaceTracker()));
faceTrackerGroup->addAction(visageFaceTracker);
#endif
}
#ifdef HAVE_DDE
faceTrackingMenu->addSeparator();
QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true);
useAudioForMouth->setVisible(false);
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);
ddeFiltering->setVisible(false);
#endif
auto avatarManager = DependencyManager::get<AvatarManager>();
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false,
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSkeletonCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes);
@ -391,14 +405,14 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
QMenu* handOptionsMenu = developerMenu->addMenu("Hands");
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
QMenu* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
MenuWrapper* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
#ifdef __APPLE__
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
MenuOption::SixenseEnabled,
@ -421,7 +435,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true);
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false);
QMenu* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
#ifdef HAVE_RSSDK
@ -430,7 +444,7 @@ Menu::Menu() {
RealSense::getInstance(), SLOT(loadRSSDKFile()));
#endif
QMenu* networkMenu = developerMenu->addMenu("Network");
MenuWrapper* networkMenu = developerMenu->addMenu("Network");
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false);
addCheckableActionToQMenuAndActionHash(networkMenu,
MenuOption::DisableActivityLogger,
@ -443,8 +457,8 @@ Menu::Menu() {
addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0,
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
QMenu* timingMenu = developerMenu->addMenu("Timing and Stats");
QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer");
MenuWrapper* timingMenu = developerMenu->addMenu("Timing and Stats");
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
@ -460,7 +474,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
auto audioIO = DependencyManager::get<AudioClient>();
QMenu* audioDebugMenu = developerMenu->addMenu("Audio");
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction,
0,
true,
@ -471,8 +485,6 @@ Menu::Menu() {
audioIO.data(), SLOT(toggleServerEcho()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false,
audioIO.data(), SLOT(toggleLocalEcho()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::StereoAudio, 0, false,
audioIO.data(), SLOT(toggleStereoInput()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteAudio,
Qt::CTRL | Qt::Key_M,
false,
@ -483,34 +495,10 @@ Menu::Menu() {
0,
audioIO.data(),
SLOT(sendMuteEnvironmentPacket()));
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioSourceInject,
0,
false,
audioIO.data(),
SLOT(toggleAudioSourceInject()));
QMenu* audioSourceMenu = audioDebugMenu->addMenu("Generated Audio Source");
{
QAction *pinkNoise = addCheckableActionToQMenuAndActionHash(audioSourceMenu, MenuOption::AudioSourcePinkNoise,
0,
false,
audioIO.data(),
SLOT(selectAudioSourcePinkNoise()));
QAction *sine440 = addCheckableActionToQMenuAndActionHash(audioSourceMenu, MenuOption::AudioSourceSine440,
0,
true,
audioIO.data(),
SLOT(selectAudioSourceSine440()));
QActionGroup* audioSourceGroup = new QActionGroup(audioSourceMenu);
audioSourceGroup->addAction(pinkNoise);
audioSourceGroup->addAction(sine440);
}
auto scope = DependencyManager::get<AudioScope>();
QMenu* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope");
MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope");
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope,
Qt::CTRL | Qt::Key_P, false,
scope.data(),
@ -559,7 +547,12 @@ Menu::Menu() {
statsRenderer.data(),
SLOT(toggleShowInjectedStreams()));
QMenu* helpMenu = addMenu("Help");
MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics");
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned);
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls);
MenuWrapper* helpMenu = addMenu("Help");
addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp()));
#ifndef Q_OS_MAC
@ -605,7 +598,7 @@ void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& setting
settings.endGroup();
}
void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName, int menuItemLocation) {
void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, int menuItemLocation) {
QAction* actionBefore = NULL;
if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) {
actionBefore = destinationMenu->actions()[menuItemLocation];
@ -625,7 +618,7 @@ void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString&
}
}
QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
const QString& actionName,
const QKeySequence& shortcut,
const QObject* receiver,
@ -662,7 +655,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
return action;
}
QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
QAction* action,
const QString& actionName,
const QKeySequence& shortcut,
@ -697,7 +690,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu,
return action;
}
QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
const QString& actionName,
const QKeySequence& shortcut,
const bool checked,
@ -713,7 +706,7 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
return action;
}
void Menu::removeAction(QMenu* menu, const QString& actionName) {
void Menu::removeAction(MenuWrapper* menu, const QString& actionName) {
menu->removeAction(_actionHash.value(actionName));
_actionHash.remove(actionName);
}
@ -752,7 +745,7 @@ QAction* Menu::getActionForOption(const QString& menuOption) {
return _actionHash.value(menuOption);
}
QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) {
QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) {
QList<QAction*> menuActions;
if (menu) {
menuActions = menu->actions();
@ -761,6 +754,7 @@ QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) {
}
foreach (QAction* menuAction, menuActions) {
QString actionText = menuAction->text();
if (menuName == menuAction->text()) {
return menuAction;
}
@ -768,18 +762,18 @@ QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) {
return NULL;
}
QMenu* Menu::getSubMenuFromName(const QString& menuName, QMenu* menu) {
MenuWrapper* Menu::getSubMenuFromName(const QString& menuName, MenuWrapper* menu) {
QAction* action = getActionFromName(menuName, menu);
if (action) {
return action->menu();
return MenuWrapper::fromMenu(action->menu());
}
return NULL;
}
QMenu* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) {
MenuWrapper* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) {
QStringList menuTree = menuName.split(">");
QMenu* parent = NULL;
QMenu* menu = NULL;
MenuWrapper* parent = NULL;
MenuWrapper* menu = NULL;
foreach (QString menuTreePart, menuTree) {
parent = menu;
finalMenuPart = menuTreePart.trimmed();
@ -791,10 +785,10 @@ QMenu* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) {
return parent;
}
QMenu* Menu::getMenu(const QString& menuName) {
MenuWrapper* Menu::getMenu(const QString& menuName) {
QStringList menuTree = menuName.split(">");
QMenu* parent = NULL;
QMenu* menu = NULL;
MenuWrapper* parent = NULL;
MenuWrapper* menu = NULL;
int item = 0;
foreach (QString menuTreePart, menuTree) {
menu = getSubMenuFromName(menuTreePart.trimmed(), parent);
@ -809,19 +803,19 @@ QMenu* Menu::getMenu(const QString& menuName) {
QAction* Menu::getMenuAction(const QString& menuName) {
QStringList menuTree = menuName.split(">");
QMenu* parent = NULL;
MenuWrapper* parent = NULL;
QAction* action = NULL;
foreach (QString menuTreePart, menuTree) {
action = getActionFromName(menuTreePart.trimmed(), parent);
if (!action) {
break;
}
parent = action->menu();
parent = MenuWrapper::fromMenu(action->menu());
}
return action;
}
int Menu::findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem) {
int Menu::findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem) {
int position = 0;
foreach(QAction* action, menu->actions()) {
if (action->text() == searchMenuItem) {
@ -832,7 +826,7 @@ int Menu::findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem) {
return UNSPECIFIED_POSITION; // not found
}
int Menu::positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition) {
int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition) {
QList<QAction*> menuActions = menu->actions();
if (requestedPosition > 1 && requestedPosition < menuActions.size()) {
QAction* beforeRequested = menuActions[requestedPosition - 1];
@ -844,15 +838,15 @@ int Menu::positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition) {
}
QMenu* Menu::addMenu(const QString& menuName) {
MenuWrapper* Menu::addMenu(const QString& menuName) {
QStringList menuTree = menuName.split(">");
QMenu* addTo = NULL;
QMenu* menu = NULL;
MenuWrapper* addTo = NULL;
MenuWrapper* menu = NULL;
foreach (QString menuTreePart, menuTree) {
menu = getSubMenuFromName(menuTreePart.trimmed(), addTo);
if (!menu) {
if (!addTo) {
menu = QMenuBar::addMenu(menuTreePart.trimmed());
menu = new MenuWrapper(QMenuBar::addMenu(menuTreePart.trimmed()));
} else {
menu = addTo->addMenu(menuTreePart.trimmed());
}
@ -870,7 +864,7 @@ void Menu::removeMenu(const QString& menuName) {
// only proceed if the menu actually exists
if (action) {
QString finalMenuPart;
QMenu* parent = getMenuParent(menuName, finalMenuPart);
MenuWrapper* parent = getMenuParent(menuName, finalMenuPart);
if (parent) {
parent->removeAction(action);
} else {
@ -892,14 +886,14 @@ bool Menu::menuExists(const QString& menuName) {
}
void Menu::addSeparator(const QString& menuName, const QString& separatorName) {
QMenu* menuObj = getMenu(menuName);
MenuWrapper* menuObj = getMenu(menuName);
if (menuObj) {
addDisabledActionAndSeparator(menuObj, separatorName);
}
}
void Menu::removeSeparator(const QString& menuName, const QString& separatorName) {
QMenu* menu = getMenu(menuName);
MenuWrapper* menu = getMenu(menuName);
bool separatorRemoved = false;
if (menu) {
int textAt = findPositionOfMenuItem(menu, separatorName);
@ -922,7 +916,7 @@ void Menu::removeSeparator(const QString& menuName, const QString& separatorName
}
void Menu::addMenuItem(const MenuItemProperties& properties) {
QMenu* menuObj = getMenu(properties.menuName);
MenuWrapper* menuObj = getMenu(properties.menuName);
if (menuObj) {
QShortcut* shortcut = NULL;
if (!properties.shortcutKeySequence.isEmpty()) {
@ -963,7 +957,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
}
void Menu::removeMenuItem(const QString& menu, const QString& menuitem) {
QMenu* menuObj = getMenu(menu);
MenuWrapper* menuObj = getMenu(menu);
if (menuObj) {
removeAction(menuObj, menuitem);
QMenuBar::repaint();
@ -978,28 +972,62 @@ bool Menu::menuItemExists(const QString& menu, const QString& menuitem) {
return false;
};
void Menu::setVisibility() {
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
if (Menu::getInstance()->isOptionChecked(MenuOption::VisibleToEveryone)) {
discoverabilityManager->setDiscoverabilityMode(Discoverability::All);
} else if (Menu::getInstance()->isOptionChecked(MenuOption::VisibleToFriends)) {
discoverabilityManager->setDiscoverabilityMode(Discoverability::Friends);
} else if (Menu::getInstance()->isOptionChecked(MenuOption::VisibleToNoOne)) {
discoverabilityManager->setDiscoverabilityMode(Discoverability::None);
} else {
qCDebug(interfaceapp) << "ERROR Menu::setVisibility() called with unrecognized value.";
}
MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) {
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->addMenu(menu);
});
_backMap[menu] = this;
}
void Menu::visibilityChanged(Discoverability::Mode discoverabilityMode) {
if (discoverabilityMode == Discoverability::All) {
setIsOptionChecked(MenuOption::VisibleToEveryone, true);
} else if (discoverabilityMode == Discoverability::Friends) {
setIsOptionChecked(MenuOption::VisibleToFriends, true);
} else if (discoverabilityMode == Discoverability::None) {
setIsOptionChecked(MenuOption::VisibleToNoOne, true);
} else {
qCDebug(interfaceapp) << "ERROR Menu::visibilityChanged() called with unrecognized value.";
}
QList<QAction*> MenuWrapper::actions() {
return _realMenu->actions();
}
MenuWrapper* MenuWrapper::addMenu(const QString& menuName) {
return new MenuWrapper(_realMenu->addMenu(menuName));
}
void MenuWrapper::setEnabled(bool enabled) {
_realMenu->setEnabled(enabled);
}
void MenuWrapper::addSeparator() {
_realMenu->addSeparator();
}
void MenuWrapper::addAction(QAction* action) {
_realMenu->addAction(action);
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->addAction(_realMenu, action);
});
}
QAction* MenuWrapper::addAction(const QString& menuName) {
QAction* action = _realMenu->addAction(menuName);
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->addAction(_realMenu, action);
});
return action;
}
QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) {
QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut);
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->addAction(_realMenu, action);
});
return action;
}
void MenuWrapper::removeAction(QAction* action) {
_realMenu->removeAction(action);
}
void MenuWrapper::insertAction(QAction* before, QAction* action) {
_realMenu->insertAction(before, action);
VrMenu::executeOrQueue([=](VrMenu* vrMenu) {
vrMenu->insertAction(before, action);
});
}
QHash<QMenu*, MenuWrapper*> MenuWrapper::_backMap;

View file

@ -25,6 +25,35 @@
class Settings;
class MenuWrapper : public QObject {
public:
QList<QAction*> actions();
MenuWrapper* addMenu(const QString& menuName);
void setEnabled(bool enabled = true);
void addSeparator();
void addAction(QAction* action);
QAction* addAction(const QString& menuName);
void insertAction(QAction* before, QAction* menuName);
QAction* addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0);
void removeAction(QAction* action);
QAction* newAction() {
return new QAction(_realMenu);
}
private:
MenuWrapper(QMenu* menu);
static MenuWrapper* fromMenu(QMenu* menu) {
return _backMap[menu];
}
QMenu* const _realMenu;
static QHash<QMenu*, MenuWrapper*> _backMap;
friend class Menu;
};
class Menu : public QMenuBar {
Q_OBJECT
public:
@ -33,29 +62,29 @@ public:
void loadSettings();
void saveSettings();
QMenu* getMenu(const QString& menuName);
MenuWrapper* getMenu(const QString& menuName);
void triggerOption(const QString& menuOption);
QAction* getActionForOption(const QString& menuOption);
QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu,
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
const QString& actionName,
const QKeySequence& shortcut = 0,
const QObject* receiver = NULL,
const char* member = NULL,
QAction::MenuRole role = QAction::NoRole,
int menuItemLocation = UNSPECIFIED_POSITION);
QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu,
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
QAction* action,
const QString& actionName = QString(),
const QKeySequence& shortcut = 0,
QAction::MenuRole role = QAction::NoRole,
int menuItemLocation = UNSPECIFIED_POSITION);
void removeAction(QMenu* menu, const QString& actionName);
void removeAction(MenuWrapper* menu, const QString& actionName);
public slots:
QMenu* addMenu(const QString& menuName);
MenuWrapper* addMenu(const QString& menuName);
void removeMenu(const QString& menuName);
bool menuExists(const QString& menuName);
void addSeparator(const QString& menuName, const QString& separatorName);
@ -66,9 +95,6 @@ public slots:
bool isOptionChecked(const QString& menuOption) const;
void setIsOptionChecked(const QString& menuOption, bool isChecked);
private slots:
void setVisibility();
private:
static Menu* _instance;
Menu();
@ -80,10 +106,10 @@ private:
void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings);
/// helper method to have separators with labels that are also compatible with OS X
void addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName,
void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName,
int menuItemLocation = UNSPECIFIED_POSITION);
QAction* addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
const QString& actionName,
const QKeySequence& shortcut = 0,
const bool checked = false,
@ -91,15 +117,13 @@ private:
const char* member = NULL,
int menuItemLocation = UNSPECIFIED_POSITION);
QAction* getActionFromName(const QString& menuName, QMenu* menu);
QMenu* getSubMenuFromName(const QString& menuName, QMenu* menu);
QMenu* getMenuParent(const QString& menuName, QString& finalMenuPart);
QAction* getActionFromName(const QString& menuName, MenuWrapper* menu);
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart);
QAction* getMenuAction(const QString& menuName);
int findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem);
int positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition);
void visibilityChanged(Discoverability::Mode discoverabilityMode);
int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem);
int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition);
QHash<QString, QAction*> _actionHash;
};
@ -123,9 +147,7 @@ namespace MenuOption {
const QString AudioScopeTwentyFrames = "Twenty";
const QString AudioStats = "Audio Stats";
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
const QString AudioSourceInject = "Generated Audio";
const QString AudioSourcePinkNoise = "Pink Noise";
const QString AudioSourceSine440 = "Sine 440hz";
const QString AvatarReceiveStats = "Show Receive Stats";
const QString BandwidthDetails = "Bandwidth Details";
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
const QString BookmarkLocation = "Bookmark Location";
@ -138,7 +160,6 @@ namespace MenuOption {
const QString ControlWithSpeech = "Control With Speech";
const QString CopyAddress = "Copy Address to Clipboard";
const QString CopyPath = "Copy Path to Clipboard";
const QString DDEFaceRegression = "DDE Face Regression";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
const QString DeleteBookmark = "Delete Bookmark...";
const QString DisableActivityLogger = "Disable Activity Logger";
@ -194,7 +215,10 @@ namespace MenuOption {
const QString OctreeStats = "Entity Statistics";
const QString OffAxisProjection = "Off-Axis Projection";
const QString OnlyDisplayTopTen = "Only Display Top Ten";
const QString PackageModel = "Package Model...";
const QString Pair = "Pair";
const QString PhysicsShowOwned = "Highlight Simulation Ownership";
const QString PhysicsShowHulls = "Draw Collision Hulls";
const QString PipelineWarnings = "Log Render Pipeline Warnings";
const QString Preferences = "Preferences...";
const QString Quit = "Quit";
@ -244,15 +268,15 @@ namespace MenuOption {
const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations";
const QString Stars = "Stars";
const QString Stats = "Stats";
const QString StereoAudio = "Stereo Audio (disables spatial sound)";
const QString StopAllScripts = "Stop All Scripts";
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
const QString TestPing = "Test Ping";
const QString ToolWindow = "Tool Window";
const QString TransmitterDrive = "Transmitter Drive";
const QString TurnWithHead = "Turn using Head";
const QString PackageModel = "Package Model...";
const QString Visage = "Visage";
const QString UseAudioForMouth = "Use Audio for Mouth";
const QString UseCamera = "Use Camera";
const QString VelocityFilter = "Velocity Filter";
const QString VisibleToEveryone = "Everyone";
const QString VisibleToFriends = "Friends";
const QString VisibleToNoOne = "No one";

View file

@ -50,7 +50,7 @@ public:
TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin);
const QString& getLocalPath() { return _localPath; }
const QString& getFullPath() { return _fullPath; };
const ScriptOrigin getOrigin() { return _origin; };
ScriptOrigin getOrigin() { return _origin; };
private:
QString _localPath;

View file

@ -581,7 +581,8 @@ void Avatar::renderBillboard() {
glm::vec2 texCoordTopLeft(0.0f, 0.0f);
glm::vec2 texCoordBottomRight(1.0f, 1.0f);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
glPopMatrix();
@ -709,11 +710,24 @@ void Avatar::renderDisplayName() {
glm::vec4(0.2f, 0.2f, 0.2f, _displayNameAlpha * DISPLAYNAME_BACKGROUND_ALPHA / DISPLAYNAME_ALPHA));
glm::vec4 color(0.93f, 0.93f, 0.93f, _displayNameAlpha);
// optionally render timing stats for this avatar with the display name
QString renderedDisplayName = _displayName;
if (DependencyManager::get<AvatarManager>()->shouldShowReceiveStats()) {
const float BYTES_PER_KILOBYTE = 1000.0f;
float kilobytesPerSecond = getAverageBytesReceivedPerSecond() / BYTES_PER_KILOBYTE;
renderedDisplayName += QString(" - (%1 KBps, %2 Hz)")
.arg(QString::number(kilobytesPerSecond, 'f', 2))
.arg(getReceiveRate());
}
QByteArray ba = _displayName.toLocal8Bit();
const char* text = ba.data();
glDisable(GL_POLYGON_OFFSET_FILL);
textRenderer(DISPLAYNAME)->draw(text_x, text_y, text, color);
textRenderer(DISPLAYNAME)->draw(text_x, text_y, renderedDisplayName, color);
glPopMatrix();
@ -1058,3 +1072,20 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
}
// virtual
void Avatar::rebuildSkeletonBody() {
/* TODO: implement this and remove override from MyAvatar (when we have AvatarMotionStates working)
if (_motionState) {
// compute localAABox
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
float radius = capsule.getRadius();
float height = 2.0f * (capsule.getHalfHeight() + radius);
glm::vec3 corner(-radius, -0.5f * height, -radius);
corner += _skeletonModel.getBoundingShapeOffset();
glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
//_characterController.setLocalBoundingBox(corner, scale);
_motionState->setBoundingBox(corner, scale);
}
*/
}

View file

@ -162,6 +162,8 @@ public:
// (otherwise floating point error will cause problems at large positions).
void applyPositionDelta(const glm::vec3& delta);
virtual void rebuildSkeletonBody();
signals:
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
@ -228,6 +230,8 @@ private:
static int _jointConesID;
int _voiceSphereID;
//AvatarMotionState* _motionState = nullptr;
};
#endif // hifi_Avatar_h

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