mirror of
https://github.com/lubosz/overte.git
synced 2025-04-14 10:08:20 +02:00
Merge branch 'console' of github.com:birarda/hifi into console-logging
This commit is contained in:
commit
0f2b9744d1
206 changed files with 5568 additions and 2417 deletions
1
CMakeGraphvizOptions.cmake
Normal file
1
CMakeGraphvizOptions.cmake
Normal file
|
@ -0,0 +1 @@
|
|||
set(GRAPHVIZ_EXTERNAL_LIBS FALSE)
|
|
@ -30,20 +30,28 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
|||
|
||||
if (WIN32)
|
||||
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
|
||||
# set path for Microsoft SDKs
|
||||
# if get build error about missing 'glu32' this path is likely wrong
|
||||
# Uncomment the line with 8.1 if running Windows 8.1
|
||||
|
||||
if (NOT WINDOW_SDK_PATH)
|
||||
set(DEBUG_DISCOVERED_SDK_PATH TRUE)
|
||||
endif()
|
||||
|
||||
# sets path for Microsoft SDKs
|
||||
# if you get build error about missing 'glu32' this path is likely wrong
|
||||
if (MSVC10)
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 ")
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1 " CACHE PATH "Windows SDK PATH")
|
||||
elseif (MSVC12)
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(WINDOW_SDK_FOLDER "x64")
|
||||
else()
|
||||
set(WINDOW_SDK_FOLDER "x86")
|
||||
endif()
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}")
|
||||
set(WINDOW_SDK_PATH "C:\\Program Files (x86)\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\${WINDOW_SDK_FOLDER}" CACHE PATH "Windows SDK PATH")
|
||||
endif ()
|
||||
message (WINDOW_SDK_PATH= ${WINDOW_SDK_PATH})
|
||||
|
||||
if (DEBUG_DISCOVERED_SDK_PATH)
|
||||
message(STATUS "The discovered Windows SDK path is ${WINDOW_SDK_PATH}")
|
||||
endif ()
|
||||
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOW_SDK_PATH})
|
||||
# /wd4351 disables warning C4351: new behavior: elements of array will be default initialized
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4351")
|
||||
|
@ -201,6 +209,8 @@ if (NOT DEFINED SERVER_ONLY)
|
|||
set(SERVER_ONLY 0)
|
||||
endif()
|
||||
|
||||
set_packaging_parameters()
|
||||
|
||||
# add subdirectories for all targets
|
||||
if (NOT ANDROID)
|
||||
add_subdirectory(assignment-client)
|
||||
|
@ -232,5 +242,4 @@ if (HIFI_MEMORY_DEBUGGING)
|
|||
endif (UNIX)
|
||||
endif ()
|
||||
|
||||
include_application_version()
|
||||
generate_installers()
|
||||
|
|
|
@ -3,12 +3,11 @@ set(TARGET_NAME assignment-client)
|
|||
setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets)
|
||||
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(
|
||||
audio avatars octree environment gpu model fbx entities
|
||||
link_hifi_libraries(
|
||||
audio avatars octree environment gpu model fbx entities
|
||||
networking animation recording shared script-engine embedded-webserver
|
||||
controllers physics
|
||||
)
|
||||
|
||||
include_application_version()
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
||||
consolidate_installer_components()
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <SharedUtil.h>
|
||||
#include <ShutdownEventListener.h>
|
||||
#include <SoundCache.h>
|
||||
#include <ResourceScriptingInterface.h>
|
||||
|
||||
#include "AssignmentFactory.h"
|
||||
#include "AssignmentActionFactory.h"
|
||||
|
@ -61,6 +62,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
|
||||
DependencyManager::registerInheritance<EntityActionFactoryInterface, AssignmentActionFactory>();
|
||||
auto actionFactory = DependencyManager::set<AssignmentActionFactory>();
|
||||
DependencyManager::set<ResourceScriptingInterface>();
|
||||
|
||||
// setup a thread for the NodeList and its PacketReceiver
|
||||
QThread* nodeThread = new QThread(this);
|
||||
|
|
|
@ -11,9 +11,14 @@
|
|||
|
||||
#include "AssignmentParentFinder.h"
|
||||
|
||||
SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID) const {
|
||||
SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& success) const {
|
||||
SpatiallyNestableWeakPointer parent;
|
||||
// search entities
|
||||
parent = _tree->findEntityByEntityItemID(parentID);
|
||||
if (parent.expired()) {
|
||||
success = false;
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ class AssignmentParentFinder : public SpatialParentFinder {
|
|||
public:
|
||||
AssignmentParentFinder(EntityTreePointer tree) : _tree(tree) { }
|
||||
virtual ~AssignmentParentFinder() { }
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID) const;
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const;
|
||||
|
||||
protected:
|
||||
EntityTreePointer _tree;
|
||||
|
|
6
cmake/externals/zlib/CMakeLists.txt
vendored
6
cmake/externals/zlib/CMakeLists.txt
vendored
|
@ -5,14 +5,14 @@ include(ExternalProject)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://zlib.net/zlib128.zip
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/zlib128.zip
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
|
@ -29,4 +29,4 @@ select_library_configurations(${EXTERNAL_NAME_UPPER})
|
|||
|
||||
# Force selected libraries into the cache
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of zlib libraries")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of zlib libraries")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of zlib libraries")
|
||||
|
|
33
cmake/macros/ConsolidateInstallerComponents.cmake
Normal file
33
cmake/macros/ConsolidateInstallerComponents.cmake
Normal file
|
@ -0,0 +1,33 @@
|
|||
macro(CONSOLIDATE_INSTALLER_COMPONENTS)
|
||||
|
||||
if (DEPLOY_PACKAGE AND WIN32)
|
||||
# Copy icon files for interface and stack manager
|
||||
if (TARGET_NAME STREQUAL "interface")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/${INTERFACE_ICON}")
|
||||
set (ICON_DESTINATION_NAME "interface.ico")
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/package-bundle/${ICON_DESTINATION_NAME}
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/package-bundle
|
||||
)
|
||||
elseif (TARGET_NAME STREQUAL "package-console")
|
||||
set (ICON_FILE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/resources/${CONSOLE_ICON}")
|
||||
set (ICON_DESTINATION_NAME "console.ico")
|
||||
|
||||
# add a command to copy the folder produced by electron-packager to package-bundle
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/package-bundle/${ICON_DESTINATION_NAME}
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGED_CONSOLE_FOLDER} ${CMAKE_BINARY_DIR}/package-bundle
|
||||
)
|
||||
else ()
|
||||
# add a command to copy the fixed up binary and libraries to package-bundle
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/package-bundle
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
endmacro()
|
|
@ -1,28 +0,0 @@
|
|||
macro(CONSOLIDATE_STACK_COMPONENTS)
|
||||
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE)
|
||||
if (WIN32)
|
||||
# Copy icon files for interface and stack manager
|
||||
if (TARGET_NAME STREQUAL "interface" OR TARGET_NAME STREQUAL "stack-manager")
|
||||
if (TARGET_NAME STREQUAL "interface")
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/icon/${INTERFACE_ICON}")
|
||||
set (ICON_DESTINATION_NAME "interface.ico")
|
||||
else ()
|
||||
set (ICON_FILE_PATH "${PROJECT_SOURCE_DIR}/assets/${STACK_MANAGER_ICON}")
|
||||
set (ICON_DESTINATION_NAME "stack-manager.ico")
|
||||
endif ()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy ${ICON_FILE_PATH} ${CMAKE_BINARY_DIR}/package-bundle/${ICON_DESTINATION_NAME}
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/package-bundle
|
||||
)
|
||||
else ()
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory $<TARGET_FILE_DIR:${TARGET_NAME}> ${CMAKE_BINARY_DIR}/package-bundle
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
endmacro()
|
|
@ -1,4 +1,4 @@
|
|||
#
|
||||
#
|
||||
# GenerateInstallers.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
|
@ -7,10 +7,10 @@
|
|||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
#
|
||||
|
||||
macro(GENERATE_INSTALLERS)
|
||||
if (DEFINED DEPLOY_PACKAGE AND DEPLOY_PACKAGE AND WIN32)
|
||||
if (DEPLOY_PACKAGE AND WIN32)
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/package-bundle")
|
||||
find_program(MAKENSIS_COMMAND makensis PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS])
|
||||
if (NOT MAKENSIS_COMMAND)
|
||||
|
@ -26,5 +26,7 @@ macro(GENERATE_INSTALLERS)
|
|||
COMMAND set INSTALLER_DIRECTORY=${INSTALLER_DIRECTORY}
|
||||
COMMAND CMD /C "\"${MAKENSIS_COMMAND}\" ${CMAKE_SOURCE_DIR}/tools/nsis/release.nsi"
|
||||
)
|
||||
|
||||
set_target_properties(build-package PROPERTIES EXCLUDE_FROM_ALL TRUE FOLDER "Installer")
|
||||
endif ()
|
||||
endmacro()
|
||||
endmacro()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# IncludeApplicationVersion.cmake
|
||||
#
|
||||
# SetPackagingParameters.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Created by Leonardo Murillo on 07/14/2015.
|
||||
|
@ -7,36 +7,42 @@
|
|||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
macro(INCLUDE_APPLICATION_VERSION)
|
||||
#
|
||||
# We are relying on Jenkins defined environment variables to determine the origin of this build
|
||||
# and will only package if this is a PR or Release build
|
||||
# This macro checks some Jenkins defined environment variables to determine the origin of this build
|
||||
# and decides how targets should be packaged.
|
||||
|
||||
macro(SET_PACKAGING_PARAMETERS)
|
||||
|
||||
if (DEFINED ENV{JOB_ID})
|
||||
set(DEPLOY_PACKAGE 1)
|
||||
set(BUILD_SEQ $ENV{JOB_ID})
|
||||
set(BUILD_TAGGED_BETA FALSE)
|
||||
set(INSTALLER_COMPANY "High Fidelity")
|
||||
set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}")
|
||||
set(INSTALLER_NAME "interface-win64-${BUILD_SEQ}.exe")
|
||||
set(INTERFACE_ICON "interface.ico")
|
||||
set(STACK_MANAGER_ICON "icon.ico")
|
||||
set(CONSOLE_ICON "console.ico")
|
||||
elseif (DEFINED ENV{ghprbPullId})
|
||||
set(DEPLOY_PACKAGE 1)
|
||||
set(BUILD_TAGGED_BETA TRUE)
|
||||
set(BUILD_SEQ "PR-$ENV{ghprbPullId}")
|
||||
set(INSTALLER_COMPANY "High Fidelity - PR")
|
||||
set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}\\${BUILD_SEQ}")
|
||||
set(INSTALLER_NAME "pr-interface-win64-${BUILD_SEQ}.exe")
|
||||
set(INTERFACE_ICON "interface-beta.ico")
|
||||
set(STACK_MANAGER_ICON "icon-beta.ico")
|
||||
set(CONSOLE_ICON "console-beta.ico")
|
||||
else ()
|
||||
set(BUILD_SEQ "dev")
|
||||
set(BUILD_TAGGED_BETA TRUE)
|
||||
set(INSTALLER_COMPANY "High Fidelity - Dev")
|
||||
set(INSTALLER_DIRECTORY "${INSTALLER_COMPANY}")
|
||||
set(INSTALLER_NAME "dev-interface-win64.exe")
|
||||
set(INTERFACE_ICON "interface-beta.ico")
|
||||
set(STACK_MANAGER_ICON "icon-beta.ico")
|
||||
set(CONSOLE_ICON "console-beta.ico")
|
||||
endif ()
|
||||
configure_file("${MACRO_DIR}/ApplicationVersion.h.in" "${PROJECT_BINARY_DIR}/includes/ApplicationVersion.h")
|
||||
include_directories("${PROJECT_BINARY_DIR}/includes")
|
||||
endmacro(INCLUDE_APPLICATION_VERSION)
|
||||
|
||||
# create a header file our targets can use to find out the application version
|
||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/includes")
|
||||
configure_file("${MACRO_DIR}/ApplicationVersion.h.in" "${CMAKE_BINARY_DIR}/includes/ApplicationVersion.h")
|
||||
|
||||
endmacro(SET_PACKAGING_PARAMETERS)
|
|
@ -1,36 +1,39 @@
|
|||
#
|
||||
#
|
||||
# SetupHifiProject.cmake
|
||||
#
|
||||
#
|
||||
# Copyright 2013 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
#
|
||||
|
||||
macro(SETUP_HIFI_PROJECT)
|
||||
project(${TARGET_NAME})
|
||||
|
||||
|
||||
# grab the implemenation and header files
|
||||
file(GLOB TARGET_SRCS src/*)
|
||||
|
||||
|
||||
file(GLOB SRC_SUBDIRS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src/*)
|
||||
|
||||
|
||||
foreach(DIR ${SRC_SUBDIRS})
|
||||
if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/${DIR}")
|
||||
file(GLOB DIR_CONTENTS "src/${DIR}/*")
|
||||
set(TARGET_SRCS ${TARGET_SRCS} "${DIR_CONTENTS}")
|
||||
endif ()
|
||||
endforeach()
|
||||
|
||||
|
||||
if (DEFINED BUILD_BUNDLE AND BUILD_BUNDLE AND APPLE)
|
||||
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${TARGET_SRCS} ${AUTOMTC_SRC} ${AUTOSCRIBE_SHADER_LIB_SRC})
|
||||
else ()
|
||||
add_executable(${TARGET_NAME} ${TARGET_SRCS} ${AUTOMTC_SRC} ${AUTOSCRIBE_SHADER_LIB_SRC})
|
||||
endif()
|
||||
|
||||
# include the generated application version header
|
||||
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes")
|
||||
|
||||
set(${TARGET_NAME}_DEPENDENCY_QT_MODULES ${ARGN})
|
||||
list(APPEND ${TARGET_NAME}_DEPENDENCY_QT_MODULES Core)
|
||||
|
||||
|
||||
# find these Qt modules and link them to our own target
|
||||
find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED)
|
||||
|
||||
|
@ -44,7 +47,7 @@ macro(SETUP_HIFI_PROJECT)
|
|||
foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES})
|
||||
target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
|
||||
endforeach()
|
||||
|
||||
|
||||
target_glm()
|
||||
|
||||
|
||||
endmacro()
|
||||
|
|
|
@ -1,20 +1,28 @@
|
|||
#
|
||||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Bradley Austin Davis on 2015/10/10
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
#
|
||||
macro(TARGET_NSIGHT)
|
||||
if (WIN32)
|
||||
if (USE_NSIGHT)
|
||||
if (WIN32 AND USE_NSIGHT)
|
||||
|
||||
# grab the global CHECKED_FOR_NSIGHT_ONCE property
|
||||
get_property(NSIGHT_CHECKED GLOBAL PROPERTY CHECKED_FOR_NSIGHT_ONCE)
|
||||
|
||||
if (NOT NSIGHT_CHECKED)
|
||||
# try to find the Nsight package and add it to the build if we find it
|
||||
find_package(NSIGHT)
|
||||
if (NSIGHT_FOUND)
|
||||
include_directories(${NSIGHT_INCLUDE_DIRS})
|
||||
add_definitions(-DNSIGHT_FOUND)
|
||||
target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}")
|
||||
endif ()
|
||||
endif()
|
||||
endif (WIN32)
|
||||
endmacro()
|
||||
|
||||
# set the global CHECKED_FOR_NSIGHT_ONCE property so that we only debug that we couldn't find it once
|
||||
set_property(GLOBAL PROPERTY CHECKED_FOR_NSIGHT_ONCE TRUE)
|
||||
endif ()
|
||||
|
||||
if (NSIGHT_FOUND)
|
||||
include_directories(${NSIGHT_INCLUDE_DIRS})
|
||||
add_definitions(-DNSIGHT_FOUND)
|
||||
target_link_libraries(${TARGET_NAME} "${NSIGHT_LIBRARIES}")
|
||||
endif ()
|
||||
endif ()
|
||||
endmacro()
|
||||
|
|
3
console/.gitignore
vendored
3
console/.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
High\ Fidelity-*/
|
||||
Server\ Console-*/
|
||||
server-console-*/
|
||||
npm-debug.log
|
||||
logs/
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
set(PACKAGING_TARGET_NAME package-console)
|
||||
set(TARGET_NAME package-console)
|
||||
|
||||
# add a target that when built will produce an executable of console for this platform
|
||||
if (APPLE)
|
||||
set(PACKAGE_COMMAND package-darwin)
|
||||
elseif (WIN32)
|
||||
set(PACKAGE_COMMAND package-win)
|
||||
elseif (UNIX)
|
||||
set(PACKAGE_COMMAND package-linux)
|
||||
endif ()
|
||||
if (BUILD_TAGGED_BETA)
|
||||
set(BETA_OPTION "--beta")
|
||||
endif()
|
||||
|
||||
add_custom_target(${PACKAGING_TARGET_NAME}
|
||||
COMMAND npm run-script ${PACKAGE_COMMAND}
|
||||
# add a target that will package the console
|
||||
add_custom_target(${TARGET_NAME}
|
||||
COMMAND npm run packager -- --out ${CMAKE_CURRENT_BINARY_DIR} ${BETA_OPTION}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
# set the packaged console folder depending on platform, so we can copy it
|
||||
if (APPLE)
|
||||
set(PACKAGED_CONSOLE_FOLDER "Server\\ Console-darwin-x64")
|
||||
elseif (WIN32)
|
||||
set(PACKAGED_CONSOLE_FOLDER "server-console-win32-x64")
|
||||
elseif (UNIX)
|
||||
set(PACKAGED_CONSOLE_FOLDER "server-console-linux-x64")
|
||||
endif ()
|
||||
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE FOLDER "Installer")
|
||||
|
||||
consolidate_installer_components()
|
||||
|
|
|
@ -20,9 +20,7 @@
|
|||
"scripts": {
|
||||
"start": "electron . --local-debug-builds --debug",
|
||||
"local-release": "electron . --local-release-builds --debug",
|
||||
"package-darwin": "electron-packager . High\\ Fidelity --overwrite --platform=darwin --arch=x64 --version=0.35.4 --icon=resources/console.icns",
|
||||
"package-win": "electron-packager . High\\ Fidelity --overwrite --platform=win32 --arch=x64 --version=0.35.4 --icon=resources/console.ico",
|
||||
"package-linux": "electron-packager . High\\ Fidelity --overwrite --platform=linux --arch=x64 --version=0.35.4"
|
||||
"packager": "node packager.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"extend": "^3.0.0",
|
||||
|
|
57
console/packager.js
Normal file
57
console/packager.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
var packager = require('electron-packager')
|
||||
var osType = require('os').type();
|
||||
|
||||
var platform = null;
|
||||
if (osType == "Darwin" || osType == "Linux") {
|
||||
platform = osType.toLowerCase();
|
||||
} else if (osType == "Windows_NT") {
|
||||
platform = "win32"
|
||||
}
|
||||
|
||||
var argv = require('yargs').argv;
|
||||
|
||||
// check which icon we should use, beta or regular
|
||||
var iconName = argv.beta ? "console-beta" : "console"
|
||||
|
||||
// setup the common options for the packager
|
||||
var options = {
|
||||
dir: __dirname,
|
||||
name: "server-console",
|
||||
version: "0.35.4",
|
||||
overwrite: true,
|
||||
prune: true,
|
||||
arch: "x64",
|
||||
platform: platform,
|
||||
icon: "resources/" + iconName
|
||||
}
|
||||
|
||||
const EXEC_NAME = "server-console";
|
||||
const SHORT_NAME = "Server Console";
|
||||
const FULL_NAME = "High Fidelity Server Console";
|
||||
|
||||
// setup per OS options
|
||||
if (osType == "Darwin") {
|
||||
options["name"] = SHORT_NAME
|
||||
} else if (osType == "Windows_NT") {
|
||||
options["version-string"] = {
|
||||
CompanyName: "High Fidelity, Inc.",
|
||||
FileDescription: SHORT_NAME,
|
||||
ProductName: FULL_NAME,
|
||||
OriginalFilename: EXEC_NAME + ".exe"
|
||||
}
|
||||
}
|
||||
|
||||
// check if we were passed a custom out directory, pass it along if so
|
||||
if (argv.out) {
|
||||
options.out = argv.out
|
||||
}
|
||||
|
||||
// call the packager to produce the executable
|
||||
packager(options, function(error, appPath) {
|
||||
if (error) {
|
||||
console.error("There was an error writing the packaged console: " + error.message);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log("Wrote new app to " + appPath);
|
||||
}
|
||||
});
|
BIN
console/resources/console-beta.icns
Normal file
BIN
console/resources/console-beta.icns
Normal file
Binary file not shown.
BIN
console/resources/console-beta.ico
Normal file
BIN
console/resources/console-beta.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
BIN
console/resources/console-beta.png
Normal file
BIN
console/resources/console-beta.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 320 KiB |
|
@ -22,6 +22,8 @@ var progress = require('request-progress');
|
|||
|
||||
var Config = require('./modules/config').Config;
|
||||
|
||||
const dialog = require('electron').dialog;
|
||||
|
||||
var hfprocess = require('./modules/hf-process.js');
|
||||
var Process = hfprocess.Process;
|
||||
var ACMonitorProcess = hfprocess.ACMonitorProcess;
|
||||
|
@ -95,7 +97,6 @@ userConfig.load(configPath);
|
|||
|
||||
const TRAY_FILENAME = (osType == "Darwin" ? "console-tray-Template.png" : "console-tray.png");
|
||||
const TRAY_ICON = path.join(__dirname, '../resources/' + TRAY_FILENAME);
|
||||
const APP_ICON = path.join(__dirname, '../resources/console.png');
|
||||
|
||||
// print out uncaught exceptions in the console
|
||||
process.on('uncaughtException', function(err) {
|
||||
|
@ -130,8 +131,40 @@ if (argv.localDebugBuilds || argv.localReleaseBuilds) {
|
|||
acPath = pathFinder.discoveredPath("assignment-client", argv.localReleaseBuilds);
|
||||
}
|
||||
|
||||
function binaryMissingMessage(displayName, executableName, required) {
|
||||
var message = "The " + displayName + " executable was not found.\n";
|
||||
|
||||
if (required) {
|
||||
message += "It is required for the Server Console to run.\n\n";
|
||||
} else {
|
||||
message += "\n";
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
message += "Please ensure there is a compiled " + displayName + " in a folder named build in this checkout.\n\n";
|
||||
message += "It was expected to be found at one of the following paths:\n";
|
||||
|
||||
var paths = pathFinder.searchPaths(executableName, argv.localReleaseBuilds);
|
||||
message += paths.join("\n");
|
||||
} else {
|
||||
message += "It is expected to be found beside this executable.\n"
|
||||
message += "You may need to re-install the Server Console.";
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
// if at this point any of the paths are null, we're missing something we wanted to find
|
||||
// TODO: show an error for the binaries that couldn't be found
|
||||
|
||||
if (!dsPath) {
|
||||
dialog.showErrorBox("Domain Server Not Found", binaryMissingMessage("domain-server", "domain-server", true));
|
||||
app.quit();
|
||||
}
|
||||
|
||||
if (!acPath) {
|
||||
dialog.showErrorBox("Assignment Client Not Found", binaryMissingMessage("assignment-client", "assignment-client", true))
|
||||
app.quit();
|
||||
}
|
||||
|
||||
function openFileBrowser(path) {
|
||||
// Add quotes around path
|
||||
|
@ -207,6 +240,15 @@ LogWindow.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
function goHomeClicked() {
|
||||
if (interfacePath) {
|
||||
startInterface('hifi://localhost');
|
||||
} else {
|
||||
// show an error to say that we can't go home without an interface instance
|
||||
dialog.showErrorBox("Client Not Found", binaryMissingMessage("High Fidelity Client", "Interface", false));
|
||||
}
|
||||
}
|
||||
|
||||
var logWindow = null;
|
||||
|
||||
function buildMenuArray(serverState) {
|
||||
|
@ -222,7 +264,7 @@ function buildMenuArray(serverState) {
|
|||
menuArray = [
|
||||
{
|
||||
label: 'Go Home',
|
||||
click: function() { startInterface('hifi://localhost'); },
|
||||
click: goHomeClicked,
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
|
@ -446,18 +488,22 @@ app.on('ready', function() {
|
|||
|
||||
// Create tray icon
|
||||
tray = new Tray(TRAY_ICON);
|
||||
tray.setToolTip('High Fidelity');
|
||||
tray.setToolTip('High Fidelity Server Console');
|
||||
|
||||
tray.on('click', function() {
|
||||
tray.popUpContextMenu(tray.menu);
|
||||
});
|
||||
|
||||
updateTrayMenu(ProcessGroupStates.STOPPED);
|
||||
|
||||
maybeInstallDefaultContentSet(function() {
|
||||
maybeShowSplash();
|
||||
|
||||
if (interfacePath && dsPath && acPath) {
|
||||
if (dsPath && acPath) {
|
||||
domainServer = new Process('domain-server', dsPath, [], logPath);
|
||||
acMonitor = new ACMonitorProcess('ac-monitor', acPath, ['-n4',
|
||||
'--log-directory', logPath,
|
||||
'--http-status-port', httpStatusPort], httpStatusPort, logPath);
|
||||
acMonitor = new ACMonitorProcess('ac-monitor', acPath, ['-n6',
|
||||
'--log-directory', logPath,
|
||||
'--http-status-port', httpStatusPort], httpStatusPort, logPath);
|
||||
homeServer = new ProcessGroup('home', [domainServer, acMonitor]);
|
||||
logWindow = new LogWindow(acMonitor, domainServer);
|
||||
|
||||
|
|
|
@ -1,54 +1,49 @@
|
|||
var fs = require('fs');
|
||||
|
||||
exports.discoveredPath = function (name, preferRelease) {
|
||||
var path = "../build/" + name + "/";
|
||||
|
||||
function binaryFromPath(name, path) {
|
||||
function platformExtension(name) {
|
||||
if (name == "Interface") {
|
||||
if (process.platform == "darwin") {
|
||||
return ".app/Contents/MacOS/" + name
|
||||
} else if (process.platform == "win32") {
|
||||
return ".exe"
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
exports.searchPaths = function(name, preferRelease) {
|
||||
function platformExtension(name) {
|
||||
if (name == "Interface") {
|
||||
if (process.platform == "darwin") {
|
||||
return ".app/Contents/MacOS/" + name
|
||||
} else if (process.platform == "win32") {
|
||||
return ".exe"
|
||||
} else {
|
||||
return process.platform == "win32" ? ".exe" : ""
|
||||
return ""
|
||||
}
|
||||
} else {
|
||||
return process.platform == "win32" ? ".exe" : ""
|
||||
}
|
||||
}
|
||||
|
||||
var extension = platformExtension(name);
|
||||
var fullPath = path + name + extension;
|
||||
var extension = platformExtension(name);
|
||||
var basePath = "../build/" + name + "/";
|
||||
|
||||
try {
|
||||
var stats = fs.lstatSync(fullPath);
|
||||
return [
|
||||
basePath + name + extension,
|
||||
basePath + (preferRelease ? "Release/" : "Debug/") + name + extension
|
||||
];
|
||||
}
|
||||
|
||||
if (stats.isFile() || (stats.isDirectory() && extension == ".app")) {
|
||||
console.log("Found " + name + " at " + fullPath);
|
||||
return fullPath;
|
||||
exports.discoveredPath = function (name, preferRelease) {
|
||||
function binaryFromPaths(name, paths) {
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
var path = paths[i];
|
||||
|
||||
try {
|
||||
var stats = fs.lstatSync(path);
|
||||
|
||||
if (stats.isFile() || (stats.isDirectory() && extension == ".app")) {
|
||||
console.log("Found " + name + " at " + path);
|
||||
return path;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Executable with name " + name + " not found at path " + path);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Executable with name " + name + " not found at path " + fullPath);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// does the executable exist at this path already?
|
||||
// if so assume we're on a platform that doesn't have Debug/Release
|
||||
// folders and just use the discovered executable
|
||||
var matchingBinary = binaryFromPath(name, path);
|
||||
|
||||
if (matchingBinary == null) {
|
||||
if (preferRelease) {
|
||||
// check if we can find the executable in a Release folder below this path
|
||||
matchingBinary = binaryFromPath(name, path + "Release/");
|
||||
} else {
|
||||
// check if we can find the executable in a Debug folder below this path
|
||||
matchingBinary = binaryFromPath(name, path + "Debug/");
|
||||
}
|
||||
}
|
||||
|
||||
return matchingBinary;
|
||||
// attempt to find a binary at the usual paths, return null if it doesn't exist
|
||||
return binaryFromPaths(name, this.searchPaths(name, preferRelease));
|
||||
}
|
||||
|
|
|
@ -36,6 +36,5 @@ if (UNIX)
|
|||
target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS})
|
||||
endif (UNIX)
|
||||
|
||||
include_application_version()
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
||||
consolidate_installer_components()
|
||||
|
|
|
@ -23,13 +23,15 @@ var WANT_DEBUG = false;
|
|||
// these tune time-averaging and "on" value for analog trigger
|
||||
//
|
||||
|
||||
var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing
|
||||
var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab
|
||||
var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab
|
||||
var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing
|
||||
var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab
|
||||
var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab
|
||||
var TRIGGER_OFF_VALUE = 0.15;
|
||||
|
||||
var BUMPER_ON_VALUE = 0.5;
|
||||
|
||||
var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only use head for search/move.
|
||||
|
||||
//
|
||||
// distant manipulation
|
||||
//
|
||||
|
@ -38,6 +40,7 @@ var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand
|
|||
var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position
|
||||
var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did
|
||||
var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects
|
||||
var FAR_TO_NEAR_GRAB_PADDING_FACTOR = 1.2;
|
||||
|
||||
var NO_INTERSECT_COLOR = {
|
||||
red: 10,
|
||||
|
@ -57,7 +60,6 @@ var LINE_ENTITY_DIMENSIONS = {
|
|||
|
||||
var LINE_LENGTH = 500;
|
||||
var PICK_MAX_DISTANCE = 500; // max length of pick-ray
|
||||
|
||||
//
|
||||
// near grabbing
|
||||
//
|
||||
|
@ -130,6 +132,7 @@ var USE_PARTICLE_BEAM_FOR_SEARCHING = false;
|
|||
var USE_ENTITY_LINES_FOR_MOVING = false;
|
||||
var USE_OVERLAY_LINES_FOR_MOVING = false;
|
||||
var USE_PARTICLE_BEAM_FOR_MOVING = true;
|
||||
var TEMPORARY_PARTICLE_BEAM_LIFETIME = 30;
|
||||
|
||||
var USE_SPOTLIGHT = false;
|
||||
var USE_POINTLIGHT = false;
|
||||
|
@ -419,7 +422,7 @@ function MyController(hand) {
|
|||
this.searchSphereOn = function(location, size, color) {
|
||||
if (this.searchSphere === null) {
|
||||
var sphereProperties = {
|
||||
position: location,
|
||||
position: location,
|
||||
size: size,
|
||||
color: color,
|
||||
alpha: SEARCH_SPHERE_ALPHA,
|
||||
|
@ -427,10 +430,15 @@ function MyController(hand) {
|
|||
visible: true
|
||||
}
|
||||
this.searchSphere = Overlays.addOverlay("sphere", sphereProperties);
|
||||
} else {
|
||||
Overlays.editOverlay(this.searchSphere, { position: location, size: size, color: color, visible: true });
|
||||
} else {
|
||||
Overlays.editOverlay(this.searchSphere, {
|
||||
position: location,
|
||||
size: size,
|
||||
color: color,
|
||||
visible: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.overlayLineOn = function(closePoint, farPoint, color) {
|
||||
if (this.overlayLine === null) {
|
||||
|
@ -572,6 +580,13 @@ function MyController(hand) {
|
|||
|
||||
};
|
||||
|
||||
this.renewParticleBeamLifetime = function() {
|
||||
var props = Entities.getEntityProperties(this.particleBeam, "age");
|
||||
Entities.editEntity(this.particleBeam, {
|
||||
lifetime: TEMPORARY_PARTICLE_BEAM_LIFETIME + props.age // renew lifetime
|
||||
})
|
||||
}
|
||||
|
||||
this.evalLightWorldTransform = function(modelPos, modelRot) {
|
||||
|
||||
var MODEL_LIGHT_POSITION = {
|
||||
|
@ -763,7 +778,7 @@ function MyController(hand) {
|
|||
if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) {
|
||||
this.lastPickTime = 0;
|
||||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
|
||||
this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
|
||||
if (this.triggerSmoothedSqueezed()) {
|
||||
this.setState(STATE_SEARCHING);
|
||||
} else {
|
||||
|
@ -786,10 +801,16 @@ function MyController(hand) {
|
|||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
|
||||
var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation));
|
||||
|
||||
|
||||
var distantPickRay = {
|
||||
origin: Camera.position,
|
||||
direction: Quat.getFront(Quat.multiply(Camera.orientation, handDeltaRotation)),
|
||||
origin: Camera.position,
|
||||
direction: Vec3.mix(Quat.getUp(this.getHandRotation()), Quat.getFront(Camera.orientation), HAND_HEAD_MIX_RATIO),
|
||||
length: PICK_MAX_DISTANCE
|
||||
};
|
||||
|
||||
var searchVisualizationPickRay = {
|
||||
origin: handPosition,
|
||||
direction: Quat.getUp(this.getHandRotation()),
|
||||
length: PICK_MAX_DISTANCE
|
||||
};
|
||||
|
||||
|
@ -884,11 +905,19 @@ function MyController(hand) {
|
|||
if (intersection.properties.collisionsWillMove && !intersection.properties.locked) {
|
||||
// the hand is far from the intersected object. go into distance-holding mode
|
||||
this.grabbedEntity = intersection.entityID;
|
||||
if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) {
|
||||
if (this.state == STATE_EQUIP_SEARCHING) {
|
||||
// if a distance pick in equip mode hits something with a spatialKey, equip it
|
||||
// TODO use STATE_EQUIP_SPRING here once it works right.
|
||||
// this.setState(STATE_EQUIP_SPRING);
|
||||
if (typeof grabbableData.spatialKey === 'undefined') {
|
||||
// We want to give a temporary position offset to this object so it is pulled close to hand
|
||||
var intersectionPointToCenterDistance = Vec3.length(Vec3.subtract(intersection.intersection, intersection.properties.position));
|
||||
this.temporaryPositionOffset = Vec3.normalize(Vec3.subtract(intersection.properties.position, handPosition));
|
||||
this.temporaryPositionOffset = Vec3.multiply(this.temporaryPositionOffset, intersectionPointToCenterDistance * FAR_TO_NEAR_GRAB_PADDING_FACTOR);
|
||||
|
||||
}
|
||||
this.setState(STATE_EQUIP);
|
||||
this.turnOffVisualizations();
|
||||
return;
|
||||
} else if ((this.state == STATE_SEARCHING) && this.triggerSmoothedGrab()) {
|
||||
this.setState(STATE_DISTANCE_HOLDING);
|
||||
|
@ -1013,6 +1042,11 @@ function MyController(hand) {
|
|||
if (USE_PARTICLE_BEAM_FOR_SEARCHING === true) {
|
||||
this.handleParticleBeam(distantPickRay.origin, this.getHandRotation(), NO_INTERSECT_COLOR);
|
||||
}
|
||||
|
||||
if (USE_OVERLAY_LINES_FOR_SEARCHING === true) {
|
||||
this.overlayLineOn(searchVisualizationPickRay.origin, Vec3.sum(searchVisualizationPickRay.origin, Vec3.multiply(searchVisualizationPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR);
|
||||
}
|
||||
|
||||
if (this.intersectionDistance > 0) {
|
||||
var SPHERE_INTERSECTION_SIZE = 0.011;
|
||||
var SEARCH_SPHERE_FOLLOW_RATE = 0.50;
|
||||
|
@ -1022,9 +1056,7 @@ function MyController(hand) {
|
|||
searchSphereLocation.y -= ((this.intersectionDistance - this.searchSphereDistance) / this.intersectionDistance) * SEARCH_SPHERE_CHASE_DROP;
|
||||
this.searchSphereOn(searchSphereLocation, SPHERE_INTERSECTION_SIZE * this.intersectionDistance, this.triggerSmoothedGrab() ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
|
||||
if (USE_OVERLAY_LINES_FOR_SEARCHING === true) {
|
||||
var OVERLAY_BEAM_SETBACK = 0.9;
|
||||
var startBeam = Vec3.sum(handPosition, Vec3.multiply(Vec3.subtract(searchSphereLocation, handPosition), OVERLAY_BEAM_SETBACK));
|
||||
this.overlayLineOn(startBeam, searchSphereLocation, this.triggerSmoothedGrab() ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
|
||||
this.overlayLineOn(handPosition, searchSphereLocation, this.triggerSmoothedGrab() ? INTERSECT_COLOR : NO_INTERSECT_COLOR);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1189,15 +1221,12 @@ function MyController(hand) {
|
|||
y: 0.0,
|
||||
z: objDistance
|
||||
});
|
||||
var change = Vec3.subtract(before, after);
|
||||
var change = Vec3.multiply(Vec3.subtract(before, after), HAND_HEAD_MIX_RATIO);
|
||||
this.currentCameraOrientation = Camera.orientation;
|
||||
this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change);
|
||||
}
|
||||
} else {
|
||||
// print('should not head move!');
|
||||
}
|
||||
|
||||
|
||||
var defaultConstraintData = {
|
||||
axisStart: false,
|
||||
axisEnd: false,
|
||||
|
@ -1237,131 +1266,144 @@ function MyController(hand) {
|
|||
this.handleSpotlight(this.grabbedEntity);
|
||||
}
|
||||
|
||||
Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
var success = Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
targetPosition: targetPosition,
|
||||
linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
|
||||
targetRotation: this.currentObjectRotation,
|
||||
angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
|
||||
ttl: ACTION_TTL
|
||||
});
|
||||
if (success) {
|
||||
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC);
|
||||
} else {
|
||||
print("continueDistanceHolding -- updateAction failed");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.setupHoldAction = function() {
|
||||
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
|
||||
hand: this.hand === RIGHT_HAND ? "right" : "left",
|
||||
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
|
||||
relativePosition: this.offsetPosition,
|
||||
relativeRotation: this.offsetRotation,
|
||||
ttl: ACTION_TTL,
|
||||
kinematic: NEAR_GRABBING_KINEMATIC,
|
||||
kinematicSetVelocity: true,
|
||||
ignoreIK: this.ignoreIK
|
||||
});
|
||||
if (this.actionID === NULL_ACTION_ID) {
|
||||
this.actionID = null;
|
||||
return false;
|
||||
}
|
||||
var now = Date.now();
|
||||
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.projectVectorAlongAxis = function(position, axisStart, axisEnd) {
|
||||
|
||||
var aPrime = Vec3.subtract(position, axisStart);
|
||||
var aPrime = Vec3.subtract(position, axisStart);
|
||||
|
||||
|
||||
var bPrime = Vec3.subtract(axisEnd, axisStart);
|
||||
var bPrime = Vec3.subtract(axisEnd, axisStart);
|
||||
|
||||
|
||||
var bPrimeMagnitude = Vec3.length(bPrime);
|
||||
var bPrimeMagnitude = Vec3.length(bPrime);
|
||||
|
||||
var dotProduct = Vec3.dot(aPrime, bPrime);
|
||||
var dotProduct = Vec3.dot(aPrime, bPrime);
|
||||
|
||||
|
||||
var scalar = dotProduct / bPrimeMagnitude;
|
||||
var scalar = dotProduct / bPrimeMagnitude;
|
||||
|
||||
if (scalar < 0) {
|
||||
scalar = 0;
|
||||
}
|
||||
if (scalar < 0) {
|
||||
scalar = 0;
|
||||
}
|
||||
|
||||
if (scalar > 1) {
|
||||
scalar = 1;
|
||||
}
|
||||
if (scalar > 1) {
|
||||
scalar = 1;
|
||||
}
|
||||
|
||||
var projection = Vec3.sum(axisStart, Vec3.multiply(scalar, Vec3.normalize(bPrime)));
|
||||
var projection = Vec3.sum(axisStart, Vec3.multiply(scalar, Vec3.normalize(bPrime)));
|
||||
|
||||
return projection
|
||||
return projection
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
this.nearGrabbing = function() {
|
||||
var now = Date.now();
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||
this.nearGrabbing = function() {
|
||||
var now = Date.now();
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||
|
||||
if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
|
||||
return;
|
||||
}
|
||||
if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
|
||||
return;
|
||||
}
|
||||
|
||||
this.lineOff();
|
||||
this.overlayLineOff();
|
||||
this.lineOff();
|
||||
this.overlayLineOff();
|
||||
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||
this.activateEntity(this.grabbedEntity, grabbedProperties);
|
||||
if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) {
|
||||
Entities.editEntity(this.grabbedEntity, {
|
||||
collisionsWillMove: false
|
||||
});
|
||||
}
|
||||
|
||||
var handRotation = this.getHandRotation();
|
||||
var handPosition = this.getHandPosition();
|
||||
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||
|
||||
if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) {
|
||||
// if an object is "equipped" and has a spatialKey, use it.
|
||||
this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false;
|
||||
this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
|
||||
this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
|
||||
} else {
|
||||
this.ignoreIK = false;
|
||||
|
||||
var objectRotation = grabbedProperties.rotation;
|
||||
this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
|
||||
|
||||
var currentObjectPosition = grabbedProperties.position;
|
||||
var offset = Vec3.subtract(currentObjectPosition, handPosition);
|
||||
this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset);
|
||||
}
|
||||
|
||||
this.actionID = NULL_ACTION_ID;
|
||||
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
|
||||
hand: this.hand === RIGHT_HAND ? "right" : "left",
|
||||
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
|
||||
relativePosition: this.offsetPosition,
|
||||
relativeRotation: this.offsetRotation,
|
||||
ttl: ACTION_TTL,
|
||||
kinematic: NEAR_GRABBING_KINEMATIC,
|
||||
kinematicSetVelocity: true,
|
||||
ignoreIK: this.ignoreIK
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||
this.activateEntity(this.grabbedEntity, grabbedProperties);
|
||||
if (grabbedProperties.collisionsWillMove && NEAR_GRABBING_KINEMATIC) {
|
||||
Entities.editEntity(this.grabbedEntity, {
|
||||
collisionsWillMove: false
|
||||
});
|
||||
if (this.actionID === NULL_ACTION_ID) {
|
||||
this.actionID = null;
|
||||
} else {
|
||||
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC);
|
||||
if (this.state == STATE_NEAR_GRABBING) {
|
||||
this.setState(STATE_CONTINUE_NEAR_GRABBING);
|
||||
} else {
|
||||
// equipping
|
||||
Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]);
|
||||
this.startHandGrasp();
|
||||
}
|
||||
|
||||
this.setState(STATE_CONTINUE_EQUIP_BD);
|
||||
}
|
||||
var handRotation = this.getHandRotation();
|
||||
var handPosition = this.getHandPosition();
|
||||
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
|
||||
} else {
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
|
||||
}
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]);
|
||||
if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) {
|
||||
// if an object is "equipped" and has a spatialKey, use it.
|
||||
this.ignoreIK = grabbableData.spatialKey.ignoreIK ? grabbableData.spatialKey.ignoreIK : false;
|
||||
this.offsetPosition = getSpatialOffsetPosition(this.hand, grabbableData.spatialKey);
|
||||
this.offsetRotation = getSpatialOffsetRotation(this.hand, grabbableData.spatialKey);
|
||||
} else {
|
||||
this.ignoreIK = false;
|
||||
|
||||
Entities.callEntityMethod(this.grabbedEntity, "startNearGrab");
|
||||
var objectRotation = grabbedProperties.rotation;
|
||||
this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation);
|
||||
|
||||
var currentObjectPosition = grabbedProperties.position;
|
||||
var offset = Vec3.subtract(currentObjectPosition, handPosition);
|
||||
this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset);
|
||||
if (this.temporaryPositionOffset && this.state != STATE_NEAR_GRABBING) {
|
||||
this.offsetPosition = this.temporaryPositionOffset;
|
||||
}
|
||||
}
|
||||
|
||||
this.currentHandControllerTipPosition =
|
||||
(this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;
|
||||
|
||||
this.currentObjectTime = Date.now();
|
||||
};
|
||||
if (!this.setupHoldAction()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state == STATE_NEAR_GRABBING) {
|
||||
this.setState(STATE_CONTINUE_NEAR_GRABBING);
|
||||
} else {
|
||||
// equipping
|
||||
Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]);
|
||||
this.startHandGrasp();
|
||||
|
||||
this.setState(STATE_CONTINUE_EQUIP_BD);
|
||||
}
|
||||
|
||||
if (this.hand === RIGHT_HAND) {
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
|
||||
} else {
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
|
||||
}
|
||||
|
||||
Entities.callEntityMethod(this.grabbedEntity, "setHand", [this.hand]);
|
||||
Entities.callEntityMethod(this.grabbedEntity, "startNearGrab");
|
||||
|
||||
this.currentHandControllerTipPosition =
|
||||
(this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;
|
||||
|
||||
this.currentObjectTime = Date.now();
|
||||
};
|
||||
|
||||
this.continueNearGrabbing = function() {
|
||||
if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) {
|
||||
|
@ -1406,7 +1448,7 @@ function MyController(hand) {
|
|||
|
||||
if (this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) {
|
||||
// if less than a 5 seconds left, refresh the actions ttl
|
||||
Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
var success = Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
hand: this.hand === RIGHT_HAND ? "right" : "left",
|
||||
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
|
||||
relativePosition: this.offsetPosition,
|
||||
|
@ -1416,7 +1458,13 @@ function MyController(hand) {
|
|||
kinematicSetVelocity: true,
|
||||
ignoreIK: this.ignoreIK
|
||||
});
|
||||
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC);
|
||||
if (success) {
|
||||
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC);
|
||||
} else {
|
||||
print("continueNearGrabbing -- updateAction failed");
|
||||
Entities.deleteAction(this.grabbedEntity, this.actionID);
|
||||
this.setupHoldAction();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1463,7 +1511,7 @@ function MyController(hand) {
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
Entities.updateAction(this.grabbedEntity, this.equipSpringID, {
|
||||
var success = Entities.updateAction(this.grabbedEntity, this.equipSpringID, {
|
||||
targetPosition: targetPosition,
|
||||
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||
targetRotation: targetRotation,
|
||||
|
@ -1471,6 +1519,9 @@ function MyController(hand) {
|
|||
ttl: ACTION_TTL,
|
||||
ignoreIK: ignoreIK
|
||||
});
|
||||
if (!success) {
|
||||
print("pullTowardEquipPosition -- updateActionfailed");
|
||||
}
|
||||
}
|
||||
|
||||
if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) {
|
||||
|
@ -1849,7 +1900,31 @@ function cleanup() {
|
|||
rightController.cleanup();
|
||||
leftController.cleanup();
|
||||
Controller.disableMapping(MAPPING_NAME);
|
||||
if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) {
|
||||
Script.update.disconnect(renewParticleBeamLifetimes);
|
||||
}
|
||||
}
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Script.update.connect(update);
|
||||
|
||||
// particle systems can end up hanging around if a user crashes or something else causes controller cleanup not to get called.
|
||||
// we can't create the search system on-demand since it takes some time for the particles to reach their entire length.
|
||||
// thus the system cannot have a fixed lifetime. this loop updates the lifetimes and will stop updating if a user crashes.
|
||||
|
||||
if (USE_PARTICLE_BEAM_FOR_SEARCHING === true || USE_PARTICLE_BEAM_FOR_MOVING === true) {
|
||||
Script.update.connect(renewParticleBeamLifetimes)
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Script.update.connect(update);
|
||||
var sinceLastParticleLifetimeUpdate = 0;
|
||||
|
||||
function renewParticleBeamLifetimes(deltaTime) {
|
||||
//debounce this call since we don't want it 60x a second
|
||||
sinceLastParticleLifetimeUpdate = sinceLastParticleLifetimeUpdate + deltaTime;
|
||||
if (sinceLastParticleLifetimeUpdate > TEMPORARY_PARTICLE_BEAM_LIFETIME - 2) {
|
||||
sinceLastParticleLifetimeUpdate = 0;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
rightController.renewParticleBeamLifetime();
|
||||
leftController.renewParticleBeamLifetime();
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// actionInspector.js
|
||||
// examples
|
||||
// examples/debugging/
|
||||
//
|
||||
// Created by Seth Alves on 2015-9-30.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
|
@ -9,7 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("libraries/utils.js");
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
|
||||
var INSPECT_RADIUS = 10;
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// grabInspector.js
|
||||
// examples
|
||||
// examples/debugging/
|
||||
//
|
||||
// Created by Seth Alves on 2015-9-30.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
|
@ -9,7 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("libraries/utils.js");
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
var INSPECT_RADIUS = 10;
|
||||
var overlays = {};
|
58
examples/debugging/queryAACubeInspector.js
Normal file
58
examples/debugging/queryAACubeInspector.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// grabInspector.js
|
||||
// examples/debugging/
|
||||
//
|
||||
// Created by Seth Alves on 2015-12-19.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// This script draws an overlay cube around nearby entities to show their queryAABoxes.
|
||||
|
||||
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
var INSPECT_RADIUS = 10;
|
||||
var overlays = {};
|
||||
|
||||
function updateOverlay(entityID, queryAACube) {
|
||||
var cubeCenter = {x: queryAACube.x + queryAACube.scale / 2.0,
|
||||
y: queryAACube.y + queryAACube.scale / 2.0,
|
||||
z: queryAACube.z + queryAACube.scale / 2.0};
|
||||
|
||||
if (entityID in overlays) {
|
||||
var overlay = overlays[entityID];
|
||||
Overlays.editOverlay(overlay, {
|
||||
position: cubeCenter,
|
||||
size: queryAACube.scale
|
||||
});
|
||||
} else {
|
||||
overlays[entityID] = Overlays.addOverlay("cube", {
|
||||
position: cubeCenter,
|
||||
size: queryAACube.scale,
|
||||
color: { red: 0, green: 0, blue: 255},
|
||||
alpha: 1,
|
||||
// borderSize: ...,
|
||||
solid: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Script.setInterval(function() {
|
||||
var nearbyEntities = Entities.findEntities(MyAvatar.position, INSPECT_RADIUS);
|
||||
for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) {
|
||||
var entityID = nearbyEntities[entityIndex];
|
||||
var queryAACube = Entities.getEntityProperties(entityID, ["queryAACube"]).queryAACube;
|
||||
updateOverlay(entityID, queryAACube);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
function cleanup() {
|
||||
for (var entityID in overlays) {
|
||||
Overlays.deleteOverlay(overlays[entityID]);
|
||||
}
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -62,8 +62,13 @@ var directory = (function () {
|
|||
function setUp() {
|
||||
viewport = Controller.getViewportDimensions();
|
||||
|
||||
directoryWindow = new OverlayWebWindow('Directory', DIRECTORY_URL, 900, 700, false);
|
||||
directoryWindow.setVisible(false);
|
||||
directoryWindow = new OverlayWebWindow({
|
||||
title: 'Directory',
|
||||
source: DIRECTORY_URL,
|
||||
width: 900,
|
||||
height: 700,
|
||||
visible: false
|
||||
});
|
||||
|
||||
directoryButton = Overlays.addOverlay("image", {
|
||||
imageURL: DIRECTORY_BUTTON_URL,
|
||||
|
|
|
@ -140,8 +140,13 @@ var importingSVOTextOverlay = Overlays.addOverlay("text", {
|
|||
});
|
||||
|
||||
var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace";
|
||||
var marketplaceWindow = new OverlayWebWindow('Marketplace', "about:blank", 900, 700, false);
|
||||
marketplaceWindow.setVisible(false);
|
||||
var marketplaceWindow = new OverlayWebWindow({
|
||||
title: 'Marketplace',
|
||||
source: "about:blank",
|
||||
width: 900,
|
||||
height: 700,
|
||||
visible: false
|
||||
});
|
||||
|
||||
function showMarketplace(marketplaceID) {
|
||||
var url = MARKETPLACE_URL;
|
||||
|
|
|
@ -16,5 +16,12 @@
|
|||
|
||||
this.collisionWithEntity = function(myID, otherID, collisionInfo) {
|
||||
Entities.editEntity(myID, { color: { red: getRandomInt(128,255), green: getRandomInt(128,255), blue: getRandomInt(128,255)} });
|
||||
print("collisionWithEntity() myID:" + myID + ", otherID:" + otherID);
|
||||
print(" collisionInfo.type:" + collisionInfo.type);
|
||||
print(" collisionInfo.idA:" + collisionInfo.idA);
|
||||
print(" collisionInfo.idB:" + collisionInfo.idB);
|
||||
Vec3.print(" collisionInfo.penetration:", collisionInfo.penetration);
|
||||
Vec3.print(" collisionInfo.contactPoint:", collisionInfo.contactPoint);
|
||||
Vec3.print(" collisionInfo.velocityChange:", collisionInfo.velocityChange);
|
||||
};
|
||||
})
|
121
examples/example/avatarcontrol/doppelganger.js
Normal file
121
examples/example/avatarcontrol/doppelganger.js
Normal file
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// doppelganger.js
|
||||
//
|
||||
// Created by James B. Pollack @imgntn on 12/28/2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This script shows how to hook up a model entity to your avatar to act as a doppelganger.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
// To-Do: mirror joints, rotate avatar fully, automatically get avatar fbx, make sure dimensions for avatar are right when u bring it in
|
||||
|
||||
var TEST_MODEL_URL = 'https://s3.amazonaws.com/hifi-public/ozan/avatars/albert/albert/albert.fbx';
|
||||
|
||||
var doppelgangers = [];
|
||||
|
||||
function Doppelganger(avatar) {
|
||||
this.initialProperties = {
|
||||
name: 'Hifi-Doppelganger',
|
||||
type: 'Model',
|
||||
modelURL: TEST_MODEL_URL,
|
||||
// dimensions: getAvatarDimensions(avatar),
|
||||
position: putDoppelgangerAcrossFromAvatar(this, avatar),
|
||||
rotation: rotateDoppelgangerTowardAvatar(this, avatar),
|
||||
};
|
||||
|
||||
this.id = createDoppelgangerEntity(this);
|
||||
this.avatar = avatar;
|
||||
return this;
|
||||
}
|
||||
|
||||
function getJointData(avatar) {
|
||||
var allJointData = [];
|
||||
var jointNames = MyAvatar.jointNames;
|
||||
jointNames.forEach(function(joint, index) {
|
||||
var translation = MyAvatar.getJointTranslation(index);
|
||||
var rotation = MyAvatar.getJointRotation(index)
|
||||
allJointData.push({
|
||||
joint: joint,
|
||||
index: index,
|
||||
translation: translation,
|
||||
rotation: rotation
|
||||
})
|
||||
});
|
||||
|
||||
return allJointData;
|
||||
}
|
||||
|
||||
function setJointData(doppelganger, allJointData) {
|
||||
var jointRotations = [];
|
||||
allJointData.forEach(function(jointData, index) {
|
||||
jointRotations.push(jointData.rotation);
|
||||
});
|
||||
Entities.setAbsoluteJointRotationsInObjectFrame(doppelganger.id, jointRotations);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function mirrorJointData() {
|
||||
return mirroredJointData;
|
||||
}
|
||||
|
||||
function createDoppelganger(avatar) {
|
||||
return new Doppelganger(avatar);
|
||||
}
|
||||
|
||||
function createDoppelgangerEntity(doppelganger) {
|
||||
return Entities.addEntity(doppelganger.initialProperties);
|
||||
}
|
||||
|
||||
function putDoppelgangerAcrossFromAvatar(doppelganger, avatar) {
|
||||
var avatarRot = Quat.fromPitchYawRollDegrees(0, avatar.bodyYaw, 0.0);
|
||||
var basePosition = Vec3.sum(avatar.position, Vec3.multiply(1.5, Quat.getFront(avatarRot)));
|
||||
return basePosition;
|
||||
}
|
||||
|
||||
function getAvatarDimensions(avatar) {
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
function rotateDoppelgangerTowardAvatar(doppelganger, avatar) {
|
||||
var avatarRot = Quat.fromPitchYawRollDegrees(0, avatar.bodyYaw, 0.0);
|
||||
avatarRot = Vec3.multiply(-1, avatarRot);
|
||||
return avatarRot;
|
||||
}
|
||||
|
||||
function connectDoppelgangerUpdates() {
|
||||
// Script.update.connect(updateDoppelganger);
|
||||
Script.setInterval(updateDoppelganger, 100);
|
||||
}
|
||||
|
||||
function disconnectDoppelgangerUpdates() {
|
||||
Script.update.disconnect(updateDoppelganger);
|
||||
}
|
||||
|
||||
function updateDoppelganger() {
|
||||
doppelgangers.forEach(function(doppelganger) {
|
||||
var joints = getJointData(MyAvatar);
|
||||
//var mirroredJoints = mirrorJointData(joints);
|
||||
setJointData(doppelganger, joints);
|
||||
});
|
||||
}
|
||||
|
||||
function makeDoppelgangerForMyAvatar() {
|
||||
var doppelganger = createDoppelganger(MyAvatar);
|
||||
doppelgangers.push(doppelganger);
|
||||
connectDoppelgangerUpdates();
|
||||
}
|
||||
|
||||
makeDoppelgangerForMyAvatar();
|
||||
|
||||
function cleanup() {
|
||||
disconnectDoppelgangerUpdates();
|
||||
|
||||
doppelgangers.forEach(function(doppelganger) {
|
||||
Entities.deleteEntity(doppelganger.id);
|
||||
});
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -71,7 +71,7 @@
|
|||
Entities.editEntity(particles, {
|
||||
radiusSpread: 0.0,
|
||||
radiusStart: 0.0,
|
||||
particleRadius: 2 * PARTICLE_RADIUS, // Bezier interpolation used means that middle value isn't intersected
|
||||
particleRadius: PARTICLE_RADIUS,
|
||||
radiusFinish: 0.0
|
||||
});
|
||||
break;
|
||||
|
|
|
@ -24,6 +24,9 @@ RaveStick = function(spawnPosition) {
|
|||
green: 10,
|
||||
blue: 40
|
||||
}];
|
||||
|
||||
|
||||
|
||||
var stick = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "raveStick",
|
||||
|
@ -40,23 +43,25 @@ RaveStick = function(spawnPosition) {
|
|||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
spatialKey: {
|
||||
rightRelativePosition: {
|
||||
x: 0.02,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
leftRelativePosition: {
|
||||
x: -0.02,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0)
|
||||
rightRelativePosition: {
|
||||
x: 0.02,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
leftRelativePosition: {
|
||||
x: -0.02,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
relativeRotation: Quat.fromPitchYawRollDegrees(90, 90, 0)
|
||||
},
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
var glowEmitter = createGlowEmitter();
|
||||
|
||||
var light = Entities.addEntity({
|
||||
type: 'Light',
|
||||
name: "raveLight",
|
||||
|
@ -82,14 +87,76 @@ RaveStick = function(spawnPosition) {
|
|||
green: 200,
|
||||
blue: 40
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(stick);
|
||||
Entities.deleteEntity(light);
|
||||
Entities.deleteEntity(glowEmitter);
|
||||
}
|
||||
|
||||
this.cleanup = cleanup;
|
||||
}
|
||||
|
||||
function createGlowEmitter() {
|
||||
var props = Entities.getEntityProperties(stick, ["position", "rotation"]);
|
||||
var forwardVec = Quat.getFront(props.rotation);
|
||||
var forwardQuat = Quat.rotationBetween(Vec3.UNIT_Z, forwardVec);
|
||||
var position = props.position;
|
||||
var color = {
|
||||
red: 150,
|
||||
green: 20,
|
||||
blue: 100
|
||||
}
|
||||
var props = {
|
||||
type: "ParticleEffect",
|
||||
name: "Rave Stick Glow Emitter",
|
||||
position: position,
|
||||
parentID: stick,
|
||||
isEmitting: true,
|
||||
colorStart: color,
|
||||
color: {
|
||||
red: 200,
|
||||
green: 200,
|
||||
blue: 255
|
||||
},
|
||||
colorFinish: color,
|
||||
maxParticles: 100000,
|
||||
lifespan: 0.8,
|
||||
emitRate: 1000,
|
||||
emitOrientation: forwardQuat,
|
||||
emitSpeed: 0.2,
|
||||
speedSpread: 0.0,
|
||||
emitDimensions: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
polarStart: 0,
|
||||
polarFinish: 0,
|
||||
azimuthStart: 0.1,
|
||||
azimuthFinish: 0.01,
|
||||
emitAcceleration: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
accelerationSpread: {
|
||||
x: 0.00,
|
||||
y: 0.00,
|
||||
z: 0.00
|
||||
},
|
||||
radiusStart: 0.01,
|
||||
radiusFinish: 0.005,
|
||||
alpha: 0.7,
|
||||
alphaSpread: 0.1,
|
||||
alphaStart: 0.1,
|
||||
alphaFinish: 0.1,
|
||||
textures: "https://s3.amazonaws.com/hifi-public/eric/textures/particleSprites/beamParticle.png",
|
||||
emitterShouldTrail: false
|
||||
}
|
||||
var glowEmitter = Entities.addEntity(props);
|
||||
return glowEmitter;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ Grabber.prototype.computeNewGrabPlane = function() {
|
|||
}
|
||||
|
||||
Grabber.prototype.pressEvent = function(event) {
|
||||
if (!event.isLeftButton) {
|
||||
if (event.isLeftButton!==true ||event.isRightButton===true || event.isMiddleButton===true) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -374,7 +374,11 @@ Grabber.prototype.pressEvent = function(event) {
|
|||
//Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME });
|
||||
}
|
||||
|
||||
Grabber.prototype.releaseEvent = function() {
|
||||
Grabber.prototype.releaseEvent = function(event) {
|
||||
if (event.isLeftButton!==true ||event.isRightButton===true || event.isMiddleButton===true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isGrabbing) {
|
||||
this.deactivateEntity(this.entityID);
|
||||
this.isGrabbing = false
|
||||
|
|
13
examples/tests/qmlTest.js
Normal file
13
examples/tests/qmlTest.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
print("Launching web window");
|
||||
qmlWindow = new OverlayWindow({
|
||||
title: 'Test Qml',
|
||||
source: "https://s3.amazonaws.com/DreamingContent/qml/content.qml",
|
||||
height: 240,
|
||||
width: 320,
|
||||
toolWindow: false,
|
||||
visible: true
|
||||
});
|
||||
|
||||
Script.setInterval(function() {
|
||||
qmlWindow.raise();
|
||||
}, 2 * 1000);
|
|
@ -1,6 +1,7 @@
|
|||
print("Launching web window");
|
||||
|
||||
webWindow = new OverlayWebWindow('Test Event Bridge', "file:///C:/Users/bdavis/Git/hifi/examples/html/qmlWebTest.html", 320, 240, false);
|
||||
var htmlUrl = Script.resolvePath("..//html/qmlWebTest.html")
|
||||
webWindow = new OverlayWebWindow('Test Event Bridge', htmlUrl, 320, 240, false);
|
||||
print("JS Side window: " + webWindow);
|
||||
print("JS Side bridge: " + webWindow.eventBridge);
|
||||
webWindow.eventBridge.webEventReceived.connect(function(data) {
|
||||
|
|
|
@ -17,34 +17,34 @@
|
|||
}
|
||||
|
||||
Target.prototype = {
|
||||
hasPlayedSound: false,
|
||||
hasBecomeActive: false,
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
var SOUND_URL = "http://hifi-public.s3.amazonaws.com/sounds/Clay_Pigeon_02.L.wav";
|
||||
this.hitSound = SoundCache.getSound(SOUND_URL);
|
||||
},
|
||||
collisionWithEntity: function(me, otherEntity) {
|
||||
var position = Entities.getEntityProperties(me, "position").position;
|
||||
Entities.editEntity(me, {
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
}
|
||||
});
|
||||
if (this.hasBecomeActive === false) {
|
||||
var position = Entities.getEntityProperties(me, "position").position;
|
||||
Entities.editEntity(me, {
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -0.01,
|
||||
z: 0
|
||||
}
|
||||
});
|
||||
|
||||
if (this.hasPlayedSound === false) {
|
||||
this.audioInjector = Audio.playSound(this.hitSound, {
|
||||
position: position,
|
||||
volume: 0.5
|
||||
});
|
||||
|
||||
this.hasPlayedSound = true;
|
||||
this.hasBecomeActive = true;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,13 @@
|
|||
this.bulletForce = 10;
|
||||
this.showLaser = false;
|
||||
|
||||
this.laserOffsets = {
|
||||
y: 0.095
|
||||
};
|
||||
this.firingOffsets = {
|
||||
z: 0.16
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Pistol.prototype = {
|
||||
|
@ -272,46 +279,12 @@
|
|||
});
|
||||
}, 100);
|
||||
|
||||
Entities.editEntity(this.flash, {
|
||||
isEmitting: true
|
||||
});
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(_this.flash, {
|
||||
isEmitting: false
|
||||
});
|
||||
}, 100)
|
||||
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.laser = Overlays.addOverlay("line3d", {
|
||||
start: ZERO_VECTOR,
|
||||
end: ZERO_VECTOR,
|
||||
color: COLORS.RED,
|
||||
alpha: 1,
|
||||
visible: true,
|
||||
lineWidth: 2
|
||||
});
|
||||
this.laserOffsets = {
|
||||
y: 0.095
|
||||
};
|
||||
this.firingOffsets = {
|
||||
z: 0.16
|
||||
}
|
||||
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
var position = gunProps.position;
|
||||
var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
this.firingDirection = Quat.getFront(rotation);
|
||||
var upVec = Quat.getUp(rotation);
|
||||
this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y));
|
||||
this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z))
|
||||
|
||||
this.flash = Entities.addEntity({
|
||||
var flash = Entities.addEntity({
|
||||
type: "ParticleEffect",
|
||||
position: this.barrelPoint,
|
||||
"name": "Muzzle Flash",
|
||||
isEmitting: false,
|
||||
lifetime: 4,
|
||||
parentID: this.entityID,
|
||||
"color": {
|
||||
red: 228,
|
||||
green: 128,
|
||||
|
@ -363,14 +336,27 @@
|
|||
"additiveBlending": true,
|
||||
"textures": "http://ericrius1.github.io/PartiArt/assets/star.png"
|
||||
});
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(flash, {
|
||||
isEmitting: false
|
||||
});
|
||||
}, 100)
|
||||
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(_this.flash, {parentID: _this.entityID});
|
||||
}, 500)
|
||||
},
|
||||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.laser = Overlays.addOverlay("line3d", {
|
||||
start: ZERO_VECTOR,
|
||||
end: ZERO_VECTOR,
|
||||
color: COLORS.RED,
|
||||
alpha: 1,
|
||||
visible: true,
|
||||
lineWidth: 2
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// entity scripts always need to return a newly constructed object of our type
|
||||
return new Pistol();
|
||||
});
|
||||
});
|
|
@ -20,12 +20,10 @@ find_package(Qt5LinguistTools REQUIRED)
|
|||
find_package(Qt5LinguistToolsMacros)
|
||||
|
||||
if (WIN32)
|
||||
add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h
|
||||
add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines
|
||||
add_definitions(-D_USE_MATH_DEFINES) # apparently needed to get M_PI and other defines from cmath/math.h
|
||||
add_definitions(-DWINDOWS_LEAN_AND_MEAN) # needed to make sure windows doesn't go to crazy with its defines
|
||||
endif()
|
||||
|
||||
include_application_version()
|
||||
|
||||
# grab the implementation and header files from src dirs
|
||||
file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h")
|
||||
GroupSources("src")
|
||||
|
@ -45,8 +43,8 @@ else ()
|
|||
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
|
||||
endif ()
|
||||
|
||||
find_package(Qt5 COMPONENTS
|
||||
Gui Multimedia Network OpenGL Qml Quick Script Svg
|
||||
find_package(Qt5 COMPONENTS
|
||||
Gui Multimedia Network OpenGL Qml Quick Script Svg
|
||||
WebChannel WebEngine WebEngineWidgets WebKitWidgets WebSockets)
|
||||
|
||||
# grab the ui files in resources/ui
|
||||
|
@ -69,19 +67,19 @@ if (APPLE)
|
|||
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME Interface)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER io.highfidelity.Interface)
|
||||
|
||||
|
||||
if (UPPER_CMAKE_BUILD_TYPE MATCHES RELEASE OR UPPER_CMAKE_BUILD_TYPE MATCHES RELWITHDEBINFO)
|
||||
set(ICON_FILENAME "interface.icns")
|
||||
else ()
|
||||
set(ICON_FILENAME "interface-beta.icns")
|
||||
endif ()
|
||||
|
||||
|
||||
# set how the icon shows up in the Info.plist file
|
||||
SET(MACOSX_BUNDLE_ICON_FILE "${ICON_FILENAME}")
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
set(DISCOVERED_RESOURCES "")
|
||||
|
||||
# use the add_resources_to_os_x_bundle macro to recurse into resources
|
||||
|
@ -89,7 +87,7 @@ if (APPLE)
|
|||
|
||||
# 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()
|
||||
|
||||
|
@ -102,7 +100,9 @@ else()
|
|||
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
||||
endif()
|
||||
|
||||
# These are external plugins, but we need to do the 'add dependency' here so that their
|
||||
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes")
|
||||
|
||||
# These are external plugins, but we need to do the 'add dependency' here so that their
|
||||
# binary directories get added to the fixup path
|
||||
add_dependency_external_projects(sixense)
|
||||
add_dependency_external_projects(sdl2)
|
||||
|
@ -121,11 +121,14 @@ if (WIN32)
|
|||
endif()
|
||||
|
||||
# link required hifi libraries
|
||||
link_hifi_libraries(shared octree environment gpu gl procedural model render
|
||||
recording fbx networking model-networking entities avatars
|
||||
audio audio-client animation script-engine physics
|
||||
link_hifi_libraries(shared octree environment gpu gl procedural model render
|
||||
recording fbx networking model-networking entities avatars
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer ui auto-updater
|
||||
controllers plugins display-plugins input-plugins )
|
||||
controllers plugins display-plugins input-plugins)
|
||||
|
||||
# include the binary directory of render-utils for shader includes
|
||||
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/libraries/render-utils")
|
||||
|
||||
#fixme find a way to express faceshift as a plugin
|
||||
target_bullet()
|
||||
|
@ -138,34 +141,34 @@ endif()
|
|||
|
||||
# perform standard include and linking for found externals
|
||||
foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||
|
||||
|
||||
if (${${EXTERNAL}_UPPERCASE}_REQUIRED)
|
||||
find_package(${EXTERNAL} REQUIRED)
|
||||
else ()
|
||||
find_package(${EXTERNAL})
|
||||
endif ()
|
||||
|
||||
|
||||
if (${${EXTERNAL}_UPPERCASE}_FOUND AND NOT DISABLE_${${EXTERNAL}_UPPERCASE})
|
||||
add_definitions(-DHAVE_${${EXTERNAL}_UPPERCASE})
|
||||
|
||||
|
||||
# include the library directories (ignoring warnings)
|
||||
if (NOT ${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS)
|
||||
set(${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIR})
|
||||
endif ()
|
||||
|
||||
|
||||
include_directories(SYSTEM ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS})
|
||||
|
||||
|
||||
# perform the system include hack for OS X to ignore warnings
|
||||
if (APPLE)
|
||||
foreach(EXTERNAL_INCLUDE_DIR ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS})
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${EXTERNAL_INCLUDE_DIR}")
|
||||
endforeach()
|
||||
endif ()
|
||||
|
||||
|
||||
if (NOT ${${EXTERNAL}_UPPERCASE}_LIBRARIES)
|
||||
set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY})
|
||||
endif ()
|
||||
|
||||
|
||||
if (NOT APPLE OR NOT ${${EXTERNAL}_UPPERCASE} MATCHES "SIXENSE")
|
||||
target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES})
|
||||
elseif (APPLE AND NOT INSTALLER_BUILD)
|
||||
|
@ -179,13 +182,13 @@ include_directories("${PROJECT_SOURCE_DIR}/src")
|
|||
|
||||
target_link_libraries(
|
||||
${TARGET_NAME}
|
||||
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
|
||||
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
|
||||
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
|
||||
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
|
||||
Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets
|
||||
)
|
||||
|
||||
# Issue causes build failure unless we add this directory.
|
||||
# See https://bugreports.qt.io/browse/QTBUG-43351
|
||||
# Issue causes build failure unless we add this directory.
|
||||
# See https://bugreports.qt.io/browse/QTBUG-43351
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine)
|
||||
endif()
|
||||
|
@ -199,7 +202,7 @@ if (APPLE)
|
|||
find_library(AppKit AppKit)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} ${OpenGL} ${AppKit})
|
||||
|
||||
|
||||
# install command for OS X bundle
|
||||
INSTALL(TARGETS ${TARGET_NAME}
|
||||
BUNDLE DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/install" COMPONENT Runtime
|
||||
|
@ -229,4 +232,4 @@ if (WIN32)
|
|||
endif()
|
||||
|
||||
package_libraries_for_deployment()
|
||||
consolidate_stack_components()
|
||||
consolidate_installer_components()
|
||||
|
|
61
interface/resources/controllers/standard_navigation.json
Normal file
61
interface/resources/controllers/standard_navigation.json
Normal file
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"name": "Standard to Action",
|
||||
"when": "Application.NavigationFocused",
|
||||
"channels": [
|
||||
{ "disabled_from": { "makeAxis" : [ "Standard.DD", "Standard.DU" ] }, "to": "Actions.UiNavVertical" },
|
||||
{ "disabled_from": { "makeAxis" : [ "Standard.DL", "Standard.DR" ] }, "to": "Actions.UiNavLateral" },
|
||||
{ "disabled_from": { "makeAxis" : [ "Standard.LB", "Standard.RB" ] }, "to": "Actions.UiNavGroup" },
|
||||
{ "from": "Standard.DU", "to": "Actions.UiNavVertical" },
|
||||
{ "from": "Standard.DD", "to": "Actions.UiNavVertical", "filters": "invert" },
|
||||
{ "from": "Standard.DL", "to": "Actions.UiNavLateral", "filters": "invert" },
|
||||
{ "from": "Standard.DR", "to": "Actions.UiNavLateral" },
|
||||
{ "from": "Standard.LB", "to": "Actions.UiNavGroup","filters": "invert" },
|
||||
{ "from": "Standard.RB", "to": "Actions.UiNavGroup" },
|
||||
{ "from": [ "Standard.A", "Standard.X" ], "to": "Actions.UiNavSelect" },
|
||||
{ "from": [ "Standard.B", "Standard.Y", "Standard.RightPrimaryThumb", "Standard.LeftPrimaryThumb" ], "to": "Actions.UiNavBack" },
|
||||
{
|
||||
"from": [ "Standard.RT", "Standard.LT" ],
|
||||
"to": "Actions.UiNavSelect",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.5 },
|
||||
"constrainToInteger"
|
||||
]
|
||||
},
|
||||
{
|
||||
"from": "Standard.LX", "to": "Actions.UiNavLateral",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.95 },
|
||||
"constrainToInteger",
|
||||
{ "type": "pulse", "interval": 0.4 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"from": "Standard.LY", "to": "Actions.UiNavVertical",
|
||||
"filters": [
|
||||
"invert",
|
||||
{ "type": "deadZone", "min": 0.95 },
|
||||
"constrainToInteger",
|
||||
{ "type": "pulse", "interval": 0.4 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"from": "Standard.RX", "to": "Actions.UiNavLateral",
|
||||
"filters": [
|
||||
{ "type": "deadZone", "min": 0.95 },
|
||||
"constrainToInteger",
|
||||
{ "type": "pulse", "interval": 0.4 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"from": "Standard.RY", "to": "Actions.UiNavVertical",
|
||||
"filters": [
|
||||
"invert",
|
||||
{ "type": "deadZone", "min": 0.95 },
|
||||
"constrainToInteger",
|
||||
{ "type": "pulse", "interval": 0.4 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ import "styles"
|
|||
DialogContainer {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
z: 1000
|
||||
|
||||
objectName: "AddressBarDialog"
|
||||
|
||||
|
@ -150,6 +151,17 @@ DialogContainer {
|
|||
addressLine.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
running: root.enabled
|
||||
interval: 500
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (root.enabled && !addressLine.activeFocus) {
|
||||
addressLine.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
|
@ -169,8 +181,10 @@ DialogContainer {
|
|||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
enabled = false
|
||||
event.accepted = true
|
||||
if (enabled) {
|
||||
enabled = false
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
|
|
|
@ -84,6 +84,7 @@ Hifi.AvatarInputs {
|
|||
Item {
|
||||
width: root.mirrorWidth
|
||||
height: 44
|
||||
visible: !root.isHMD
|
||||
|
||||
x: root.mirrorLeftPad
|
||||
y: root.mirrorVisible ? root.mirrorTopPad + root.mirrorHeight : 5
|
||||
|
|
|
@ -96,6 +96,9 @@ DialogContainer {
|
|||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
|
|
165
interface/resources/qml/Global.js
Normal file
165
interface/resources/qml/Global.js
Normal file
|
@ -0,0 +1,165 @@
|
|||
var OFFSCREEN_ROOT_OBJECT_NAME = "desktopRoot"
|
||||
var OFFSCREEN_WINDOW_OBJECT_NAME = "topLevelWindow"
|
||||
|
||||
function findChild(item, name) {
|
||||
for (var i = 0; i < item.children.length; ++i) {
|
||||
if (item.children[i].objectName === name) {
|
||||
return item.children[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findParent(item, name) {
|
||||
while (item) {
|
||||
if (item.objectName === name) {
|
||||
return item;
|
||||
}
|
||||
item = item.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getDesktop(item) {
|
||||
return findParent(item, OFFSCREEN_ROOT_OBJECT_NAME);
|
||||
}
|
||||
|
||||
function findRootMenu(item) {
|
||||
item = getDesktop(item);
|
||||
return item ? item.rootMenu : null;
|
||||
}
|
||||
|
||||
|
||||
function getTopLevelWindows(item) {
|
||||
var desktop = getDesktop(item);
|
||||
var currentWindows = [];
|
||||
if (!desktop) {
|
||||
console.log("Could not find desktop for " + item)
|
||||
return currentWindows;
|
||||
}
|
||||
|
||||
for (var i = 0; i < desktop.children.length; ++i) {
|
||||
var child = desktop.children[i];
|
||||
if (Global.OFFSCREEN_WINDOW_OBJECT_NAME === child.objectName) {
|
||||
var windowId = child.toString();
|
||||
currentWindows.push(child)
|
||||
}
|
||||
}
|
||||
return currentWindows;
|
||||
}
|
||||
|
||||
|
||||
function getDesktopWindow(item) {
|
||||
item = findParent(item, OFFSCREEN_WINDOW_OBJECT_NAME);
|
||||
return item;
|
||||
}
|
||||
|
||||
function closeWindow(item) {
|
||||
item = findDialog(item);
|
||||
if (item) {
|
||||
item.enabled = false
|
||||
} else {
|
||||
console.warn("Could not find top level dialog")
|
||||
}
|
||||
}
|
||||
|
||||
function findMenuChild(menu, childName) {
|
||||
if (!menu) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (menu.type !== 2) {
|
||||
console.warn("Tried to find child of a non-menu");
|
||||
return null;
|
||||
}
|
||||
|
||||
var items = menu.items;
|
||||
var count = items.length;
|
||||
for (var i = 0; i < count; ++i) {
|
||||
var child = items[i];
|
||||
var name;
|
||||
switch (child.type) {
|
||||
case 2:
|
||||
name = child.title;
|
||||
break;
|
||||
case 1:
|
||||
name = child.text;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (name && name === childName) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findMenu(rootMenu, path) {
|
||||
if ('string' === typeof(path)) {
|
||||
path = [ path ]
|
||||
}
|
||||
|
||||
var currentMenu = rootMenu;
|
||||
for (var i = 0; currentMenu && i < path.length; ++i) {
|
||||
currentMenu = findMenuChild(currentMenu, path[i]);
|
||||
}
|
||||
|
||||
return currentMenu;
|
||||
}
|
||||
|
||||
function findInRootMenu(item, path) {
|
||||
return findMenu(findRootMenu(item), path);
|
||||
}
|
||||
|
||||
|
||||
function menuItemsToListModel(parent, items) {
|
||||
var newListModel = Qt.createQmlObject('import QtQuick 2.5; ListModel {}', parent);
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
var item = items[i];
|
||||
switch (item.type) {
|
||||
case 2:
|
||||
newListModel.append({"type":item.type, "name": item.title, "item": item})
|
||||
break;
|
||||
case 1:
|
||||
newListModel.append({"type":item.type, "name": item.text, "item": item})
|
||||
break;
|
||||
case 0:
|
||||
newListModel.append({"type":item.type, "name": "-----", "item": item})
|
||||
break;
|
||||
}
|
||||
}
|
||||
return newListModel;
|
||||
}
|
||||
|
||||
function raiseWindow(item) {
|
||||
var targetWindow = getDesktopWindow(item);
|
||||
if (!targetWindow) {
|
||||
console.warn("Could not find top level window for " + item);
|
||||
return;
|
||||
}
|
||||
|
||||
var desktop = getDesktop(targetWindow);
|
||||
if (!desktop) {
|
||||
//console.warn("Could not find desktop for window " + targetWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
var maxZ = 0;
|
||||
var minZ = 100000;
|
||||
var windows = desktop.windows;
|
||||
windows.sort(function(a, b){
|
||||
return a.z - b.z;
|
||||
});
|
||||
var lastZ = -1;
|
||||
var lastTargetZ = -1;
|
||||
for (var i = 0; i < windows.length; ++i) {
|
||||
var window = windows[i];
|
||||
if (window.z > lastZ) {
|
||||
lastZ = window.z;
|
||||
++lastTargetZ;
|
||||
}
|
||||
window.z = lastTargetZ;
|
||||
}
|
||||
targetWindow.z = lastTargetZ + 1;
|
||||
}
|
|
@ -343,8 +343,10 @@ DialogContainer {
|
|||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
case Qt.Key_Back:
|
||||
enabled = false
|
||||
event.accepted = true
|
||||
if (enabled) {
|
||||
enabled = false
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
|
|
|
@ -300,6 +300,10 @@ VrDialog {
|
|||
|
||||
|
||||
Keys.onPressed: {
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.modifiers === Qt.ControlModifier)
|
||||
switch (event.key) {
|
||||
case Qt.Key_A:
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtWebEngine 1.1
|
||||
import QtWebChannel 1.0
|
||||
import QtWebSockets 1.0
|
||||
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
@ -13,6 +10,8 @@ VrDialog {
|
|||
HifiConstants { id: hifi }
|
||||
title: "WebWindow"
|
||||
resizable: true
|
||||
enabled: false
|
||||
visible: false
|
||||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false
|
||||
contentImplicitWidth: clientArea.implicitWidth
|
||||
|
@ -24,23 +23,13 @@ VrDialog {
|
|||
function stop() {
|
||||
webview.stop();
|
||||
}
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
enabled = true
|
||||
console.log("Web Window Created " + root);
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
webview.loadingChanged.connect(handleWebviewLoading)
|
||||
}
|
||||
|
||||
|
||||
function handleWebviewLoading(loadRequest) {
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
var newUrl = loadRequest.url.toString();
|
||||
root.navigating(newUrl)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -56,13 +45,38 @@ VrDialog {
|
|||
id: webview
|
||||
url: root.source
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
property var originalUrl
|
||||
property var lastFixupTime: 0
|
||||
|
||||
onUrlChanged: {
|
||||
var currentUrl = url.toString();
|
||||
var newUrl = urlFixer.fixupUrl(currentUrl);
|
||||
var newUrl = urlHandler.fixupUrl(currentUrl).toString();
|
||||
if (newUrl != currentUrl) {
|
||||
var now = new Date().valueOf();
|
||||
if (url === originalUrl && (now - lastFixupTime < 100)) {
|
||||
console.warn("URL fixup loop detected")
|
||||
return;
|
||||
}
|
||||
originalUrl = url
|
||||
lastFixupTime = now
|
||||
url = newUrl;
|
||||
}
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
// Required to support clicking on "hifi://" links
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
var url = loadRequest.url.toString();
|
||||
if (urlHandler.canHandleUrl(url)) {
|
||||
if (urlHandler.handleUrl(url)) {
|
||||
webview.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
profile: WebEngineProfile {
|
||||
id: webviewProfile
|
||||
httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)"
|
||||
|
|
56
interface/resources/qml/QmlWindow.qml
Normal file
56
interface/resources/qml/QmlWindow.qml
Normal file
|
@ -0,0 +1,56 @@
|
|||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtWebChannel 1.0
|
||||
import QtWebSockets 1.0
|
||||
import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel
|
||||
|
||||
import "Global.js" as Global
|
||||
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
VrDialog {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
title: "QmlWindow"
|
||||
resizable: true
|
||||
enabled: false
|
||||
visible: false
|
||||
focus: true
|
||||
property var channel;
|
||||
|
||||
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||
destroyOnCloseButton: false
|
||||
contentImplicitWidth: clientArea.implicitWidth
|
||||
contentImplicitHeight: clientArea.implicitHeight
|
||||
property alias source: pageLoader.source
|
||||
|
||||
function raiseWindow() {
|
||||
Global.raiseWindow(root)
|
||||
}
|
||||
|
||||
Item {
|
||||
id: clientArea
|
||||
implicitHeight: 600
|
||||
implicitWidth: 800
|
||||
x: root.clientX
|
||||
y: root.clientY
|
||||
width: root.clientWidth
|
||||
height: root.clientHeight
|
||||
focus: true
|
||||
clip: true
|
||||
|
||||
Loader {
|
||||
id: pageLoader
|
||||
objectName: "Loader"
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
property var dialog: root
|
||||
|
||||
Keys.onPressed: {
|
||||
console.log("QmlWindow pageLoader keypress")
|
||||
}
|
||||
}
|
||||
} // item
|
||||
} // dialog
|
|
@ -1,11 +1,23 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import "Global.js" as Global
|
||||
|
||||
// This is our primary 'window' object to which all dialogs and controls will
|
||||
// be childed.
|
||||
Root {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
objectName: Global.OFFSCREEN_ROOT_OBJECT_NAME
|
||||
anchors.fill: parent;
|
||||
property var windows: [];
|
||||
property var rootMenu: Menu {
|
||||
objectName: "rootMenu"
|
||||
}
|
||||
|
||||
onChildrenChanged: {
|
||||
windows = Global.getTopLevelWindows(root);
|
||||
}
|
||||
|
||||
onParentChanged: {
|
||||
forceActiveFocus();
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.3
|
||||
|
||||
Item {
|
||||
Menu {
|
||||
id: root
|
||||
objectName: "rootMenu"
|
||||
}
|
||||
}
|
|
@ -258,14 +258,14 @@ Item {
|
|||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
visible: root.showAcuity
|
||||
text: "LOD: " + root.lodStatus;
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
visible: root.expanded
|
||||
text: "Renderable avatars: " + root.avatarRenderableCount + " w/in " + root.avatarRenderDistance + "m";
|
||||
visible: root.expanded && !root.showAcuity
|
||||
text: root.lodStatsRenderText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
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"
|
||||
|
||||
|
@ -21,15 +23,12 @@ Hifi.VrMenu {
|
|||
property var models: []
|
||||
property var columns: []
|
||||
|
||||
|
||||
onEnabledChanged: {
|
||||
if (enabled && columns.length == 0) {
|
||||
pushColumn(rootMenu.items);
|
||||
}
|
||||
opacity = enabled ? 1.0 : 0.0
|
||||
if (enabled) {
|
||||
forceActiveFocus()
|
||||
}
|
||||
offscreenFlags.navigationFocused = enabled;
|
||||
}
|
||||
|
||||
// The actual animator
|
||||
|
@ -49,13 +48,12 @@ Hifi.VrMenu {
|
|||
}
|
||||
|
||||
property var menuBuilder: Component {
|
||||
Border {
|
||||
HifiConstants { id: hifi }
|
||||
property int menuDepth
|
||||
VrMenuView {
|
||||
property int menuDepth: root.models.length - 1
|
||||
model: root.models[menuDepth]
|
||||
|
||||
Component.onCompleted: {
|
||||
menuDepth = root.models.length - 1
|
||||
if (menuDepth == 0) {
|
||||
if (menuDepth === 0) {
|
||||
x = lastMousePosition.x - 20
|
||||
y = lastMousePosition.y - 20
|
||||
} else {
|
||||
|
@ -65,48 +63,8 @@ Hifi.VrMenu {
|
|||
}
|
||||
}
|
||||
|
||||
border.color: hifi.colors.hifiBlue
|
||||
color: hifi.colors.window
|
||||
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
|
||||
source: "VrMenuItem.qml"
|
||||
Binding {
|
||||
target: loader.item
|
||||
property: "menuContainer"
|
||||
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: "listView"
|
||||
value: listView
|
||||
when: loader.status == Loader.Ready
|
||||
}
|
||||
}
|
||||
}
|
||||
onSelected: {
|
||||
root.selectItem(menuDepth, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,14 +74,14 @@ Hifi.VrMenu {
|
|||
}
|
||||
|
||||
function pushColumn(items) {
|
||||
models.push(items)
|
||||
models.push(itemsToModel(items))
|
||||
if (columns.length) {
|
||||
var oldColumn = lastColumn();
|
||||
//oldColumn.enabled = false
|
||||
}
|
||||
var newColumn = menuBuilder.createObject(root);
|
||||
columns.push(newColumn);
|
||||
newColumn.forceActiveFocus();
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
function popColumn() {
|
||||
|
@ -145,13 +103,39 @@ Hifi.VrMenu {
|
|||
curColumn.forceActiveFocus();
|
||||
}
|
||||
|
||||
function selectItem(source) {
|
||||
function itemsToModel(items) {
|
||||
var newListModel = Qt.createQmlObject('import QtQuick 2.2; ListModel {}', root);
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
var item = items[i];
|
||||
switch (item.type) {
|
||||
case 2:
|
||||
newListModel.append({"type":item.type, "name": item.title, "item": item})
|
||||
break;
|
||||
case 1:
|
||||
newListModel.append({"type":item.type, "name": item.text, "item": item})
|
||||
break;
|
||||
case 0:
|
||||
newListModel.append({"type":item.type, "name": "-----", "item": item})
|
||||
break;
|
||||
}
|
||||
}
|
||||
return newListModel;
|
||||
}
|
||||
|
||||
function selectItem(depth, source) {
|
||||
var popped = false;
|
||||
while (depth + 1 < columns.length) {
|
||||
popColumn()
|
||||
popped = true
|
||||
}
|
||||
|
||||
switch (source.type) {
|
||||
case 2:
|
||||
lastColumn().enabled = false
|
||||
pushColumn(source.items)
|
||||
break;
|
||||
case 1:
|
||||
source.trigger()
|
||||
if (!popped) source.trigger()
|
||||
enabled = false
|
||||
break;
|
||||
case 0:
|
||||
|
@ -165,14 +149,6 @@ Hifi.VrMenu {
|
|||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
root.popColumn()
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
id: mouseArea
|
||||
|
@ -206,4 +182,36 @@ Hifi.VrMenu {
|
|||
function removeItem(menu, menuItem) {
|
||||
menu.removeItem(menuItem);
|
||||
}
|
||||
|
||||
function previousItem() {
|
||||
if (columns.length) {
|
||||
lastColumn().incrementCurrentIndex()
|
||||
}
|
||||
}
|
||||
|
||||
function nextItem() {
|
||||
if (columns.length) {
|
||||
lastColumn().decrementCurrentIndex()
|
||||
}
|
||||
}
|
||||
|
||||
function selectCurrentItem() {
|
||||
if (columns.length) {
|
||||
var depth = columns.length - 1;
|
||||
var index = lastColumn().currentIndex;
|
||||
if (index >= 0) {
|
||||
var model = models[depth];
|
||||
var item = model.get(index).item;
|
||||
selectItem(depth, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onDownPressed: previousItem();
|
||||
Keys.onUpPressed: nextItem();
|
||||
Keys.onSpacePressed: selectCurrentItem();
|
||||
Keys.onReturnPressed: selectCurrentItem();
|
||||
Keys.onRightPressed: selectCurrentItem();
|
||||
Keys.onLeftPressed: popColumn();
|
||||
Keys.onEscapePressed: popColumn();
|
||||
}
|
||||
|
|
|
@ -6,57 +6,18 @@ import "styles"
|
|||
|
||||
Item {
|
||||
id: root
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
// The model object
|
||||
property alias text: label.text
|
||||
property var source
|
||||
property var menuContainer
|
||||
property var listView
|
||||
|
||||
MouseArea {
|
||||
anchors.left: parent.left
|
||||
anchors.right: tag.right
|
||||
anchors.rightMargin: -4
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
acceptedButtons: Qt.LeftButton
|
||||
hoverEnabled: true
|
||||
|
||||
Rectangle {
|
||||
id: highlight
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
color: "#7f0e7077"
|
||||
}
|
||||
|
||||
onEntered: {
|
||||
//if (source.type == 2 && enabled) {
|
||||
// timer.start()
|
||||
//}
|
||||
highlight.visible = source.enabled
|
||||
}
|
||||
|
||||
onExited: {
|
||||
timer.stop()
|
||||
highlight.visible = false
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
select()
|
||||
}
|
||||
}
|
||||
|
||||
implicitHeight: source.visible ? label.implicitHeight * 1.5 : 0
|
||||
implicitWidth: label.implicitWidth + label.height * 2.5
|
||||
implicitWidth: label.width + label.height * 2.5
|
||||
visible: source.visible
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 1000
|
||||
onTriggered: parent.select()
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
|
||||
FontAwesome {
|
||||
clip: true
|
||||
|
@ -84,32 +45,18 @@ Item {
|
|||
|
||||
Text {
|
||||
id: label
|
||||
text: typedText()
|
||||
anchors.left: check.right
|
||||
anchors.leftMargin: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: source.enabled ? hifi.colors.text : hifi.colors.disabledText
|
||||
enabled: source.enabled && source.visible
|
||||
enabled: source.visible && (source.type !== 0 ? source.enabled : false)
|
||||
visible: source.visible
|
||||
function typedText() {
|
||||
if (source) {
|
||||
switch (source.type) {
|
||||
case 2:
|
||||
return source.title
|
||||
case 1:
|
||||
return source.text
|
||||
case 0:
|
||||
return "-----"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
FontAwesome {
|
||||
id: tag
|
||||
x: listView.width - width - 4
|
||||
x: root.parent.width - width
|
||||
size: label.height
|
||||
width: implicitWidth
|
||||
visible: source.visible && (source.type == 2)
|
||||
|
@ -117,17 +64,4 @@ Item {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: label.color
|
||||
}
|
||||
|
||||
function select() {
|
||||
//timer.stop();
|
||||
var popped = false
|
||||
while (columns.length - 1 > listView.parent.menuDepth) {
|
||||
popColumn()
|
||||
popped = true
|
||||
}
|
||||
|
||||
if (!popped || source.type != 1) {
|
||||
root.menuContainer.selectItem(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
77
interface/resources/qml/VrMenuView.qml
Normal file
77
interface/resources/qml/VrMenuView.qml
Normal file
|
@ -0,0 +1,77 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls 1.3
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
|
||||
import "styles"
|
||||
|
||||
ListView {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
width: 128
|
||||
height: count * 32
|
||||
onEnabledChanged: recalcSize();
|
||||
onVisibleChanged: recalcSize();
|
||||
onCountChanged: recalcSize();
|
||||
|
||||
signal selected(var item)
|
||||
|
||||
highlight: Rectangle {
|
||||
width: root.currentItem ? root.currentItem.width : 0
|
||||
height: root.currentItem ? root.currentItem.height : 0
|
||||
color: "lightsteelblue"; radius: 3
|
||||
}
|
||||
|
||||
delegate: VrMenuItem {
|
||||
text: name
|
||||
source: item
|
||||
onImplicitHeightChanged: root.recalcSize()
|
||||
onImplicitWidthChanged: root.recalcSize()
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: root.currentIndex = index
|
||||
onClicked: root.selected(item)
|
||||
}
|
||||
}
|
||||
|
||||
function recalcSize() {
|
||||
if (model.count !== count || !visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
var originalIndex = currentIndex;
|
||||
var maxWidth = width;
|
||||
var newHeight = 0;
|
||||
for (var i = 0; i < count; ++i) {
|
||||
currentIndex = i;
|
||||
if (!currentItem) {
|
||||
continue;
|
||||
}
|
||||
if (currentItem && currentItem.implicitWidth > maxWidth) {
|
||||
maxWidth = currentItem.implicitWidth
|
||||
}
|
||||
if (currentItem.visible) {
|
||||
newHeight += currentItem.implicitHeight
|
||||
}
|
||||
}
|
||||
if (maxWidth > width) {
|
||||
width = maxWidth;
|
||||
}
|
||||
if (newHeight > height) {
|
||||
height = newHeight
|
||||
}
|
||||
currentIndex = originalIndex;
|
||||
}
|
||||
|
||||
Border {
|
||||
id: border
|
||||
anchors.fill: parent
|
||||
anchors.margins: -8
|
||||
z: parent.z - 1
|
||||
border.color: hifi.colors.hifiBlue
|
||||
color: hifi.colors.window
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5,6 +5,7 @@ import "../styles"
|
|||
|
||||
Item {
|
||||
id: root
|
||||
objectName: "topLevelWindow"
|
||||
HifiConstants { id: hifi }
|
||||
implicitHeight: contentImplicitHeight + titleBorder.height + hifi.styles.borderWidth
|
||||
implicitWidth: contentImplicitWidth + hifi.styles.borderWidth * 2
|
||||
|
|
|
@ -3,6 +3,8 @@ import QtQuick.Controls 1.2
|
|||
import "."
|
||||
import "../styles"
|
||||
|
||||
import "../Global.js" as Global
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -41,8 +43,22 @@ DialogBase {
|
|||
// modify the visibility
|
||||
onEnabledChanged: {
|
||||
opacity = enabled ? 1.0 : 0.0
|
||||
// If the dialog is initially invisible, setting opacity doesn't
|
||||
// trigger making it visible.
|
||||
if (enabled) {
|
||||
visible = true;
|
||||
if (root.parent) {
|
||||
Global.raiseWindow(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onParentChanged: {
|
||||
if (enabled && parent) {
|
||||
Global.raiseWindow(root);
|
||||
}
|
||||
}
|
||||
|
||||
// The actual animator
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
|
@ -55,6 +71,12 @@ DialogBase {
|
|||
onOpacityChanged: {
|
||||
visible = (opacity != 0.0);
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (visible) {
|
||||
Global.raiseWindow(root);
|
||||
}
|
||||
}
|
||||
|
||||
// Some dialogs should be destroyed when they become invisible,
|
||||
// so handle that
|
||||
|
@ -113,6 +135,7 @@ DialogBase {
|
|||
y: root.titleY
|
||||
width: root.titleWidth
|
||||
height: root.titleHeight
|
||||
onClicked: Global.raiseWindow(root)
|
||||
|
||||
drag {
|
||||
target: root
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "Application.h"
|
||||
|
||||
#include <gl/Config.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/component_wise.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
@ -18,6 +20,8 @@
|
|||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QTimer>
|
||||
|
@ -28,7 +32,6 @@
|
|||
#include <QtGui/QImage>
|
||||
#include <QtGui/QWheelEvent>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtQml/QQmlContext>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
@ -36,6 +39,10 @@
|
|||
#include <QtNetwork/QLocalSocket>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
|
||||
#include <QtQml/QQmlContext>
|
||||
#include <QtQml/QQmlEngine>
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
#include <QtWidgets/QActionGroup>
|
||||
#include <QtWidgets/QDesktopWidget>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
|
@ -50,6 +57,7 @@
|
|||
#include <gl/Config.h>
|
||||
#include <gl/QOpenGLContextWrapper.h>
|
||||
|
||||
#include <ResourceScriptingInterface.h>
|
||||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <ApplicationVersion.h>
|
||||
|
@ -83,6 +91,7 @@
|
|||
#include <ObjectMotionState.h>
|
||||
#include <OctalCode.h>
|
||||
#include <OctreeSceneStats.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <PathUtils.h>
|
||||
#include <PerfStat.h>
|
||||
|
@ -181,6 +190,7 @@ static const QString JS_EXTENSION = ".js";
|
|||
static const QString FST_EXTENSION = ".fst";
|
||||
static const QString FBX_EXTENSION = ".fbx";
|
||||
static const QString OBJ_EXTENSION = ".obj";
|
||||
static const QString AVA_JSON_EXTENSION = ".ava.json";
|
||||
|
||||
static const int MIRROR_VIEW_TOP_PADDING = 5;
|
||||
static const int MIRROR_VIEW_LEFT_PADDING = 10;
|
||||
|
@ -216,7 +226,8 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi
|
|||
{ SVO_EXTENSION, &Application::importSVOFromURL },
|
||||
{ SVO_JSON_EXTENSION, &Application::importSVOFromURL },
|
||||
{ JS_EXTENSION, &Application::askToLoadScript },
|
||||
{ FST_EXTENSION, &Application::askToSetAvatarUrl }
|
||||
{ FST_EXTENSION, &Application::askToSetAvatarUrl },
|
||||
{ AVA_JSON_EXTENSION, &Application::askToWearAvatarAttachmentUrl }
|
||||
};
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
@ -340,6 +351,8 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<RecordingScriptingInterface>();
|
||||
DependencyManager::set<WindowScriptingInterface>();
|
||||
DependencyManager::set<HMDScriptingInterface>();
|
||||
DependencyManager::set<ResourceScriptingInterface>();
|
||||
|
||||
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
DependencyManager::set<SpeechRecognizer>();
|
||||
|
@ -370,7 +383,7 @@ PluginContainer* _pluginContainer;
|
|||
|
||||
|
||||
// FIXME hack access to the internal share context for the Chromium helper
|
||||
// Normally we'd want to use QWebEngine::initialize(), but we can't because
|
||||
// Normally we'd want to use QWebEngine::initialize(), but we can't because
|
||||
// our primary context is a QGLWidget, which can't easily be initialized to share
|
||||
// from a QOpenGLContext.
|
||||
//
|
||||
|
@ -685,6 +698,76 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
// Setup the userInputMapper with the actions
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
|
||||
using namespace controller;
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
if (offscreenUi->navigationFocused()) {
|
||||
auto actionEnum = static_cast<Action>(action);
|
||||
int key = Qt::Key_unknown;
|
||||
static int lastKey = Qt::Key_unknown;
|
||||
bool navAxis = false;
|
||||
switch (actionEnum) {
|
||||
case Action::UI_NAV_VERTICAL:
|
||||
navAxis = true;
|
||||
if (state > 0.0f) {
|
||||
key = Qt::Key_Up;
|
||||
} else if (state < 0.0f) {
|
||||
key = Qt::Key_Down;
|
||||
}
|
||||
break;
|
||||
|
||||
case Action::UI_NAV_LATERAL:
|
||||
navAxis = true;
|
||||
if (state > 0.0f) {
|
||||
key = Qt::Key_Right;
|
||||
} else if (state < 0.0f) {
|
||||
key = Qt::Key_Left;
|
||||
}
|
||||
break;
|
||||
|
||||
case Action::UI_NAV_GROUP:
|
||||
navAxis = true;
|
||||
if (state > 0.0f) {
|
||||
key = Qt::Key_Tab;
|
||||
} else if (state < 0.0f) {
|
||||
key = Qt::Key_Backtab;
|
||||
}
|
||||
break;
|
||||
|
||||
case Action::UI_NAV_BACK:
|
||||
key = Qt::Key_Escape;
|
||||
break;
|
||||
|
||||
case Action::UI_NAV_SELECT:
|
||||
key = Qt::Key_Return;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (navAxis) {
|
||||
if (lastKey != Qt::Key_unknown) {
|
||||
QKeyEvent event(QEvent::KeyRelease, lastKey, Qt::NoModifier);
|
||||
sendEvent(offscreenUi->getWindow(), &event);
|
||||
lastKey = Qt::Key_unknown;
|
||||
}
|
||||
|
||||
if (key != Qt::Key_unknown) {
|
||||
QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier);
|
||||
sendEvent(offscreenUi->getWindow(), &event);
|
||||
lastKey = key;
|
||||
}
|
||||
} else if (key != Qt::Key_unknown) {
|
||||
if (state) {
|
||||
QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier);
|
||||
sendEvent(offscreenUi->getWindow(), &event);
|
||||
} else {
|
||||
QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier);
|
||||
sendEvent(offscreenUi->getWindow(), &event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == controller::toInt(controller::Action::RETICLE_CLICK)) {
|
||||
auto globalPos = QCursor::pos();
|
||||
auto localPos = _glWidget->mapFromGlobal(globalPos);
|
||||
|
@ -755,6 +838,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float {
|
||||
return (float)qApp->getMyAvatar()->getCharacterController()->onGround();
|
||||
}));
|
||||
_applicationStateDevice->addInputVariant(QString("NavigationFocused"), controller::StateController::ReadLambda([]() -> float {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->navigationFocused() ? 1.0 : 0.0;
|
||||
}));
|
||||
|
||||
userInputMapper->registerDevice(_applicationStateDevice);
|
||||
|
||||
|
@ -955,7 +1042,7 @@ void Application::cleanupBeforeQuit() {
|
|||
|
||||
// destroy the AudioClient so it and its thread have a chance to go down safely
|
||||
DependencyManager::destroy<AudioClient>();
|
||||
|
||||
|
||||
// destroy the AudioInjectorManager so it and its thread have a chance to go down safely
|
||||
// this will also stop any ongoing network injectors
|
||||
DependencyManager::destroy<AudioInjectorManager>();
|
||||
|
@ -967,6 +1054,8 @@ void Application::cleanupBeforeQuit() {
|
|||
#ifdef HAVE_IVIEWHMD
|
||||
DependencyManager::destroy<EyeTracker>();
|
||||
#endif
|
||||
|
||||
DependencyManager::destroy<OffscreenUi>();
|
||||
}
|
||||
|
||||
void Application::emptyLocalCache() {
|
||||
|
@ -1000,8 +1089,8 @@ Application::~Application() {
|
|||
// remove avatars from physics engine
|
||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||
VectorOfMotionStates motionStates;
|
||||
DependencyManager::get<AvatarManager>()->getObjectsToDelete(motionStates);
|
||||
_physicsEngine->deleteObjects(motionStates);
|
||||
DependencyManager::get<AvatarManager>()->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
||||
DependencyManager::destroy<OffscreenUi>();
|
||||
DependencyManager::destroy<AvatarManager>();
|
||||
|
@ -1096,10 +1185,59 @@ void Application::initializeUi() {
|
|||
offscreenUi->setProxyWindow(_window->windowHandle());
|
||||
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
|
||||
offscreenUi->load("Root.qml");
|
||||
offscreenUi->load("RootMenu.qml");
|
||||
auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
|
||||
offscreenUi->getRootContext()->setContextProperty("Controller", scriptingInterface.data());
|
||||
offscreenUi->getRootContext()->setContextProperty("MyAvatar", getMyAvatar());
|
||||
// FIXME either expose so that dialogs can set this themselves or
|
||||
// do better detection in the offscreen UI of what has focus
|
||||
offscreenUi->setNavigationFocused(false);
|
||||
|
||||
auto rootContext = offscreenUi->getRootContext();
|
||||
auto engine = rootContext->engine();
|
||||
connect(engine, &QQmlEngine::quit, [] {
|
||||
qApp->quit();
|
||||
});
|
||||
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||
rootContext->setContextProperty("MyAvatar", getMyAvatar());
|
||||
rootContext->setContextProperty("Messages", DependencyManager::get<MessagesClient>().data());
|
||||
rootContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data());
|
||||
|
||||
rootContext->setContextProperty("TREE_SCALE", TREE_SCALE);
|
||||
rootContext->setContextProperty("Quat", new Quat());
|
||||
rootContext->setContextProperty("Vec3", new Vec3());
|
||||
rootContext->setContextProperty("Uuid", new ScriptUUID());
|
||||
|
||||
rootContext->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
|
||||
|
||||
rootContext->setContextProperty("Camera", &_myCamera);
|
||||
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
rootContext->setContextProperty("SpeechRecognizer", DependencyManager::get<SpeechRecognizer>().data());
|
||||
#endif
|
||||
|
||||
rootContext->setContextProperty("Overlays", &_overlays);
|
||||
rootContext->setContextProperty("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
|
||||
|
||||
rootContext->setContextProperty("Window", DependencyManager::get<WindowScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Menu", MenuScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("Stats", Stats::getInstance());
|
||||
rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
rootContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
rootContext->setContextProperty("Account", AccountScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
rootContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());
|
||||
rootContext->setContextProperty("AvatarManager", DependencyManager::get<AvatarManager>().data());
|
||||
rootContext->setContextProperty("UndoStack", &_undoStackScriptingInterface);
|
||||
rootContext->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
|
||||
rootContext->setContextProperty("Paths", DependencyManager::get<PathUtils>().data());
|
||||
rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Render", DependencyManager::get<RenderScriptingInterface>().data());
|
||||
rootContext->setContextProperty("ScriptDiscoveryService", this->getRunningScriptsWidget());
|
||||
|
||||
_glWidget->installEventFilter(offscreenUi.data());
|
||||
VrMenu::load();
|
||||
VrMenu::executeQueuedLambdas();
|
||||
|
@ -1135,7 +1273,7 @@ void Application::initializeUi() {
|
|||
}
|
||||
|
||||
void Application::paintGL() {
|
||||
// paintGL uses a queued connection, so we can get messages from the queue even after we've quit
|
||||
// paintGL uses a queued connection, so we can get messages from the queue even after we've quit
|
||||
// and the plugins have shutdown
|
||||
if (_aboutToQuit) {
|
||||
return;
|
||||
|
@ -1205,7 +1343,7 @@ void Application::paintGL() {
|
|||
renderArgs._context->syncCache();
|
||||
}
|
||||
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) {
|
||||
PerformanceTimer perfTimer("Mirror");
|
||||
auto primaryFbo = DependencyManager::get<FramebufferCache>()->getPrimaryFramebuffer();
|
||||
|
||||
|
@ -1494,7 +1632,7 @@ void Application::aboutApp() {
|
|||
InfoView::show(INFO_HELP_PATH);
|
||||
}
|
||||
|
||||
void Application::showEditEntitiesHelp() {
|
||||
void Application::showHelp() {
|
||||
InfoView::show(INFO_EDIT_ENTITIES_PATH);
|
||||
}
|
||||
|
||||
|
@ -1852,7 +1990,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
|
||||
case Qt::Key_H:
|
||||
if (isShifted) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Mirror);
|
||||
Menu::getInstance()->triggerOption(MenuOption::MiniMirror);
|
||||
} else {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));
|
||||
if (!Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
|
@ -2863,7 +3001,11 @@ void Application::update(float deltaTime) {
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::update()");
|
||||
|
||||
updateLOD();
|
||||
if (DependencyManager::get<LODManager>()->getUseAcuity()) {
|
||||
updateLOD();
|
||||
} else {
|
||||
DependencyManager::get<LODManager>()->updatePIDRenderDistance(getTargetFrameRate(), getLastInstanteousFps(), deltaTime, isThrottleRendering());
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("devices");
|
||||
|
@ -2948,11 +3090,11 @@ void Application::update(float deltaTime) {
|
|||
PerformanceTimer perfTimer("physics");
|
||||
|
||||
static VectorOfMotionStates motionStates;
|
||||
_entitySimulation.getObjectsToDelete(motionStates);
|
||||
_physicsEngine->deleteObjects(motionStates);
|
||||
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
|
||||
getEntities()->getTree()->withWriteLock([&] {
|
||||
_entitySimulation.getObjectsToAdd(motionStates);
|
||||
_entitySimulation.getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
|
||||
});
|
||||
|
@ -2965,9 +3107,9 @@ void Application::update(float deltaTime) {
|
|||
_entitySimulation.applyActionChanges();
|
||||
|
||||
AvatarManager* avatarManager = DependencyManager::get<AvatarManager>().data();
|
||||
avatarManager->getObjectsToDelete(motionStates);
|
||||
_physicsEngine->deleteObjects(motionStates);
|
||||
avatarManager->getObjectsToAdd(motionStates);
|
||||
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
||||
_physicsEngine->removeObjects(motionStates);
|
||||
avatarManager->getObjectsToAddToPhysics(motionStates);
|
||||
_physicsEngine->addObjects(motionStates);
|
||||
avatarManager->getObjectsToChange(motionStates);
|
||||
_physicsEngine->changeObjects(motionStates);
|
||||
|
@ -3002,8 +3144,7 @@ void Application::update(float deltaTime) {
|
|||
getEntities()->update(); // update the models...
|
||||
}
|
||||
|
||||
myAvatar->harvestResultsFromPhysicsSimulation();
|
||||
myAvatar->simulateAttachments(deltaTime);
|
||||
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3605,7 +3746,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
auto backgroundRenderData = make_shared<BackgroundRenderData>(&_environment);
|
||||
auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);
|
||||
BackgroundRenderData::_item = _main3DScene->allocateID();
|
||||
pendingChanges.resetItem(WorldBoxRenderData::_item, backgroundRenderPayload);
|
||||
pendingChanges.resetItem(BackgroundRenderData::_item, backgroundRenderPayload);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
@ -3947,7 +4088,7 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
|
|||
});
|
||||
|
||||
foreach (EntityItemPointer entity, entities) {
|
||||
if (!entity->isReadyToComputeShape()) {
|
||||
if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) {
|
||||
static QString repeatedMessage =
|
||||
LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*");
|
||||
qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName();
|
||||
|
@ -4114,6 +4255,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
|
||||
scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1);
|
||||
scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor);
|
||||
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
|
||||
|
||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
||||
|
@ -4246,7 +4388,7 @@ bool Application::askToSetAvatarUrl(const QString& url) {
|
|||
bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
|
||||
QMessageBox::StandardButton reply;
|
||||
QString message = "Would you like to run this script:\n" + scriptFilenameOrURL;
|
||||
reply = QMessageBox::question(getWindow(), "Run Script", message, QMessageBox::Yes|QMessageBox::No);
|
||||
reply = OffscreenUi::question(getWindow(), "Run Script", message, QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if (reply == QMessageBox::Yes) {
|
||||
qCDebug(interfaceapp) << "Chose to run the script: " << scriptFilenameOrURL;
|
||||
|
@ -4259,7 +4401,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
|
|||
|
||||
bool Application::askToUploadAsset(const QString& filename) {
|
||||
if (!DependencyManager::get<NodeList>()->getThisNodeCanRez()) {
|
||||
QMessageBox::warning(_window, "Failed Upload",
|
||||
OffscreenUi::warning(_window, "Failed Upload",
|
||||
QString("You don't have upload rights on that domain.\n\n"));
|
||||
return false;
|
||||
}
|
||||
|
@ -4303,7 +4445,7 @@ bool Application::askToUploadAsset(const QString& filename) {
|
|||
}
|
||||
|
||||
// display a message box with the error
|
||||
QMessageBox::warning(_window, "Failed Upload", QString("Failed to upload %1.\n\n").arg(filename));
|
||||
OffscreenUi::warning(_window, "Failed Upload", QString("Failed to upload %1.\n\n").arg(filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -4330,6 +4472,99 @@ void Application::modelUploadFinished(AssetUpload* upload, const QString& hash)
|
|||
}
|
||||
}
|
||||
|
||||
bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
|
||||
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(url);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
||||
int requestNumber = ++_avatarAttachmentRequest;
|
||||
connect(reply, &QNetworkReply::finished, [this, reply, url, requestNumber]() {
|
||||
|
||||
if (requestNumber != _avatarAttachmentRequest) {
|
||||
// this request has been superseded by another more recent request
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
QNetworkReply::NetworkError networkError = reply->error();
|
||||
if (networkError == QNetworkReply::NoError) {
|
||||
// download success
|
||||
QByteArray contents = reply->readAll();
|
||||
|
||||
QJsonParseError jsonError;
|
||||
auto doc = QJsonDocument::fromJson(contents, &jsonError);
|
||||
if (jsonError.error == QJsonParseError::NoError) {
|
||||
|
||||
auto jsonObject = doc.object();
|
||||
|
||||
// retrieve optional name field from JSON
|
||||
QString name = tr("Unnamed Attachment");
|
||||
auto nameValue = jsonObject.value("name");
|
||||
if (nameValue.isString()) {
|
||||
name = nameValue.toString();
|
||||
}
|
||||
|
||||
// display confirmation dialog
|
||||
if (displayAvatarAttachmentConfirmationDialog(name)) {
|
||||
|
||||
// add attachment to avatar
|
||||
auto myAvatar = getMyAvatar();
|
||||
assert(myAvatar);
|
||||
auto attachmentDataVec = myAvatar->getAttachmentData();
|
||||
AttachmentData attachmentData;
|
||||
attachmentData.fromJson(jsonObject);
|
||||
attachmentDataVec.push_back(attachmentData);
|
||||
myAvatar->setAttachmentData(attachmentDataVec);
|
||||
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "User declined to wear the avatar attachment: " << url;
|
||||
}
|
||||
|
||||
} else {
|
||||
// json parse error
|
||||
auto avatarAttachmentParseErrorString = tr("Error parsing attachment JSON from url: \"%1\"");
|
||||
displayAvatarAttachmentWarning(avatarAttachmentParseErrorString.arg(url));
|
||||
}
|
||||
} else {
|
||||
// download failure
|
||||
auto avatarAttachmentDownloadErrorString = tr("Error downloading attachment JSON from url: \"%1\"");
|
||||
displayAvatarAttachmentWarning(avatarAttachmentDownloadErrorString.arg(url));
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::displayAvatarAttachmentWarning(const QString& message) const {
|
||||
auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure");
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setWindowTitle(avatarAttachmentWarningTitle);
|
||||
msgBox.setText(message);
|
||||
msgBox.exec();
|
||||
msgBox.addButton(QMessageBox::Ok);
|
||||
msgBox.exec();
|
||||
}
|
||||
|
||||
bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) const {
|
||||
auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation");
|
||||
auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?");
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Question);
|
||||
msgBox.setWindowTitle(avatarAttachmentConfirmationTitle);
|
||||
QPushButton* button = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
|
||||
QString message = avatarAttachmentConfirmationMessage.arg(name);
|
||||
msgBox.setText(message);
|
||||
msgBox.addButton(QMessageBox::Cancel);
|
||||
msgBox.exec();
|
||||
if (msgBox.clickedButton() == button) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,
|
||||
bool loadScriptFromEditor, bool activateMainWindow, bool reload) {
|
||||
|
||||
|
@ -4401,7 +4636,7 @@ void Application::handleScriptEngineLoaded(const QString& scriptFilename) {
|
|||
// FIXME - change to new version of ScriptCache loading notification
|
||||
void Application::handleScriptLoadError(const QString& scriptFilename) {
|
||||
qCDebug(interfaceapp) << "Application::loadScript(), script failed to load...";
|
||||
QMessageBox::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load.");
|
||||
OffscreenUi::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load.");
|
||||
}
|
||||
|
||||
QStringList Application::getRunningScripts() {
|
||||
|
@ -4857,7 +5092,7 @@ void Application::updateDisplayMode() {
|
|||
foreach(auto displayPlugin, displayPlugins) {
|
||||
addDisplayPluginToMenu(displayPlugin, first);
|
||||
// This must be a queued connection to avoid a deadlock
|
||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender,
|
||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender,
|
||||
this, &Application::paintGL, Qt::QueuedConnection);
|
||||
|
||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {
|
||||
|
|
|
@ -298,7 +298,7 @@ public slots:
|
|||
#endif
|
||||
|
||||
void aboutApp();
|
||||
void showEditEntitiesHelp();
|
||||
void showHelp();
|
||||
|
||||
void cycleCamera();
|
||||
void cameraMenuChanged();
|
||||
|
@ -343,7 +343,11 @@ private slots:
|
|||
bool askToLoadScript(const QString& scriptFilenameOrURL);
|
||||
bool askToUploadAsset(const QString& asset);
|
||||
void modelUploadFinished(AssetUpload* upload, const QString& hash);
|
||||
|
||||
|
||||
bool askToWearAvatarAttachmentUrl(const QString& url);
|
||||
void displayAvatarAttachmentWarning(const QString& message) const;
|
||||
bool displayAvatarAttachmentConfirmationDialog(const QString& name) const;
|
||||
|
||||
void setSessionUUID(const QUuid& sessionUUID);
|
||||
void domainChanged(const QString& domainHostname);
|
||||
void updateWindowTitle();
|
||||
|
@ -553,6 +557,8 @@ private:
|
|||
bool _physicsEnabled { false };
|
||||
|
||||
bool _reticleClickPressed { false };
|
||||
|
||||
int _avatarAttachmentRequest = 0;
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -16,22 +16,31 @@
|
|||
|
||||
#include "InterfaceParentFinder.h"
|
||||
|
||||
SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID) const {
|
||||
SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& success) const {
|
||||
SpatiallyNestableWeakPointer parent;
|
||||
|
||||
if (parentID.isNull()) {
|
||||
success = true;
|
||||
return parent;
|
||||
}
|
||||
|
||||
// search entities
|
||||
EntityTreeRenderer* treeRenderer = qApp->getEntities();
|
||||
EntityTreePointer tree = treeRenderer->getTree();
|
||||
parent = tree->findEntityByEntityItemID(parentID);
|
||||
EntityTreePointer tree = treeRenderer ? treeRenderer->getTree() : nullptr;
|
||||
parent = tree ? tree->findEntityByEntityItemID(parentID) : nullptr;
|
||||
if (!parent.expired()) {
|
||||
success = true;
|
||||
return parent;
|
||||
}
|
||||
|
||||
// search avatars
|
||||
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
|
||||
return avatarManager->getAvatarBySessionID(parentID);
|
||||
parent = avatarManager->getAvatarBySessionID(parentID);
|
||||
if (!parent.expired()) {
|
||||
success = true;
|
||||
return parent;
|
||||
}
|
||||
|
||||
success = false;
|
||||
return parent;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class InterfaceParentFinder : public SpatialParentFinder {
|
|||
public:
|
||||
InterfaceParentFinder() { }
|
||||
virtual ~InterfaceParentFinder() { }
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID) const;
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const;
|
||||
};
|
||||
|
||||
#endif // hifi_InterfaceParentFinder_h
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <Util.h>
|
||||
|
||||
|
@ -20,9 +21,30 @@
|
|||
|
||||
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
|
||||
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
|
||||
// There are two different systems in use, based on lodPreference:
|
||||
// pid: renderDistance is adjusted by a PID such that frame rate targets are met.
|
||||
// acuity: a pseudo-acuity target is held, or adjusted to match minimum frame rates (and a PID controlls avatar rendering distance)
|
||||
// If unspecified, acuity is used only if user has specified non-default minumum frame rates.
|
||||
Setting::Handle<int> lodPreference("lodPreference", (int)LODManager::LODPreference::unspecified);
|
||||
const float SMALLEST_REASONABLE_HORIZON = 50.0f; // meters
|
||||
Setting::Handle<float> renderDistanceInverseHighLimit("renderDistanceInverseHighLimit", 1.0f / SMALLEST_REASONABLE_HORIZON);
|
||||
void LODManager::setRenderDistanceInverseHighLimit(float newValue) {
|
||||
renderDistanceInverseHighLimit.set(newValue); // persist it, and tell all the controllers that use it
|
||||
_renderDistanceController.setControlledValueHighLimit(newValue);
|
||||
}
|
||||
|
||||
LODManager::LODManager() {
|
||||
calculateAvatarLODDistanceMultiplier();
|
||||
|
||||
setRenderDistanceInverseHighLimit(renderDistanceInverseHighLimit.get());
|
||||
setRenderDistanceInverseLowLimit(1.0f / (float)TREE_SCALE);
|
||||
// Advice for tuning parameters:
|
||||
// See PIDController.h. There's a section on tuning in the reference.
|
||||
// Turn on logging with the following (or from js with LODManager.setRenderDistanceControllerHistory("render pid", 240))
|
||||
//setRenderDistanceControllerHistory("render pid", 60 * 4);
|
||||
// Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on.
|
||||
setRenderDistanceKP(0.000012f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0.
|
||||
setRenderDistanceKI(0.00002f); // Big enough to bring us to target with the above KP.
|
||||
}
|
||||
|
||||
float LODManager::getLODDecreaseFPS() {
|
||||
|
@ -39,7 +61,6 @@ float LODManager::getLODIncreaseFPS() {
|
|||
return getDesktopLODIncreaseFPS();
|
||||
}
|
||||
|
||||
|
||||
void LODManager::autoAdjustLOD(float currentFPS) {
|
||||
|
||||
// NOTE: our first ~100 samples at app startup are completely all over the place, and we don't
|
||||
|
@ -217,15 +238,58 @@ QString LODManager::getLODFeedbackText() {
|
|||
return result;
|
||||
}
|
||||
|
||||
static float renderDistance = (float)TREE_SCALE;
|
||||
static int renderedCount = 0;
|
||||
static int lastRenderedCount = 0;
|
||||
bool LODManager::getUseAcuity() { return lodPreference.get() == (int)LODManager::LODPreference::acuity; }
|
||||
void LODManager::setUseAcuity(bool newValue) { lodPreference.set(newValue ? (int)LODManager::LODPreference::acuity : (int)LODManager::LODPreference::pid); }
|
||||
float LODManager::getRenderDistance() {
|
||||
return renderDistance;
|
||||
}
|
||||
int LODManager::getRenderedCount() {
|
||||
return lastRenderedCount;
|
||||
}
|
||||
QString LODManager::getLODStatsRenderText() {
|
||||
const QString label = "Rendered objects: ";
|
||||
return label + QString::number(getRenderedCount()) + " w/in " + QString::number((int)getRenderDistance()) + "m";
|
||||
}
|
||||
// compare audoAdjustLOD()
|
||||
void LODManager::updatePIDRenderDistance(float targetFps, float measuredFps, float deltaTime, bool isThrottled) {
|
||||
float distance;
|
||||
if (!isThrottled) {
|
||||
_renderDistanceController.setMeasuredValueSetpoint(targetFps); // No problem updating in flight.
|
||||
// The PID controller raises the controlled value when the measured value goes up.
|
||||
// The measured value is frame rate. When the controlled value (1 / render cutoff distance)
|
||||
// goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate
|
||||
// goes up.
|
||||
distance = 1.0f / _renderDistanceController.update(measuredFps, deltaTime);
|
||||
} else {
|
||||
// Here we choose to just use the maximum render cutoff distance if throttled.
|
||||
distance = 1.0f / _renderDistanceController.getControlledValueLowLimit();
|
||||
}
|
||||
_renderDistanceAverage.updateAverage(distance);
|
||||
renderDistance = _renderDistanceAverage.getAverage(); // average only once per cycle
|
||||
lastRenderedCount = renderedCount;
|
||||
renderedCount = 0;
|
||||
}
|
||||
|
||||
bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) {
|
||||
float distanceToCamera = glm::length(bounds.calcCenter() - args->_viewFrustum->getPosition());
|
||||
float largestDimension = bounds.getLargestDimension();
|
||||
if (!getUseAcuity()) {
|
||||
const float scenerySize = 300; // meters
|
||||
bool isRendered = (largestDimension > scenerySize) || // render scenery regardless of distance
|
||||
(distanceToCamera < renderDistance + largestDimension);
|
||||
renderedCount += isRendered ? 1 : 0;
|
||||
return isRendered;
|
||||
}
|
||||
|
||||
const float maxScale = (float)TREE_SCALE;
|
||||
const float octreeToMeshRatio = 4.0f; // must be this many times closer to a mesh than a voxel to see it.
|
||||
float octreeSizeScale = args->_sizeScale;
|
||||
int boundaryLevelAdjust = args->_boundaryLevelAdjust;
|
||||
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / octreeToMeshRatio;
|
||||
float distanceToCamera = glm::length(bounds.calcCenter() - args->_viewFrustum->getPosition());
|
||||
float largestDimension = bounds.getLargestDimension();
|
||||
|
||||
|
||||
static bool shouldRenderTableNeedsBuilding = true;
|
||||
static QMap<float, float> shouldRenderTable;
|
||||
if (shouldRenderTableNeedsBuilding) {
|
||||
|
@ -315,6 +379,12 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
|
|||
void LODManager::loadSettings() {
|
||||
setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get());
|
||||
setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get());
|
||||
|
||||
if (lodPreference.get() == (int)LODManager::LODPreference::unspecified) {
|
||||
setUseAcuity((getDesktopLODDecreaseFPS() != DEFAULT_DESKTOP_LOD_DOWN_FPS) || (getHMDLODDecreaseFPS() != DEFAULT_HMD_LOD_DOWN_FPS));
|
||||
}
|
||||
Menu::getInstance()->getActionForOption(MenuOption::LodTools)->setEnabled(getUseAcuity());
|
||||
Menu::getInstance()->getSubMenuFromName(MenuOption::RenderResolution, Menu::getInstance()->getSubMenuFromName("Render", Menu::getInstance()->getMenu("Developer")))->setEnabled(getUseAcuity());
|
||||
}
|
||||
|
||||
void LODManager::saveSettings() {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <DependencyManager.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <OctreeConstants.h>
|
||||
#include <PIDController.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
|
||||
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 15.0;
|
||||
|
@ -81,6 +82,27 @@ public:
|
|||
Q_INVOKABLE float getLODDecreaseFPS();
|
||||
Q_INVOKABLE float getLODIncreaseFPS();
|
||||
|
||||
enum class LODPreference {
|
||||
pid = 0,
|
||||
acuity,
|
||||
unspecified
|
||||
};
|
||||
static bool getUseAcuity();
|
||||
static void setUseAcuity(bool newValue);
|
||||
Q_INVOKABLE void setRenderDistanceKP(float newValue) { _renderDistanceController.setKP(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceKI(float newValue) { _renderDistanceController.setKI(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); }
|
||||
Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); }
|
||||
Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); }
|
||||
Q_INVOKABLE float getRenderDistanceInverseLowLimit() { return _renderDistanceController.getControlledValueLowLimit(); }
|
||||
Q_INVOKABLE void setRenderDistanceInverseLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); }
|
||||
Q_INVOKABLE float getRenderDistanceInverseHighLimit() { return _renderDistanceController.getControlledValueHighLimit(); }
|
||||
Q_INVOKABLE void setRenderDistanceInverseHighLimit(float newValue);
|
||||
void updatePIDRenderDistance(float targetFps, float measuredFps, float deltaTime, bool isThrottled);
|
||||
float getRenderDistance();
|
||||
int getRenderedCount();
|
||||
QString getLODStatsRenderText();
|
||||
|
||||
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
|
||||
bool shouldRenderMesh(float largestDimension, float distanceToCamera);
|
||||
void autoAdjustLOD(float currentFPS);
|
||||
|
@ -116,6 +138,9 @@ private:
|
|||
|
||||
bool _shouldRenderTableNeedsRebuilding = true;
|
||||
QMap<float, float> _shouldRenderTable;
|
||||
|
||||
PIDController _renderDistanceController{};
|
||||
SimpleMovingAverage _renderDistanceAverage{ 10 };
|
||||
};
|
||||
|
||||
#endif // hifi_LODManager_h
|
||||
|
|
|
@ -51,13 +51,16 @@ Menu* Menu::getInstance() {
|
|||
}
|
||||
|
||||
Menu::Menu() {
|
||||
MenuWrapper * fileMenu = addMenu("File");
|
||||
#ifdef Q_OS_MAC
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole);
|
||||
#endif
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
// File/Application menu ----------------------------------
|
||||
MenuWrapper* fileMenu = addMenu("File");
|
||||
|
||||
// File > Quit
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp,SLOT(quit()), QAction::QuitRole);
|
||||
|
||||
// File > Login menu items
|
||||
{
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Login);
|
||||
|
||||
|
@ -68,256 +71,269 @@ Menu::Menu() {
|
|||
dialogsManager.data(), &DialogsManager::toggleLoginDialog);
|
||||
}
|
||||
|
||||
// File Menu > Scripts section -- "Advanced" grouping
|
||||
addDisabledActionAndSeparator(fileMenu, "Scripts", UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O,
|
||||
qApp, SLOT(loadDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R,
|
||||
qApp, SLOT(reloadAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
|
||||
qApp, SLOT(toggleRunningScriptsWidget()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
// File > Update -- FIXME: needs implementation
|
||||
auto updateAction = addActionToQMenuAndActionHash(fileMenu, "Update");
|
||||
updateAction->setDisabled(true);
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
// File > Help
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Help, 0, qApp, SLOT(showHelp()));
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "History");
|
||||
// File > Crash Reporter...-- FIXME: needs implementation
|
||||
auto crashReporterAction = addActionToQMenuAndActionHash(fileMenu, "Crash Reporter...");
|
||||
crashReporterAction->setDisabled(true);
|
||||
|
||||
QAction* backAction = addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Back,
|
||||
0,
|
||||
addressManager.data(),
|
||||
SLOT(goBack()));
|
||||
|
||||
QAction* forwardAction = addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Forward,
|
||||
0,
|
||||
addressManager.data(),
|
||||
SLOT(goForward()));
|
||||
|
||||
// connect to the AddressManager signal to enable and disable the back and forward menu items
|
||||
connect(addressManager.data(), &AddressManager::goBackPossible, backAction, &QAction::setEnabled);
|
||||
connect(addressManager.data(), &AddressManager::goForwardPossible, forwardAction, &QAction::setEnabled);
|
||||
|
||||
// set the two actions to start disabled since the stacks are clear on startup
|
||||
backAction->setDisabled(true);
|
||||
forwardAction->setDisabled(true);
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Location");
|
||||
qApp->getBookmarks()->setupMenus(this, fileMenu);
|
||||
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::AddressBar,
|
||||
Qt::CTRL | Qt::Key_L,
|
||||
dialogsManager.data(),
|
||||
SLOT(toggleAddressBar()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0,
|
||||
addressManager.data(), SLOT(copyAddress()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0,
|
||||
addressManager.data(), SLOT(copyPath()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Quit,
|
||||
Qt::CTRL | Qt::Key_Q,
|
||||
qApp,
|
||||
SLOT(quit()),
|
||||
QAction::QuitRole);
|
||||
// File > About
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole);
|
||||
|
||||
|
||||
// Audio menu ----------------------------------
|
||||
MenuWrapper* audioMenu = addMenu("Audio");
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
|
||||
// Audio > Mute
|
||||
addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false,
|
||||
audioIO.data(), SLOT(toggleMute()));
|
||||
|
||||
// Audio > Level Meter [advanced] -- FIXME: needs implementation
|
||||
auto levelMeterAction = addCheckableActionToQMenuAndActionHash(audioMenu, "Level Meter", 0, false, NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
||||
levelMeterAction->setDisabled(true);
|
||||
|
||||
|
||||
// Avatar menu ----------------------------------
|
||||
MenuWrapper* avatarMenu = addMenu("Avatar");
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
QObject* avatar = avatarManager->getMyAvatar();
|
||||
|
||||
// Avatar > Attachments...
|
||||
addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments, 0,
|
||||
dialogsManager.data(), SLOT(editAttachments()));
|
||||
|
||||
// Avatar > Size
|
||||
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||
|
||||
// Avatar > Size > Increase
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
0, // QML Qt::Key_Plus,
|
||||
avatar, SLOT(increaseSize()));
|
||||
|
||||
// Avatar > Size > Decrease
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
0, // QML Qt::Key_Minus,
|
||||
avatar, SLOT(decreaseSize()));
|
||||
|
||||
// Avatar > Size > Reset
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
0, // QML Qt::Key_Equal,
|
||||
avatar, SLOT(resetSize()));
|
||||
|
||||
// Avatar > Reset Sensors
|
||||
addActionToQMenuAndActionHash(avatarMenu,
|
||||
MenuOption::ResetSensors,
|
||||
0, // QML Qt::Key_Apostrophe,
|
||||
qApp, SLOT(resetSensors()));
|
||||
|
||||
|
||||
// Display menu ----------------------------------
|
||||
// FIXME - this is not yet matching Alan's spec because it doesn't have
|
||||
// menus for "2D"/"3D" - we need to add support for detecting the appropriate
|
||||
// default 3D display mode
|
||||
addMenu(DisplayPlugin::MENU_PATH());
|
||||
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
|
||||
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);
|
||||
displayModeGroup->setExclusive(true);
|
||||
|
||||
|
||||
// View menu ----------------------------------
|
||||
MenuWrapper* viewMenu = addMenu("View");
|
||||
QActionGroup* cameraModeGroup = new QActionGroup(viewMenu);
|
||||
|
||||
// View > [camera group]
|
||||
cameraModeGroup->setExclusive(true);
|
||||
|
||||
// View > First Person
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::FirstPerson, 0, // QML Qt:: Key_P
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
// View > Third Person
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::ThirdPerson, 0,
|
||||
true, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
// View > Mirror
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::FullscreenMirror, 0, // QML Qt::Key_H,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
// View > Independent [advanced]
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::IndependentMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged()),
|
||||
UNSPECIFIED_POSITION, "Advanced"));
|
||||
|
||||
// View > Entity Camera [advanced]
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu,
|
||||
MenuOption::CameraEntityMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged()),
|
||||
UNSPECIFIED_POSITION, "Advanced"));
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
// View > Mini Mirror
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MiniMirror, 0, false);
|
||||
|
||||
|
||||
// Edit menu ----------------------------------
|
||||
MenuWrapper* editMenu = addMenu("Edit");
|
||||
|
||||
// Edit > Undo
|
||||
QUndoStack* undoStack = qApp->getUndoStack();
|
||||
QAction* undoAction = undoStack->createUndoAction(editMenu);
|
||||
undoAction->setShortcut(Qt::CTRL | Qt::Key_Z);
|
||||
addActionToQMenuAndActionHash(editMenu, undoAction);
|
||||
|
||||
// Edit > Redo
|
||||
QAction* redoAction = undoStack->createRedoAction(editMenu);
|
||||
redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z);
|
||||
addActionToQMenuAndActionHash(editMenu, redoAction);
|
||||
|
||||
addActionToQMenuAndActionHash(editMenu,
|
||||
MenuOption::Preferences,
|
||||
Qt::CTRL | Qt::Key_Comma,
|
||||
dialogsManager.data(),
|
||||
SLOT(editPreferences()),
|
||||
QAction::PreferencesRole);
|
||||
// Edit > Running Sccripts
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
|
||||
qApp, SLOT(toggleRunningScriptsWidget()));
|
||||
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0,
|
||||
dialogsManager.data(), SLOT(editAttachments()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
// Edit > Open and Run Script from File... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O,
|
||||
qApp, SLOT(loadDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
MenuWrapper* toolsMenu = addMenu("Tools");
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S,
|
||||
dialogsManager.data(), SLOT(showScriptEditor()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
// Edit > Open and Run Script from Url... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::LoadScriptURL,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Stop All Scripts... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Reload All Scripts... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R,
|
||||
qApp, SLOT(reloadAllScripts()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Scripts Editor... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S,
|
||||
dialogsManager.data(), SLOT(showScriptEditor()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Console... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
DependencyManager::get<StandAloneJSConsole>().data(),
|
||||
SLOT(toggleConsole()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Reload All Content [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
// Edit > Package Model... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0,
|
||||
qApp, SLOT(packageModel()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
// Navigate menu ----------------------------------
|
||||
MenuWrapper* navigateMenu = addMenu("Navigate");
|
||||
|
||||
// Navigate > Home -- FIXME: needs implementation
|
||||
auto homeAction = addActionToQMenuAndActionHash(navigateMenu, "Home");
|
||||
homeAction->setDisabled(true);
|
||||
|
||||
addActionToQMenuAndActionHash(navigateMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L,
|
||||
dialogsManager.data(), SLOT(toggleAddressBar()));
|
||||
|
||||
// Navigate > Directory -- FIXME: needs implementation
|
||||
addActionToQMenuAndActionHash(navigateMenu, "Directory");
|
||||
|
||||
// Navigate > Bookmark related menus -- Note: the Bookmark class adds its own submenus here.
|
||||
qApp->getBookmarks()->setupMenus(this, navigateMenu);
|
||||
|
||||
// Navigate > Copy Address [advanced]
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
addActionToQMenuAndActionHash(navigateMenu, MenuOption::CopyAddress, 0,
|
||||
addressManager.data(), SLOT(copyAddress()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Navigate > Copy Path [advanced]
|
||||
addActionToQMenuAndActionHash(navigateMenu, MenuOption::CopyPath, 0,
|
||||
addressManager.data(), SLOT(copyPath()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
// Market menu ----------------------------------
|
||||
MenuWrapper* marketMenu = addMenu("Market");
|
||||
|
||||
// Market > Marketplace... -- FIXME: needs implementation
|
||||
auto marketplaceAction = addActionToQMenuAndActionHash(marketMenu, "Marketplace...");
|
||||
marketplaceAction->setDisabled(true);
|
||||
|
||||
|
||||
// Settings menu ----------------------------------
|
||||
MenuWrapper* settingsMenu = addMenu("Settings");
|
||||
|
||||
// Settings > Advance Menus
|
||||
addCheckableActionToQMenuAndActionHash(settingsMenu, "Advanced Menus", 0, false, this, SLOT(toggleAdvancedMenus()));
|
||||
|
||||
// Settings > Developer Menus
|
||||
addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus()));
|
||||
|
||||
// Settings > General...
|
||||
addActionToQMenuAndActionHash(settingsMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma,
|
||||
dialogsManager.data(), SLOT(editPreferences()), QAction::PreferencesRole);
|
||||
|
||||
// Settings > Avatar...-- FIXME: needs implementation
|
||||
auto avatarAction = addActionToQMenuAndActionHash(settingsMenu, "Avatar...");
|
||||
avatarAction->setDisabled(true);
|
||||
|
||||
// Settings > Audio...-- FIXME: needs implementation
|
||||
auto audioAction = addActionToQMenuAndActionHash(settingsMenu, "Audio...");
|
||||
audioAction->setDisabled(true);
|
||||
|
||||
// Settings > LOD...-- FIXME: needs implementation
|
||||
auto lodAction = addActionToQMenuAndActionHash(settingsMenu, "LOD...");
|
||||
lodAction->setDisabled(true);
|
||||
|
||||
// Settings > Control with Speech [advanced]
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
auto speechRecognizer = DependencyManager::get<SpeechRecognizer>();
|
||||
QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::ControlWithSpeech,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_C,
|
||||
speechRecognizer->getEnabled(),
|
||||
speechRecognizer.data(),
|
||||
SLOT(setEnabled(bool)),
|
||||
UNSPECIFIED_POSITION, "Advanced");
|
||||
QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(settingsMenu, MenuOption::ControlWithSpeech,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_C,
|
||||
speechRecognizer->getEnabled(),
|
||||
speechRecognizer.data(),
|
||||
SLOT(setEnabled(bool)),
|
||||
UNSPECIFIED_POSITION, "Advanced");
|
||||
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
|
||||
#endif
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
|
||||
0, // QML Qt::Key_Backslash,
|
||||
dialogsManager.data(), SLOT(showIRCLink()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0,
|
||||
qApp, SLOT(showFriendsWindow()));
|
||||
|
||||
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,
|
||||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToEveryone);
|
||||
|
||||
QAction* visibleToFriends = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToFriends,
|
||||
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends,
|
||||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToFriends);
|
||||
|
||||
QAction* visibleToNoOne = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToNoOne,
|
||||
0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::None,
|
||||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToNoOne);
|
||||
|
||||
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
|
||||
discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged);
|
||||
}
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::ToolWindow,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_T,
|
||||
dialogsManager.data(),
|
||||
SLOT(toggleToolWindow()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::Console,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
DependencyManager::get<StandAloneJSConsole>().data(),
|
||||
SLOT(toggleConsole()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::ResetSensors,
|
||||
0, // QML Qt::Key_Apostrophe,
|
||||
qApp,
|
||||
SLOT(resetSensors()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
||||
qApp, SLOT(packageModel()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addMenu(DisplayPlugin::MENU_PATH());
|
||||
{
|
||||
MenuWrapper* displayModeMenu = addMenu(MenuOption::OutputMenu);
|
||||
QActionGroup* displayModeGroup = new QActionGroup(displayModeMenu);
|
||||
displayModeGroup->setExclusive(true);
|
||||
}
|
||||
|
||||
MenuWrapper* avatarMenu = addMenu("Avatar");
|
||||
QObject* avatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
// Settings > Input Devices
|
||||
MenuWrapper* inputModeMenu = addMenu(MenuOption::InputMenu, "Advanced");
|
||||
QActionGroup* inputModeGroup = new QActionGroup(inputModeMenu);
|
||||
inputModeGroup->setExclusive(false);
|
||||
|
||||
MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size");
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::IncreaseAvatarSize,
|
||||
0, // QML Qt::Key_Plus,
|
||||
avatar,
|
||||
SLOT(increaseSize()));
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::DecreaseAvatarSize,
|
||||
0, // QML Qt::Key_Minus,
|
||||
avatar,
|
||||
SLOT(decreaseSize()));
|
||||
addActionToQMenuAndActionHash(avatarSizeMenu,
|
||||
MenuOption::ResetAvatarSize,
|
||||
0, // QML Qt::Key_Equal,
|
||||
avatar,
|
||||
SLOT(resetSize()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
|
||||
NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
MenuWrapper* viewMenu = addMenu("View");
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
MenuWrapper* cameraModeMenu = viewMenu->addMenu("Camera Mode");
|
||||
QActionGroup* cameraModeGroup = new QActionGroup(cameraModeMenu);
|
||||
cameraModeGroup->setExclusive(true);
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||
MenuOption::FirstPerson, 0, // QML Qt:: Key_P
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||
MenuOption::ThirdPerson, 0,
|
||||
true, qApp, SLOT(cameraMenuChanged())));
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||
MenuOption::IndependentMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||
MenuOption::CameraEntityMode, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||
MenuOption::FullscreenMirror, 0, // QML Qt::Key_H,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
|
||||
0, //QML Qt::SHIFT | Qt::Key_H,
|
||||
true);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView,
|
||||
0, false, qApp, SLOT(rotationModeChanged()),
|
||||
UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::WorldAxes, 0, false, NULL, NULL, UNSPECIFIED_POSITION, "Developer");
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, 0, false, NULL, NULL, UNSPECIFIED_POSITION, "Developer");
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
qApp, SLOT(toggleLogDialog()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::AudioNetworkStats, 0,
|
||||
dialogsManager.data(), SLOT(audioStatsDetails()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0,
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0,
|
||||
dialogsManager.data(), SLOT(octreeStatsDetails()), QAction::NoRole, UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, "Advanced Menus", 0, false, this, SLOT(toggleAdvancedMenus()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, "Developer Menus", 0, false, this, SLOT(toggleDeveloperMenus()));
|
||||
|
||||
// Developer menu ----------------------------------
|
||||
MenuWrapper* developerMenu = addMenu("Developer", "Developer");
|
||||
|
||||
// Developer > Render >>>
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
|
||||
0, // QML Qt::SHIFT | Qt::Key_A,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DebugAmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Antialiasing);
|
||||
|
||||
// Developer > Render > Ambient Light
|
||||
MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight);
|
||||
QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu);
|
||||
ambientLightGroup->setExclusive(true);
|
||||
|
@ -333,8 +349,10 @@ Menu::Menu() {
|
|||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false));
|
||||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false));
|
||||
|
||||
// Developer > Render > Throttle FPS If Not Focus
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true);
|
||||
|
||||
// Developer > Render > Resolution
|
||||
MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
||||
QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu);
|
||||
resolutionGroup->setExclusive(true);
|
||||
|
@ -344,37 +362,40 @@ Menu::Menu() {
|
|||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
|
||||
|
||||
// Developer > Render > Stars
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
|
||||
0, // QML Qt::Key_Asterisk,
|
||||
true);
|
||||
|
||||
// Developer > Render > LOD Tools
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
||||
0, // QML Qt::SHIFT | Qt::Key_L,
|
||||
dialogsManager.data(), SLOT(lodTools()));
|
||||
|
||||
|
||||
// Developer > Assets >>>
|
||||
MenuWrapper* assetDeveloperMenu = developerMenu->addMenu("Assets");
|
||||
|
||||
auto& assetDialogFactory = AssetUploadDialogFactory::getInstance();
|
||||
assetDialogFactory.setDialogParent(this);
|
||||
|
||||
QAction* assetUpload = addActionToQMenuAndActionHash(assetDeveloperMenu,
|
||||
MenuOption::UploadAsset,
|
||||
0,
|
||||
&assetDialogFactory,
|
||||
SLOT(showDialog()));
|
||||
|
||||
MenuOption::UploadAsset,
|
||||
0,
|
||||
&assetDialogFactory,
|
||||
SLOT(showDialog()));
|
||||
|
||||
// disable the asset upload action by default - it gets enabled only if asset server becomes present
|
||||
assetUpload->setEnabled(false);
|
||||
|
||||
|
||||
auto& atpMigrator = ATPAssetMigrator::getInstance();
|
||||
atpMigrator.setDialogParent(this);
|
||||
|
||||
|
||||
addActionToQMenuAndActionHash(assetDeveloperMenu, MenuOption::AssetMigration,
|
||||
0, &atpMigrator,
|
||||
SLOT(loadEntityServerFile()));
|
||||
|
||||
0, &atpMigrator,
|
||||
SLOT(loadEntityServerFile()));
|
||||
|
||||
// Developer > Avatar >>>
|
||||
MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar");
|
||||
|
||||
// Developer > Avatar > Face Tracking
|
||||
MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking");
|
||||
{
|
||||
QActionGroup* faceTrackerGroup = new QActionGroup(avatarDebugMenu);
|
||||
|
@ -423,6 +444,7 @@ Menu::Menu() {
|
|||
#endif
|
||||
|
||||
#ifdef HAVE_IVIEWHMD
|
||||
// Developer > Avatar > Eye Tracking
|
||||
MenuWrapper* eyeTrackingMenu = avatarDebugMenu->addMenu("Eye Tracking");
|
||||
addCheckableActionToQMenuAndActionHash(eyeTrackingMenu, MenuOption::SMIEyeTracking, 0, false,
|
||||
qApp, SLOT(setActiveEyeTracker()));
|
||||
|
@ -439,9 +461,8 @@ Menu::Menu() {
|
|||
qApp, SLOT(setActiveEyeTracker()));
|
||||
#endif
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false,
|
||||
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
|
||||
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
|
||||
|
@ -450,13 +471,13 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false,
|
||||
avatar, SLOT(setEnableDebugDrawDefaultPose(bool)));
|
||||
avatar, SLOT(setEnableDebugDrawDefaultPose(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawAnimPose, 0, false,
|
||||
avatar, SLOT(setEnableDebugDrawAnimPose(bool)));
|
||||
avatar, SLOT(setEnableDebugDrawAnimPose(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawPosition, 0, false,
|
||||
avatar, SLOT(setEnableDebugDrawPosition(bool)));
|
||||
avatar, SLOT(setEnableDebugDrawPosition(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true,
|
||||
avatar, SLOT(setEnableMeshVisible(bool)));
|
||||
avatar, SLOT(setEnableMeshVisible(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
|
||||
|
@ -473,37 +494,37 @@ Menu::Menu() {
|
|||
avatar, SLOT(updateMotionBehaviorFromMenu()),
|
||||
UNSPECIFIED_POSITION, "Developer");
|
||||
|
||||
|
||||
|
||||
// Developer > Hands >>>
|
||||
MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::LowVelocityFilter, 0, true,
|
||||
qApp, SLOT(setLowVelocityFilter(bool)));
|
||||
qApp, SLOT(setLowVelocityFilter(bool)));
|
||||
|
||||
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
|
||||
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
|
||||
|
||||
// Developer > Network >>>
|
||||
MenuWrapper* networkMenu = developerMenu->addMenu("Network");
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false,
|
||||
qApp->getEntityEditPacketSender(),
|
||||
SLOT(toggleNackPackets()));
|
||||
qApp->getEntityEditPacketSender(),
|
||||
SLOT(toggleNackPackets()));
|
||||
addCheckableActionToQMenuAndActionHash(networkMenu,
|
||||
MenuOption::DisableActivityLogger,
|
||||
0,
|
||||
false,
|
||||
&UserActivityLogger::getInstance(),
|
||||
SLOT(disable(bool)));
|
||||
MenuOption::DisableActivityLogger,
|
||||
0,
|
||||
false,
|
||||
&UserActivityLogger::getInstance(),
|
||||
SLOT(disable(bool)));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0,
|
||||
dialogsManager.data(), SLOT(cachesSizeDialog()));
|
||||
dialogsManager.data(), SLOT(cachesSizeDialog()));
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0,
|
||||
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
|
||||
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
|
||||
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0,
|
||||
dialogsManager.data(), SLOT(showDomainConnectionDialog()));
|
||||
dialogsManager.data(), SLOT(showDomainConnectionDialog()));
|
||||
|
||||
// Developer > Timing and Stats >>>
|
||||
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);
|
||||
|
@ -520,60 +541,34 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
|
||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::ShowRealtimeEntityStats);
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
// Developer > Audio >>>
|
||||
MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio");
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction,
|
||||
0,
|
||||
true,
|
||||
audioIO.data(),
|
||||
SLOT(toggleAudioNoiseReduction()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true,
|
||||
audioIO.data(), SLOT(toggleAudioNoiseReduction()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false,
|
||||
audioIO.data(), SLOT(toggleServerEcho()));
|
||||
audioIO.data(), SLOT(toggleServerEcho()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false,
|
||||
audioIO.data(), SLOT(toggleLocalEcho()));
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteAudio,
|
||||
Qt::CTRL | Qt::Key_M,
|
||||
false,
|
||||
audioIO.data(),
|
||||
SLOT(toggleMute()));
|
||||
addActionToQMenuAndActionHash(audioDebugMenu,
|
||||
MenuOption::MuteEnvironment,
|
||||
0,
|
||||
audioIO.data(),
|
||||
SLOT(sendMuteEnvironmentPacket()));
|
||||
audioIO.data(), SLOT(toggleLocalEcho()));
|
||||
addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteEnvironment, 0,
|
||||
audioIO.data(), SLOT(sendMuteEnvironmentPacket()));
|
||||
|
||||
auto scope = DependencyManager::get<AudioScope>();
|
||||
|
||||
MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope");
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope,
|
||||
Qt::CTRL | Qt::Key_P, false,
|
||||
scope.data(),
|
||||
SLOT(toggle()));
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_P ,
|
||||
false,
|
||||
scope.data(),
|
||||
SLOT(togglePause()));
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false,
|
||||
scope.data(), SLOT(toggle()));
|
||||
addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause, Qt::CTRL | Qt::SHIFT | Qt::Key_P, false,
|
||||
scope.data(), SLOT(togglePause()));
|
||||
|
||||
addDisabledActionAndSeparator(audioScopeMenu, "Display Frames");
|
||||
{
|
||||
QAction *fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames,
|
||||
0,
|
||||
true,
|
||||
scope.data(),
|
||||
SLOT(selectAudioScopeFiveFrames()));
|
||||
QAction* fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames,
|
||||
0, true, scope.data(), SLOT(selectAudioScopeFiveFrames()));
|
||||
|
||||
QAction *twentyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeTwentyFrames,
|
||||
0,
|
||||
false,
|
||||
scope.data(),
|
||||
SLOT(selectAudioScopeTwentyFrames()));
|
||||
QAction* twentyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeTwentyFrames,
|
||||
0, false, scope.data(), SLOT(selectAudioScopeTwentyFrames()));
|
||||
|
||||
QAction *fiftyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiftyFrames,
|
||||
0,
|
||||
false,
|
||||
scope.data(),
|
||||
SLOT(selectAudioScopeFiftyFrames()));
|
||||
QAction* fiftyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiftyFrames,
|
||||
0, false, scope.data(), SLOT(selectAudioScopeFiftyFrames()));
|
||||
|
||||
QActionGroup* audioScopeFramesGroup = new QActionGroup(audioScopeMenu);
|
||||
audioScopeFramesGroup->addAction(fiveFrames);
|
||||
|
@ -581,19 +576,69 @@ Menu::Menu() {
|
|||
audioScopeFramesGroup->addAction(fiftyFrames);
|
||||
}
|
||||
|
||||
// Developer > Physics >>>
|
||||
MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics");
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned);
|
||||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls);
|
||||
|
||||
// Developer > Display Crash Options
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true);
|
||||
// Developer > Crash Application
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::CrashInterface, 0, qApp, SLOT(crashApplication()));
|
||||
|
||||
MenuWrapper* helpMenu = addMenu("Help");
|
||||
addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp()));
|
||||
// Developer > Log...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::Log, Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
qApp, SLOT(toggleLogDialog()));
|
||||
|
||||
// Developer > Stats
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats);
|
||||
|
||||
// Developer > Audio Stats...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::AudioNetworkStats, 0,
|
||||
dialogsManager.data(), SLOT(audioStatsDetails()));
|
||||
|
||||
// Developer > Bandwidth Stats...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::BandwidthDetails, 0,
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()));
|
||||
|
||||
// Developer > Entity Stats...
|
||||
addActionToQMenuAndActionHash(developerMenu, MenuOption::OctreeStats, 0,
|
||||
dialogsManager.data(), SLOT(octreeStatsDetails()));
|
||||
|
||||
// Developer > World Axes
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::WorldAxes);
|
||||
|
||||
|
||||
|
||||
#if 0 /// -------------- REMOVED FOR NOW --------------
|
||||
addDisabledActionAndSeparator(navigateMenu, "History");
|
||||
QAction* backAction = addActionToQMenuAndActionHash(navigateMenu, MenuOption::Back, 0, addressManager.data(), SLOT(goBack()));
|
||||
QAction* forwardAction = addActionToQMenuAndActionHash(navigateMenu, MenuOption::Forward, 0, addressManager.data(), SLOT(goForward()));
|
||||
|
||||
// connect to the AddressManager signal to enable and disable the back and forward menu items
|
||||
connect(addressManager.data(), &AddressManager::goBackPossible, backAction, &QAction::setEnabled);
|
||||
connect(addressManager.data(), &AddressManager::goForwardPossible, forwardAction, &QAction::setEnabled);
|
||||
|
||||
// set the two actions to start disabled since the stacks are clear on startup
|
||||
backAction->setDisabled(true);
|
||||
forwardAction->setDisabled(true);
|
||||
|
||||
MenuWrapper* toolsMenu = addMenu("Tools");
|
||||
addActionToQMenuAndActionHash(toolsMenu,
|
||||
MenuOption::ToolWindow,
|
||||
Qt::CTRL | Qt::ALT | Qt::Key_T,
|
||||
dialogsManager.data(),
|
||||
SLOT(toggleToolWindow()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true,
|
||||
NULL, NULL, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::CenterPlayerInView,
|
||||
0, false, qApp, SLOT(rotationModeChanged()),
|
||||
UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
QAction* aboutAction = helpMenu->addAction(MenuOption::AboutApp);
|
||||
connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutApp()));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
void saveSettings();
|
||||
|
||||
MenuWrapper* getMenu(const QString& menuName);
|
||||
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
|
||||
|
||||
void triggerOption(const QString& menuOption);
|
||||
QAction* getActionForOption(const QString& menuOption);
|
||||
|
@ -130,7 +131,6 @@ private:
|
|||
const QString& grouping = QString());
|
||||
|
||||
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);
|
||||
|
@ -205,7 +205,6 @@ namespace MenuOption {
|
|||
const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene";
|
||||
const QString EchoLocalAudio = "Echo Local Audio";
|
||||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString EditEntitiesHelp = "Edit Entities Help...";
|
||||
const QString Enable3DTVMode = "Enable 3DTV Mode";
|
||||
const QString EnableCharacterController = "Enable avatar collisions";
|
||||
const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation";
|
||||
|
@ -219,8 +218,9 @@ namespace MenuOption {
|
|||
const QString FixGaze = "Fix Gaze (no saccade)";
|
||||
const QString Forward = "Forward";
|
||||
const QString FrameTimer = "Show Timer";
|
||||
const QString FullscreenMirror = "Fullscreen Mirror";
|
||||
const QString FullscreenMirror = "Mirror";
|
||||
const QString GlowWhenSpeaking = "Glow When Speaking";
|
||||
const QString Help = "Help...";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
const QString IndependentMode = "Independent Mode";
|
||||
const QString InputMenu = "Avatar>Input Devices";
|
||||
|
@ -234,7 +234,7 @@ namespace MenuOption {
|
|||
const QString LogExtraTimings = "Log Extra Timing Details";
|
||||
const QString LowVelocityFilter = "Low Velocity Filter";
|
||||
const QString MeshVisible = "Draw Mesh";
|
||||
const QString Mirror = "Mirror";
|
||||
const QString MiniMirror = "Mini Mirror";
|
||||
const QString MuteAudio = "Mute Microphone";
|
||||
const QString MuteEnvironment = "Mute Environment";
|
||||
const QString MuteFaceTracking = "Mute Face Tracking";
|
||||
|
@ -249,7 +249,7 @@ namespace MenuOption {
|
|||
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 Preferences = "General...";
|
||||
const QString Quit = "Quit";
|
||||
const QString ReloadAllScripts = "Reload All Scripts";
|
||||
const QString ReloadContent = "Reload Content (Clears all caches)";
|
||||
|
@ -277,7 +277,7 @@ namespace MenuOption {
|
|||
const QString RenderAmbientLight9 = "FUNSTON_BEACH_SUNSET";
|
||||
const QString ResetAvatarSize = "Reset Avatar Size";
|
||||
const QString ResetSensors = "Reset Sensors";
|
||||
const QString RunningScripts = "Running Scripts";
|
||||
const QString RunningScripts = "Running Scripts...";
|
||||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString ScriptEditor = "Script Editor...";
|
||||
const QString ScriptedMotorControl = "Enable Scripted Motor Control";
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QTemporaryDir>
|
||||
|
||||
#include <FSTReader.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
#include "ModelSelector.h"
|
||||
#include "ModelPropertiesDialog.h"
|
||||
|
@ -78,7 +79,7 @@ bool ModelPackager::loadModel() {
|
|||
if (_modelFile.completeSuffix().contains("fst")) {
|
||||
QFile fst(_modelFile.filePath());
|
||||
if (!fst.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QMessageBox::warning(NULL,
|
||||
OffscreenUi::warning(NULL,
|
||||
QString("ModelPackager::loadModel()"),
|
||||
QString("Could not open FST file %1").arg(_modelFile.filePath()),
|
||||
QMessageBox::Ok);
|
||||
|
@ -97,7 +98,7 @@ bool ModelPackager::loadModel() {
|
|||
// open the fbx file
|
||||
QFile fbx(_fbxInfo.filePath());
|
||||
if (!_fbxInfo.exists() || !_fbxInfo.isFile() || !fbx.open(QIODevice::ReadOnly)) {
|
||||
QMessageBox::warning(NULL,
|
||||
OffscreenUi::warning(NULL,
|
||||
QString("ModelPackager::loadModel()"),
|
||||
QString("Could not open FBX file %1").arg(_fbxInfo.filePath()),
|
||||
QMessageBox::Ok);
|
||||
|
@ -402,7 +403,7 @@ bool ModelPackager::copyTextures(const QString& oldDir, const QDir& newDir) {
|
|||
}
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
QMessageBox::warning(nullptr, "ModelPackager::copyTextures()",
|
||||
OffscreenUi::warning(nullptr, "ModelPackager::copyTextures()",
|
||||
"Missing textures:" + errors);
|
||||
qCDebug(interfaceapp) << "ModelPackager::copyTextures():" << errors;
|
||||
return false;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <FSTReader.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
#include "ModelPropertiesDialog.h"
|
||||
|
||||
|
@ -200,7 +201,7 @@ void ModelPropertiesDialog::chooseTextureDirectory() {
|
|||
return;
|
||||
}
|
||||
if (!directory.startsWith(_basePath)) {
|
||||
QMessageBox::warning(NULL, "Invalid texture directory", "Texture directory must be child of base path.");
|
||||
OffscreenUi::warning(NULL, "Invalid texture directory", "Texture directory must be child of base path.");
|
||||
return;
|
||||
}
|
||||
_textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1));
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
#include <RenderArgs.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include "../../libraries/render-utils/stars_vert.h"
|
||||
#include "../../libraries/render-utils/stars_frag.h"
|
||||
#include <stars_vert.h>
|
||||
#include <stars_frag.h>
|
||||
|
||||
#include "../../libraries/render-utils/standardTransformPNTC_vert.h"
|
||||
#include "../../libraries/render-utils/starsGrid_frag.h"
|
||||
#include <standardTransformPNTC_vert.h>
|
||||
#include <starsGrid_frag.h>
|
||||
|
||||
//static const float TILT = 0.23f;
|
||||
static const float TILT = 0.0f;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <AssetUpload.h>
|
||||
#include <ResourceManager.h>
|
||||
|
||||
#include "OffscreenUi.h"
|
||||
#include "../ui/AssetUploadDialogFactory.h"
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(asset_migrator);
|
||||
|
@ -52,7 +53,7 @@ void ATPAssetMigrator::loadEntityServerFile() {
|
|||
" continue?\n\nMake sure you are connected to the right domain."
|
||||
};
|
||||
|
||||
auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT,
|
||||
auto button = OffscreenUi::question(_dialogParent, MESSAGE_BOX_TITLE, MIGRATION_CONFIRMATION_TEXT,
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
||||
if (button == QMessageBox::No) {
|
||||
|
@ -67,7 +68,7 @@ void ATPAssetMigrator::loadEntityServerFile() {
|
|||
QByteArray jsonData;
|
||||
|
||||
if (!gunzip(compressedJsonData, jsonData)) {
|
||||
QMessageBox::warning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format.");
|
||||
OffscreenUi::warning(_dialogParent, "Error", "The file at" + filename + "was not in gzip format.");
|
||||
}
|
||||
|
||||
QJsonDocument modelsJSON = QJsonDocument::fromJson(jsonData);
|
||||
|
@ -107,7 +108,7 @@ void ATPAssetMigrator::loadEntityServerFile() {
|
|||
if (request->getResult() == ResourceRequest::Success) {
|
||||
migrateResource(request);
|
||||
} else {
|
||||
QMessageBox::warning(_dialogParent, "Error",
|
||||
OffscreenUi::warning(_dialogParent, "Error",
|
||||
QString("Could not retrieve asset at %1").arg(modelURL.toString()));
|
||||
}
|
||||
request->deleteLater();
|
||||
|
@ -115,7 +116,7 @@ void ATPAssetMigrator::loadEntityServerFile() {
|
|||
|
||||
request->send();
|
||||
} else {
|
||||
QMessageBox::warning(_dialogParent, "Error",
|
||||
OffscreenUi::warning(_dialogParent, "Error",
|
||||
QString("Could not create request for asset at %1").arg(modelURL.toString()));
|
||||
}
|
||||
|
||||
|
@ -129,7 +130,7 @@ void ATPAssetMigrator::loadEntityServerFile() {
|
|||
_doneReading = true;
|
||||
|
||||
} else {
|
||||
QMessageBox::warning(_dialogParent, "Error",
|
||||
OffscreenUi::warning(_dialogParent, "Error",
|
||||
"There was a problem loading that entity-server file for ATP asset migration. Please try again");
|
||||
}
|
||||
}
|
||||
|
@ -212,7 +213,7 @@ bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) {
|
|||
"Select \"No\" to be prompted for each discovered asset."
|
||||
};
|
||||
|
||||
auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT,
|
||||
auto button = OffscreenUi::question(_dialogParent, MESSAGE_BOX_TITLE, COMPLETE_MIGRATION_TEXT,
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
||||
if (button == QMessageBox::Yes) {
|
||||
|
@ -226,7 +227,7 @@ bool ATPAssetMigrator::wantsToMigrateResource(const QUrl& url) {
|
|||
return true;
|
||||
} else {
|
||||
// present a dialog asking the user if they want to migrate this specific resource
|
||||
auto button = QMessageBox::question(_dialogParent, MESSAGE_BOX_TITLE,
|
||||
auto button = OffscreenUi::question(_dialogParent, MESSAGE_BOX_TITLE,
|
||||
"Would you like to migrate the following resource?\n" + url.toString(),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
return button == QMessageBox::Yes;
|
||||
|
@ -254,11 +255,11 @@ void ATPAssetMigrator::saveEntityServerFile() {
|
|||
QMessageBox::information(_dialogParent, "Success",
|
||||
QString("Your new entities file has been saved at %1").arg(saveName));
|
||||
} else {
|
||||
QMessageBox::warning(_dialogParent, "Error", "Could not gzip JSON data for new entities file.");
|
||||
OffscreenUi::warning(_dialogParent, "Error", "Could not gzip JSON data for new entities file.");
|
||||
}
|
||||
|
||||
} else {
|
||||
QMessageBox::warning(_dialogParent, "Error",
|
||||
OffscreenUi::warning(_dialogParent, "Error",
|
||||
QString("Could not open file at %1 to write new entities file to.").arg(saveName));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "Util.h"
|
||||
#include "world.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "SoftAttachmentModel.h"
|
||||
#include <Rig.h>
|
||||
|
||||
using namespace std;
|
||||
|
@ -107,9 +108,10 @@ Avatar::Avatar(RigPointer rig) :
|
|||
}
|
||||
|
||||
Avatar::~Avatar() {
|
||||
assert(_motionState == nullptr);
|
||||
for(auto attachment : _unusedAttachments) {
|
||||
delete attachment;
|
||||
assert(isDead()); // mark dead before calling the dtor
|
||||
if (_motionState) {
|
||||
delete _motionState;
|
||||
_motionState = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,26 +188,6 @@ void Avatar::simulate(float deltaTime) {
|
|||
qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance();
|
||||
}
|
||||
|
||||
const bool isControllerLogging = DependencyManager::get<AvatarManager>()->getRenderDistanceControllerIsLogging();
|
||||
float renderDistance = DependencyManager::get<AvatarManager>()->getRenderDistance();
|
||||
const float SKIP_HYSTERESIS_PROPORTION = isControllerLogging ? 0.0f : BILLBOARD_HYSTERESIS_PROPORTION;
|
||||
float distance = glm::distance(qApp->getCamera()->getPosition(), getPosition());
|
||||
if (_shouldSkipRender) {
|
||||
if (distance < renderDistance * (1.0f - SKIP_HYSTERESIS_PROPORTION)) {
|
||||
_shouldSkipRender = false;
|
||||
_skeletonModel.setVisibleInScene(true, qApp->getMain3DScene());
|
||||
if (!isControllerLogging) { // Test for isMyAvatar is prophylactic. Never occurs in current code.
|
||||
qCDebug(interfaceapp) << "Rerendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance;
|
||||
}
|
||||
}
|
||||
} else if (distance > renderDistance * (1.0f + SKIP_HYSTERESIS_PROPORTION)) {
|
||||
_shouldSkipRender = true;
|
||||
_skeletonModel.setVisibleInScene(false, qApp->getMain3DScene());
|
||||
if (!isControllerLogging) {
|
||||
qCDebug(interfaceapp) << "Unrendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance;
|
||||
}
|
||||
}
|
||||
|
||||
// simple frustum check
|
||||
float boundingRadius = getBillboardSize();
|
||||
bool inViewFrustum = qApp->getViewFrustum()->sphereInFrustum(getPosition(), boundingRadius) !=
|
||||
|
@ -257,6 +239,9 @@ void Avatar::simulate(float deltaTime) {
|
|||
// until velocity is included in AvatarData update message.
|
||||
//_position += _velocity * deltaTime;
|
||||
measureMotionDerivatives(deltaTime);
|
||||
|
||||
simulateAttachments(deltaTime);
|
||||
updatePalms();
|
||||
}
|
||||
|
||||
bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const {
|
||||
|
@ -324,7 +309,7 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene>
|
|||
_skeletonModel.addToScene(scene, pendingChanges);
|
||||
getHead()->getFaceModel().addToScene(scene, pendingChanges);
|
||||
|
||||
for (auto attachmentModel : _attachmentModels) {
|
||||
for (auto& attachmentModel : _attachmentModels) {
|
||||
attachmentModel->addToScene(scene, pendingChanges);
|
||||
}
|
||||
|
||||
|
@ -335,7 +320,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
|
|||
pendingChanges.removeItem(_renderItemID);
|
||||
_skeletonModel.removeFromScene(scene, pendingChanges);
|
||||
getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
|
||||
for (auto attachmentModel : _attachmentModels) {
|
||||
for (auto& attachmentModel : _attachmentModels) {
|
||||
attachmentModel->removeFromScene(scene, pendingChanges);
|
||||
}
|
||||
}
|
||||
|
@ -565,15 +550,14 @@ void Avatar::fixupModelsInScene() {
|
|||
faceModel.removeFromScene(scene, pendingChanges);
|
||||
faceModel.addToScene(scene, pendingChanges);
|
||||
}
|
||||
for (auto attachmentModel : _attachmentModels) {
|
||||
for (auto& attachmentModel : _attachmentModels) {
|
||||
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
|
||||
attachmentModel->removeFromScene(scene, pendingChanges);
|
||||
attachmentModel->addToScene(scene, pendingChanges);
|
||||
}
|
||||
}
|
||||
for (auto attachmentModelToRemove : _attachmentsToRemove) {
|
||||
for (auto& attachmentModelToRemove : _attachmentsToRemove) {
|
||||
attachmentModelToRemove->removeFromScene(scene, pendingChanges);
|
||||
_unusedAttachments << attachmentModelToRemove;
|
||||
}
|
||||
_attachmentsToRemove.clear();
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
|
@ -603,21 +587,29 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Avatar::simulateAttachments(float deltaTime) {
|
||||
for (int i = 0; i < _attachmentModels.size(); i++) {
|
||||
for (int i = 0; i < (int)_attachmentModels.size(); i++) {
|
||||
const AttachmentData& attachment = _attachmentData.at(i);
|
||||
Model* model = _attachmentModels.at(i);
|
||||
auto& model = _attachmentModels.at(i);
|
||||
int jointIndex = getJointIndex(attachment.jointName);
|
||||
glm::vec3 jointPosition;
|
||||
glm::quat jointRotation;
|
||||
if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) &&
|
||||
_skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) {
|
||||
model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale());
|
||||
model->setRotation(jointRotation * attachment.rotation);
|
||||
model->setScaleToFit(true, getUniformScale() * attachment.scale, true); // hack to force rescale
|
||||
model->setSnapModelToCenter(false); // hack to force resnap
|
||||
model->setSnapModelToCenter(true);
|
||||
if (attachment.isSoft) {
|
||||
// soft attachments do not have transform offsets
|
||||
model->setTranslation(getPosition());
|
||||
model->setRotation(getOrientation() * Quaternions::Y_180);
|
||||
model->simulate(deltaTime);
|
||||
} else {
|
||||
if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) &&
|
||||
_skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) {
|
||||
model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale());
|
||||
model->setRotation(jointRotation * attachment.rotation);
|
||||
model->setScaleToFit(true, getUniformScale() * attachment.scale, true); // hack to force rescale
|
||||
model->setSnapModelToCenter(false); // hack to force resnap
|
||||
model->setSnapModelToCenter(true);
|
||||
model->simulate(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -940,13 +932,48 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_skeletonModel.setURL(_skeletonModelURL);
|
||||
}
|
||||
|
||||
// create new model, can return an instance of a SoftAttachmentModel rather then Model
|
||||
static std::shared_ptr<Model> allocateAttachmentModel(bool isSoft, RigPointer rigOverride) {
|
||||
if (isSoft) {
|
||||
// cast to std::shared_ptr<Model>
|
||||
return std::dynamic_pointer_cast<Model>(std::make_shared<SoftAttachmentModel>(std::make_shared<Rig>(), nullptr, rigOverride));
|
||||
} else {
|
||||
return std::make_shared<Model>(std::make_shared<Rig>());
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||
AvatarData::setAttachmentData(attachmentData);
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::DirectConnection,
|
||||
Q_ARG(const QVector<AttachmentData>, attachmentData));
|
||||
return;
|
||||
}
|
||||
|
||||
auto oldAttachmentData = _attachmentData;
|
||||
AvatarData::setAttachmentData(attachmentData);
|
||||
|
||||
// if number of attachments has been reduced, remove excess models.
|
||||
while ((int)_attachmentModels.size() > attachmentData.size()) {
|
||||
auto attachmentModel = _attachmentModels.back();
|
||||
_attachmentModels.pop_back();
|
||||
_attachmentsToRemove.push_back(attachmentModel);
|
||||
}
|
||||
|
||||
for (int i = 0; i < attachmentData.size(); i++) {
|
||||
if (i == (int)_attachmentModels.size()) {
|
||||
// if number of attachments has been increased, we need to allocate a new model
|
||||
_attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel.getRig()));
|
||||
}
|
||||
else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) {
|
||||
// if the attachment has changed type, we need to re-allocate a new one.
|
||||
_attachmentsToRemove.push_back(_attachmentModels[i]);
|
||||
_attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel.getRig());
|
||||
}
|
||||
_attachmentModels[i]->setURL(attachmentData[i].modelURL);
|
||||
}
|
||||
|
||||
// AJT: TODO REMOVE
|
||||
/*
|
||||
// make sure we have as many models as attachments
|
||||
while (_attachmentModels.size() < attachmentData.size()) {
|
||||
Model* model = nullptr;
|
||||
|
@ -959,16 +986,20 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
_attachmentModels.append(model);
|
||||
}
|
||||
while (_attachmentModels.size() > attachmentData.size()) {
|
||||
auto attachmentModel = _attachmentModels.takeLast();
|
||||
_attachmentsToRemove << attachmentModel;
|
||||
auto attachmentModel = _attachmentModels.back();
|
||||
_attachmentModels.pop_back();
|
||||
_attachmentsToRemove.push_back(attachmentModel);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// update the urls
|
||||
for (int i = 0; i < attachmentData.size(); i++) {
|
||||
_attachmentModels[i]->setURL(attachmentData.at(i).modelURL);
|
||||
_attachmentModels[i]->setSnapModelToCenter(true);
|
||||
_attachmentModels[i]->setScaleToFit(true, getUniformScale() * _attachmentData.at(i).scale);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void Avatar::setBillboard(const QByteArray& billboard) {
|
||||
|
@ -995,6 +1026,9 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
if (_moving && _motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
|
||||
}
|
||||
if (_moving || _hasNewJointRotations || _hasNewJointTranslations) {
|
||||
locationChanged();
|
||||
}
|
||||
endUpdate();
|
||||
|
||||
return bytesRead;
|
||||
|
@ -1126,34 +1160,24 @@ void Avatar::rebuildCollisionShape() {
|
|||
}
|
||||
}
|
||||
|
||||
// thread-safe
|
||||
glm::vec3 Avatar::getLeftPalmPosition() {
|
||||
glm::vec3 leftHandPosition;
|
||||
getSkeletonModel().getLeftHandPosition(leftHandPosition);
|
||||
glm::quat leftRotation;
|
||||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
|
||||
leftHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(leftRotation);
|
||||
return leftHandPosition;
|
||||
return _leftPalmPositionCache.get();
|
||||
}
|
||||
|
||||
// thread-safe
|
||||
glm::quat Avatar::getLeftPalmRotation() {
|
||||
glm::quat leftRotation;
|
||||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
|
||||
return leftRotation;
|
||||
return _leftPalmRotationCache.get();
|
||||
}
|
||||
|
||||
// thread-safe
|
||||
glm::vec3 Avatar::getRightPalmPosition() {
|
||||
glm::vec3 rightHandPosition;
|
||||
getSkeletonModel().getRightHandPosition(rightHandPosition);
|
||||
glm::quat rightRotation;
|
||||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
|
||||
rightHandPosition += HAND_TO_PALM_OFFSET * glm::inverse(rightRotation);
|
||||
return rightHandPosition;
|
||||
return _rightPalmPositionCache.get();
|
||||
}
|
||||
|
||||
// thread-safe
|
||||
glm::quat Avatar::getRightPalmRotation() {
|
||||
glm::quat rightRotation;
|
||||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
|
||||
return rightRotation;
|
||||
return _rightPalmRotationCache.get();
|
||||
}
|
||||
|
||||
void Avatar::setPosition(const glm::vec3& position) {
|
||||
|
@ -1165,3 +1189,24 @@ void Avatar::setOrientation(const glm::quat& orientation) {
|
|||
AvatarData::setOrientation(orientation);
|
||||
updateAttitude();
|
||||
}
|
||||
|
||||
void Avatar::updatePalms() {
|
||||
|
||||
// get palm rotations
|
||||
glm::quat leftPalmRotation, rightPalmRotation;
|
||||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftPalmRotation);
|
||||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightPalmRotation);
|
||||
|
||||
// get palm positions
|
||||
glm::vec3 leftPalmPosition, rightPalmPosition;
|
||||
getSkeletonModel().getLeftHandPosition(leftPalmPosition);
|
||||
getSkeletonModel().getRightHandPosition(rightPalmPosition);
|
||||
leftPalmPosition += HAND_TO_PALM_OFFSET * glm::inverse(leftPalmRotation);
|
||||
rightPalmPosition += HAND_TO_PALM_OFFSET * glm::inverse(rightPalmRotation);
|
||||
|
||||
// update thread-safe caches
|
||||
_leftPalmRotationCache.set(leftPalmRotation);
|
||||
_rightPalmRotationCache.set(rightPalmRotation);
|
||||
_leftPalmPositionCache.set(leftPalmPosition);
|
||||
_rightPalmPositionCache.set(rightPalmPosition);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "SkeletonModel.h"
|
||||
#include "world.h"
|
||||
#include "Rig.h"
|
||||
#include <ThreadSafeValueCache.h>
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar);
|
||||
|
@ -68,7 +69,7 @@ public:
|
|||
|
||||
void init();
|
||||
void simulate(float deltaTime);
|
||||
void simulateAttachments(float deltaTime);
|
||||
virtual void simulateAttachments(float deltaTime);
|
||||
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
|
||||
|
||||
|
@ -112,6 +113,8 @@ public:
|
|||
|
||||
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
|
||||
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
|
||||
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
|
||||
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; }
|
||||
|
||||
virtual void setFaceModelURL(const QUrl& faceModelURL) override;
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||
|
@ -159,7 +162,9 @@ public:
|
|||
|
||||
AvatarMotionState* getMotionState() { return _motionState; }
|
||||
|
||||
using SpatiallyNestable::setPosition;
|
||||
virtual void setPosition(const glm::vec3& position) override;
|
||||
using SpatiallyNestable::setOrientation;
|
||||
virtual void setOrientation(const glm::quat& orientation) override;
|
||||
|
||||
public slots:
|
||||
|
@ -177,9 +182,9 @@ protected:
|
|||
|
||||
SkeletonModel _skeletonModel;
|
||||
glm::vec3 _skeletonOffset;
|
||||
QVector<Model*> _attachmentModels;
|
||||
QVector<Model*> _attachmentsToRemove;
|
||||
QVector<Model*> _unusedAttachments;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentModels;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentsToRemove;
|
||||
|
||||
float _bodyYawDelta; // degrees/sec
|
||||
|
||||
// These position histories and derivatives are in the world-frame.
|
||||
|
@ -225,8 +230,15 @@ protected:
|
|||
|
||||
virtual void updateJointMappings() override;
|
||||
|
||||
virtual void updatePalms();
|
||||
|
||||
render::ItemID _renderItemID;
|
||||
|
||||
ThreadSafeValueCache<glm::vec3> _leftPalmPositionCache { glm::vec3() };
|
||||
ThreadSafeValueCache<glm::quat> _leftPalmRotationCache { glm::quat() };
|
||||
ThreadSafeValueCache<glm::vec3> _rightPalmPositionCache { glm::vec3() };
|
||||
ThreadSafeValueCache<glm::quat> _rightPalmRotationCache { glm::quat() };
|
||||
|
||||
private:
|
||||
bool _initialized;
|
||||
NetworkTexturePointer _billboardTexture;
|
||||
|
|
|
@ -89,6 +89,8 @@ void AvatarActionHold::prepareForPhysicsSimulation() {
|
|||
// code here for future reference.
|
||||
// _palmRotationFromRigidBody = avatarRotationInverse * palmRotation;
|
||||
});
|
||||
|
||||
activateBody(true);
|
||||
}
|
||||
|
||||
std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) {
|
||||
|
@ -197,7 +199,6 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
|||
if (_kinematic) {
|
||||
doKinematicUpdate(deltaTimeStep);
|
||||
} else {
|
||||
activateBody();
|
||||
forceBodyNonStatic();
|
||||
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
||||
}
|
||||
|
@ -247,7 +248,6 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) {
|
|||
_previousSet = true;
|
||||
});
|
||||
|
||||
activateBody();
|
||||
forceBodyNonStatic();
|
||||
}
|
||||
|
||||
|
@ -344,7 +344,6 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
ownerEntity->setActionDataNeedsTransmit(true);
|
||||
}
|
||||
});
|
||||
activateBody();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -431,6 +430,5 @@ void AvatarActionHold::deserialize(QByteArray serializedArguments) {
|
|||
_active = true;
|
||||
});
|
||||
|
||||
activateBody();
|
||||
forceBodyNonStatic();
|
||||
}
|
||||
|
|
|
@ -76,11 +76,8 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
packetReceiver.registerListener(PacketType::AvatarBillboard, this, "processAvatarBillboardPacket");
|
||||
}
|
||||
|
||||
const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters
|
||||
Setting::Handle<float> avatarRenderDistanceInverseHighLimit("avatarRenderDistanceHighLimit", 1.0f / SMALLEST_REASONABLE_HORIZON);
|
||||
void AvatarManager::setRenderDistanceInverseHighLimit(float newValue) {
|
||||
avatarRenderDistanceInverseHighLimit.set(newValue);
|
||||
_renderDistanceController.setControlledValueHighLimit(newValue);
|
||||
AvatarManager::~AvatarManager() {
|
||||
_myAvatar->die();
|
||||
}
|
||||
|
||||
void AvatarManager::init() {
|
||||
|
@ -98,19 +95,6 @@ void AvatarManager::init() {
|
|||
_myAvatar->addToScene(_myAvatar, scene, pendingChanges);
|
||||
}
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
const float target_fps = qApp->getTargetFrameRate();
|
||||
_renderDistanceController.setMeasuredValueSetpoint(target_fps);
|
||||
_renderDistanceController.setControlledValueHighLimit(avatarRenderDistanceInverseHighLimit.get());
|
||||
_renderDistanceController.setControlledValueLowLimit(1.0f / (float) TREE_SCALE);
|
||||
// Advice for tuning parameters:
|
||||
// See PIDController.h. There's a section on tuning in the reference.
|
||||
// Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300))
|
||||
//_renderDistanceController.setHistorySize("avatar render", target_fps * 4);
|
||||
// Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on.
|
||||
_renderDistanceController.setKP(0.0008f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0.
|
||||
_renderDistanceController.setKI(0.0006f); // Big enough to bring us to target with the above KP.
|
||||
_renderDistanceController.setKD(0.000001f); // A touch of kd increases the speed by which we get there.
|
||||
}
|
||||
|
||||
void AvatarManager::updateMyAvatar(float deltaTime) {
|
||||
|
@ -133,38 +117,21 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||
// lock the hash for read to check the size
|
||||
QReadLocker lock(&_hashLock);
|
||||
|
||||
|
||||
if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
lock.unlock();
|
||||
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
|
||||
|
||||
PerformanceTimer perfTimer("otherAvatars");
|
||||
|
||||
float distance;
|
||||
if (!qApp->isThrottleRendering()) {
|
||||
_renderDistanceController.setMeasuredValueSetpoint(qApp->getTargetFrameRate()); // No problem updating in flight.
|
||||
// The PID controller raises the controlled value when the measured value goes up.
|
||||
// The measured value is frame rate. When the controlled value (1 / render cutoff distance)
|
||||
// goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate
|
||||
// goes up.
|
||||
const float deduced = qApp->getLastUnsynchronizedFps();
|
||||
distance = 1.0f / _renderDistanceController.update(deduced, deltaTime);
|
||||
} else {
|
||||
// Here we choose to just use the maximum render cutoff distance if throttled.
|
||||
distance = 1.0f / _renderDistanceController.getControlledValueLowLimit();
|
||||
}
|
||||
_renderDistanceAverage.updateAverage(distance);
|
||||
_renderDistance = _renderDistanceAverage.getAverage();
|
||||
int renderableCount = 0;
|
||||
|
||||
// simulate avatars
|
||||
auto hashCopy = getHashCopy();
|
||||
|
||||
|
||||
AvatarHash::iterator avatarIterator = hashCopy.begin();
|
||||
while (avatarIterator != hashCopy.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
|
||||
|
@ -179,14 +146,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
} else {
|
||||
avatar->startUpdate();
|
||||
avatar->simulate(deltaTime);
|
||||
if (avatar->getShouldRender()) {
|
||||
renderableCount++;
|
||||
}
|
||||
avatar->endUpdate();
|
||||
++avatarIterator;
|
||||
}
|
||||
}
|
||||
_renderedAvatarCount = renderableCount;
|
||||
|
||||
// simulate avatar fades
|
||||
simulateAvatarFades(deltaTime);
|
||||
|
@ -206,7 +169,12 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE);
|
||||
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
|
||||
avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
|
||||
fadingIterator = _avatarFades.erase(fadingIterator);
|
||||
// only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine
|
||||
if (_motionStatesToRemoveFromPhysics.empty()) {
|
||||
fadingIterator = _avatarFades.erase(fadingIterator);
|
||||
} else {
|
||||
++fadingIterator;
|
||||
}
|
||||
} else {
|
||||
avatar->simulate(deltaTime);
|
||||
++fadingIterator;
|
||||
|
@ -234,20 +202,6 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe
|
|||
return newAvatar;
|
||||
}
|
||||
|
||||
// protected
|
||||
void AvatarManager::removeAvatarMotionState(AvatarSharedPointer avatar) {
|
||||
auto rawPointer = std::static_pointer_cast<Avatar>(avatar);
|
||||
AvatarMotionState* motionState = rawPointer->getMotionState();
|
||||
if (motionState) {
|
||||
// clean up physics stuff
|
||||
motionState->clearObjectBackPointer();
|
||||
rawPointer->setMotionState(nullptr);
|
||||
_avatarMotionStates.remove(motionState);
|
||||
_motionStatesToAdd.remove(motionState);
|
||||
_motionStatesToDelete.push_back(motionState);
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
||||
QWriteLocker locker(&_hashLock);
|
||||
|
@ -261,8 +215,18 @@ void AvatarManager::removeAvatar(const QUuid& sessionUUID) {
|
|||
void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar) {
|
||||
AvatarHashMap::handleRemovedAvatar(removedAvatar);
|
||||
|
||||
removedAvatar->die();
|
||||
removeAvatarMotionState(removedAvatar);
|
||||
// removedAvatar is a shared pointer to an AvatarData but we need to get to the derived Avatar
|
||||
// class in this context so we can call methods that don't exist at the base class.
|
||||
Avatar* avatar = static_cast<Avatar*>(removedAvatar.get());
|
||||
avatar->die();
|
||||
|
||||
AvatarMotionState* motionState = avatar->getMotionState();
|
||||
if (motionState) {
|
||||
_motionStatesThatMightUpdate.remove(motionState);
|
||||
_motionStatesToAddToPhysics.remove(motionState);
|
||||
_motionStatesToRemoveFromPhysics.push_back(motionState);
|
||||
}
|
||||
|
||||
_avatarFades.push_back(removedAvatar);
|
||||
}
|
||||
|
||||
|
@ -315,22 +279,22 @@ AvatarData* AvatarManager::getAvatar(QUuid avatarID) {
|
|||
}
|
||||
|
||||
|
||||
void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) {
|
||||
void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
result.swap(_motionStatesToDelete);
|
||||
result.swap(_motionStatesToRemoveFromPhysics);
|
||||
}
|
||||
|
||||
void AvatarManager::getObjectsToAdd(VectorOfMotionStates& result) {
|
||||
void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
for (auto motionState : _motionStatesToAdd) {
|
||||
for (auto motionState : _motionStatesToAddToPhysics) {
|
||||
result.push_back(motionState);
|
||||
}
|
||||
_motionStatesToAdd.clear();
|
||||
_motionStatesToAddToPhysics.clear();
|
||||
}
|
||||
|
||||
void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) {
|
||||
result.clear();
|
||||
for (auto state : _avatarMotionStates) {
|
||||
for (auto state : _motionStatesThatMightUpdate) {
|
||||
if (state->_dirtyFlags > 0) {
|
||||
result.push_back(state);
|
||||
}
|
||||
|
@ -385,8 +349,8 @@ void AvatarManager::addAvatarToSimulation(Avatar* avatar) {
|
|||
// we don't add to the simulation now, we put it on a list to be added later
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||
avatar->setMotionState(motionState);
|
||||
_motionStatesToAdd.insert(motionState);
|
||||
_avatarMotionStates.insert(motionState);
|
||||
_motionStatesToAddToPhysics.insert(motionState);
|
||||
_motionStatesThatMightUpdate.insert(motionState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ public:
|
|||
/// Registers the script types associated with the avatar manager.
|
||||
static void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
virtual ~AvatarManager();
|
||||
|
||||
void init();
|
||||
|
||||
MyAvatar* getMyAvatar() { return _myAvatar.get(); }
|
||||
|
@ -41,11 +43,10 @@ public:
|
|||
|
||||
void updateMyAvatar(float deltaTime);
|
||||
void updateOtherAvatars(float deltaTime);
|
||||
|
||||
|
||||
void clearOtherAvatars();
|
||||
|
||||
|
||||
bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; }
|
||||
PIDController& getRenderDistanceController() { return _renderDistanceController; }
|
||||
|
||||
class LocalLight {
|
||||
public:
|
||||
|
@ -60,27 +61,14 @@ public:
|
|||
Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID);
|
||||
|
||||
|
||||
void getObjectsToDelete(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToAdd(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates);
|
||||
void getObjectsToChange(VectorOfMotionStates& motionStates);
|
||||
void handleOutgoingChanges(const VectorOfMotionStates& motionStates);
|
||||
void handleCollisionEvents(const CollisionEvents& collisionEvents);
|
||||
|
||||
void addAvatarToSimulation(Avatar* avatar);
|
||||
|
||||
// Expose results and parameter-tuning operations to other systems, such as stats and javascript.
|
||||
Q_INVOKABLE float getRenderDistance() { return _renderDistance; }
|
||||
Q_INVOKABLE float getRenderDistanceInverseLowLimit() { return _renderDistanceController.getControlledValueLowLimit(); }
|
||||
Q_INVOKABLE float getRenderDistanceInverseHighLimit() { return _renderDistanceController.getControlledValueHighLimit(); }
|
||||
Q_INVOKABLE int getNumberInRenderRange() { return _renderedAvatarCount; }
|
||||
Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); }
|
||||
Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); }
|
||||
Q_INVOKABLE void setRenderDistanceKP(float newValue) { _renderDistanceController.setKP(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceKI(float newValue) { _renderDistanceController.setKI(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceInverseLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceInverseHighLimit(float newValue);
|
||||
|
||||
public slots:
|
||||
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
||||
void updateAvatarRenderStatus(bool shouldRenderAvatars);
|
||||
|
@ -94,7 +82,6 @@ private:
|
|||
// virtual overrides
|
||||
virtual AvatarSharedPointer newSharedAvatar();
|
||||
virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer);
|
||||
void removeAvatarMotionState(AvatarSharedPointer avatar);
|
||||
|
||||
virtual void removeAvatar(const QUuid& sessionUUID);
|
||||
virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar);
|
||||
|
@ -106,14 +93,10 @@ private:
|
|||
QVector<AvatarManager::LocalLight> _localLights;
|
||||
|
||||
bool _shouldShowReceiveStats = false;
|
||||
float _renderDistance { (float) TREE_SCALE };
|
||||
int _renderedAvatarCount { 0 };
|
||||
PIDController _renderDistanceController { };
|
||||
SimpleMovingAverage _renderDistanceAverage { 10 };
|
||||
|
||||
SetOfAvatarMotionStates _avatarMotionStates;
|
||||
SetOfMotionStates _motionStatesToAdd;
|
||||
VectorOfMotionStates _motionStatesToDelete;
|
||||
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
|
||||
SetOfMotionStates _motionStatesToAddToPhysics;
|
||||
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(AvatarManager::LocalLight)
|
||||
|
|
|
@ -25,20 +25,17 @@ AvatarMotionState::AvatarMotionState(Avatar* avatar, btCollisionShape* shape) :
|
|||
}
|
||||
|
||||
AvatarMotionState::~AvatarMotionState() {
|
||||
assert(_avatar);
|
||||
_avatar = nullptr;
|
||||
}
|
||||
|
||||
// virtual
|
||||
uint32_t AvatarMotionState::getIncomingDirtyFlags() {
|
||||
uint32_t dirtyFlags = 0;
|
||||
if (_body && _avatar) {
|
||||
dirtyFlags = _dirtyFlags;
|
||||
}
|
||||
return dirtyFlags;
|
||||
return _body ? _dirtyFlags : 0;
|
||||
}
|
||||
|
||||
void AvatarMotionState::clearIncomingDirtyFlags() {
|
||||
if (_body && _avatar) {
|
||||
if (_body) {
|
||||
_dirtyFlags = 0;
|
||||
}
|
||||
}
|
||||
|
@ -50,12 +47,9 @@ MotionType AvatarMotionState::computeObjectMotionType() const {
|
|||
|
||||
// virtual and protected
|
||||
btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||
if (_avatar) {
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
return nullptr;
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
@ -65,9 +59,6 @@ bool AvatarMotionState::isMoving() const {
|
|||
|
||||
// virtual
|
||||
void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||
if (!_avatar) {
|
||||
return;
|
||||
}
|
||||
worldTrans.setOrigin(glmToBullet(getObjectPosition()));
|
||||
worldTrans.setRotation(glmToBullet(getObjectRotation()));
|
||||
if (_body) {
|
||||
|
@ -76,11 +67,8 @@ void AvatarMotionState::getWorldTransform(btTransform& worldTrans) const {
|
|||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
// virtual
|
||||
void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||
if (!_avatar) {
|
||||
return;
|
||||
}
|
||||
// HACK: The PhysicsEngine does not actually move OTHER avatars -- instead it slaves their local RigidBody to the transform
|
||||
// as specified by a remote simulation. However, to give the remote simulation time to respond to our own objects we tie
|
||||
// the other avatar's body to its true position with a simple spring. This is a HACK that will have to be improved later.
|
||||
|
@ -154,15 +142,8 @@ QUuid AvatarMotionState::getSimulatorID() const {
|
|||
return _avatar->getSessionUUID();
|
||||
}
|
||||
|
||||
// virtual
|
||||
int16_t AvatarMotionState::computeCollisionGroup() {
|
||||
// virtual
|
||||
int16_t AvatarMotionState::computeCollisionGroup() const {
|
||||
return COLLISION_GROUP_OTHER_AVATAR;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void AvatarMotionState::clearObjectBackPointer() {
|
||||
ObjectMotionState::clearObjectBackPointer();
|
||||
_avatar = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -21,55 +21,65 @@ class Avatar;
|
|||
class AvatarMotionState : public ObjectMotionState {
|
||||
public:
|
||||
AvatarMotionState(Avatar* avatar, btCollisionShape* shape);
|
||||
~AvatarMotionState();
|
||||
|
||||
virtual MotionType getMotionType() const { return _motionType; }
|
||||
virtual MotionType getMotionType() const override { return _motionType; }
|
||||
|
||||
virtual uint32_t getIncomingDirtyFlags();
|
||||
virtual void clearIncomingDirtyFlags();
|
||||
virtual uint32_t getIncomingDirtyFlags() override;
|
||||
virtual void clearIncomingDirtyFlags() override;
|
||||
|
||||
virtual MotionType computeObjectMotionType() const;
|
||||
virtual MotionType computeObjectMotionType() const override;
|
||||
|
||||
virtual bool isMoving() const;
|
||||
virtual bool isMoving() const override;
|
||||
|
||||
// this relays incoming position/rotation to the RigidBody
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const;
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const override;
|
||||
|
||||
// this relays outgoing position/rotation to the EntityItem
|
||||
virtual void setWorldTransform(const btTransform& worldTrans);
|
||||
virtual void setWorldTransform(const btTransform& worldTrans) override;
|
||||
|
||||
|
||||
// These pure virtual methods must be implemented for each MotionState type
|
||||
// and make it possible to implement more complicated methods in this base class.
|
||||
|
||||
virtual float getObjectRestitution() const;
|
||||
virtual float getObjectFriction() const;
|
||||
virtual float getObjectLinearDamping() const;
|
||||
virtual float getObjectAngularDamping() const;
|
||||
// pure virtual overrides from ObjectMotionState
|
||||
virtual float getObjectRestitution() const override;
|
||||
virtual float getObjectFriction() const override;
|
||||
virtual float getObjectLinearDamping() const override;
|
||||
virtual float getObjectAngularDamping() const override;
|
||||
|
||||
virtual glm::vec3 getObjectPosition() const;
|
||||
virtual glm::quat getObjectRotation() const;
|
||||
virtual glm::vec3 getObjectLinearVelocity() const;
|
||||
virtual glm::vec3 getObjectAngularVelocity() const;
|
||||
virtual glm::vec3 getObjectGravity() const;
|
||||
virtual glm::vec3 getObjectPosition() const override;
|
||||
virtual glm::quat getObjectRotation() const override;
|
||||
virtual glm::vec3 getObjectLinearVelocity() const override;
|
||||
virtual glm::vec3 getObjectAngularVelocity() const override;
|
||||
virtual glm::vec3 getObjectGravity() const override;
|
||||
|
||||
virtual const QUuid& getObjectID() const;
|
||||
virtual const QUuid& getObjectID() const override;
|
||||
|
||||
virtual QUuid getSimulatorID() const;
|
||||
virtual QUuid getSimulatorID() const override;
|
||||
|
||||
void setBoundingBox(const glm::vec3& corner, const glm::vec3& diagonal);
|
||||
|
||||
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; }
|
||||
|
||||
virtual int16_t computeCollisionGroup();
|
||||
virtual int16_t computeCollisionGroup() const override;
|
||||
|
||||
friend class AvatarManager;
|
||||
friend class Avatar;
|
||||
|
||||
protected:
|
||||
virtual bool isReadyToComputeShape() { return true; }
|
||||
// the dtor had been made protected to force the compiler to verify that it is only
|
||||
// ever called by the Avatar class dtor.
|
||||
~AvatarMotionState();
|
||||
|
||||
virtual bool isReadyToComputeShape() const override { return true; }
|
||||
virtual btCollisionShape* computeNewShape();
|
||||
virtual void clearObjectBackPointer();
|
||||
Avatar* _avatar;
|
||||
|
||||
// The AvatarMotionState keeps a RAW backpointer to its Avatar because all AvatarMotionState
|
||||
// instances are "owned" by their corresponding Avatar instance and are deleted in the Avatar dtor.
|
||||
// In other words, it is impossible for the Avatar to be deleted out from under its MotionState.
|
||||
// In conclusion: weak pointer shennanigans would be pure overhead.
|
||||
Avatar* _avatar; // do NOT use smartpointer here, no need for weakpointer
|
||||
|
||||
uint32_t _dirtyFlags;
|
||||
};
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ const float MIN_AVATAR_SPEED = 0.05f; // speed is set to zero below this
|
|||
|
||||
// TODO: normalize avatar speed for standard avatar size, then scale all motion logic
|
||||
// to properly follow avatar size.
|
||||
float MAX_AVATAR_SPEED = 300.0f;
|
||||
float MAX_AVATAR_SPEED = 30.0f;
|
||||
float MAX_KEYBOARD_MOTOR_SPEED = MAX_AVATAR_SPEED;
|
||||
float DEFAULT_KEYBOARD_MOTOR_TIMESCALE = 0.25f;
|
||||
float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;
|
||||
|
@ -201,6 +201,11 @@ MyAvatar::~MyAvatar() {
|
|||
_lookAtTargetAvatar.reset();
|
||||
}
|
||||
|
||||
// virtual
|
||||
void MyAvatar::simulateAttachments(float deltaTime) {
|
||||
// don't update attachments here, do it in harvestResultsFromPhysicsSimulation()
|
||||
}
|
||||
|
||||
QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
|
||||
CameraMode mode = qApp->getCamera()->getMode();
|
||||
_globalPosition = getPosition();
|
||||
|
@ -412,6 +417,8 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
|||
// position when driven from the head.
|
||||
glm::mat4 desiredMat = createMatFromQuatAndPos(getOrientation(), getPosition());
|
||||
_sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix);
|
||||
|
||||
lateUpdatePalms();
|
||||
}
|
||||
|
||||
// Update avatar head rotation with sensor data
|
||||
|
@ -621,6 +628,7 @@ void MyAvatar::saveData() {
|
|||
settings.setValue("rotation_y", eulers.y);
|
||||
settings.setValue("rotation_z", eulers.z);
|
||||
settings.setValue("scale", attachment.scale);
|
||||
settings.setValue("isSoft", attachment.isSoft);
|
||||
}
|
||||
settings.endArray();
|
||||
|
||||
|
@ -702,6 +710,7 @@ void MyAvatar::loadData() {
|
|||
eulers.z = loadSetting(settings, "rotation_z", 0.0f);
|
||||
attachment.rotation = glm::quat(eulers);
|
||||
attachment.scale = loadSetting(settings, "scale", 1.0f);
|
||||
attachment.isSoft = settings.value("isSoft").toBool();
|
||||
attachmentData.append(attachment);
|
||||
}
|
||||
settings.endArray();
|
||||
|
@ -1057,7 +1066,7 @@ void MyAvatar::prepareForPhysicsSimulation() {
|
|||
_characterController.setFollowVelocity(_followVelocity);
|
||||
}
|
||||
|
||||
void MyAvatar::harvestResultsFromPhysicsSimulation() {
|
||||
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaType) {
|
||||
glm::vec3 position = getPosition();
|
||||
glm::quat orientation = getOrientation();
|
||||
_characterController.getPositionAndOrientation(position, orientation);
|
||||
|
@ -1068,6 +1077,9 @@ void MyAvatar::harvestResultsFromPhysicsSimulation() {
|
|||
} else {
|
||||
setVelocity(_characterController.getLinearVelocity());
|
||||
}
|
||||
|
||||
// now that physics has adjusted our position, we can update attachements.
|
||||
Avatar::simulateAttachments(deltaType);
|
||||
}
|
||||
|
||||
void MyAvatar::adjustSensorTransform() {
|
||||
|
@ -1599,7 +1611,7 @@ void MyAvatar::maybeUpdateBillboard() {
|
|||
if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) {
|
||||
return;
|
||||
}
|
||||
foreach (Model* model, _attachmentModels) {
|
||||
for (auto& model : _attachmentModels) {
|
||||
if (!model->isLoadedWithTextures()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1829,3 +1841,8 @@ QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioList
|
|||
void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode) {
|
||||
audioListenerMode = (AudioListenerMode)object.toUInt16();
|
||||
}
|
||||
|
||||
|
||||
void MyAvatar::lateUpdatePalms() {
|
||||
Avatar::updatePalms();
|
||||
}
|
||||
|
|
|
@ -83,6 +83,8 @@ public:
|
|||
MyAvatar(RigPointer rig);
|
||||
~MyAvatar();
|
||||
|
||||
virtual void simulateAttachments(float deltaTime) override;
|
||||
|
||||
AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; }
|
||||
AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; }
|
||||
AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; }
|
||||
|
@ -204,7 +206,7 @@ public:
|
|||
MyCharacterController* getCharacterController() { return &_characterController; }
|
||||
|
||||
void prepareForPhysicsSimulation();
|
||||
void harvestResultsFromPhysicsSimulation();
|
||||
void harvestResultsFromPhysicsSimulation(float deltaTime);
|
||||
void adjustSensorTransform();
|
||||
|
||||
const QString& getCollisionSoundURL() { return _collisionSoundURL; }
|
||||
|
@ -309,12 +311,14 @@ private:
|
|||
|
||||
void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity);
|
||||
|
||||
PalmData getActivePalmData(int palmIndex) const;
|
||||
|
||||
// derive avatar body position and orientation from the current HMD Sensor location.
|
||||
// results are in HMD frame
|
||||
glm::mat4 deriveBodyFromHMDSensor() const;
|
||||
|
||||
virtual void updatePalms() override {}
|
||||
void lateUpdatePalms();
|
||||
|
||||
|
||||
float _driveKeys[MAX_DRIVE_KEYS];
|
||||
bool _wasPushing;
|
||||
bool _isPushing;
|
||||
|
|
84
interface/src/avatar/SoftAttachmentModel.cpp
Normal file
84
interface/src/avatar/SoftAttachmentModel.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// SoftAttachmentModel.cpp
|
||||
// interface/src/avatar
|
||||
//
|
||||
// Created by Anthony J. Thibault on 12/17/15.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "SoftAttachmentModel.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) :
|
||||
Model(rig, parent),
|
||||
_rigOverride(rigOverride) {
|
||||
assert(_rig);
|
||||
assert(_rigOverride);
|
||||
}
|
||||
|
||||
SoftAttachmentModel::~SoftAttachmentModel() {
|
||||
}
|
||||
|
||||
// virtual
|
||||
void SoftAttachmentModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||
_needsUpdateClusterMatrices = true;
|
||||
}
|
||||
|
||||
int SoftAttachmentModel::getJointIndexOverride(int i) const {
|
||||
QString name = _rig->nameOfJoint(i);
|
||||
if (name.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
return _rigOverride->indexOfJoint(name);
|
||||
}
|
||||
|
||||
// virtual
|
||||
// use the _rigOverride matrices instead of the Model::_rig
|
||||
void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) {
|
||||
if (!_needsUpdateClusterMatrices) {
|
||||
return;
|
||||
}
|
||||
_needsUpdateClusterMatrices = false;
|
||||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
|
||||
glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation);
|
||||
for (int i = 0; i < _meshStates.size(); i++) {
|
||||
MeshState& state = _meshStates[i];
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
|
||||
for (int j = 0; j < mesh.clusters.size(); j++) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(j);
|
||||
|
||||
// TODO: cache these look ups as an optimization
|
||||
int jointIndexOverride = getJointIndexOverride(cluster.jointIndex);
|
||||
glm::mat4 jointMatrix(glm::mat4::_null);
|
||||
if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride->getJointStateCount()) {
|
||||
jointMatrix = _rigOverride->getJointTransform(jointIndexOverride);
|
||||
} else {
|
||||
jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
||||
}
|
||||
state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
|
||||
}
|
||||
|
||||
// Once computed the cluster matrices, update the buffer(s)
|
||||
if (mesh.clusters.size() > 1) {
|
||||
if (!state.clusterBuffer) {
|
||||
state.clusterBuffer = std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4),
|
||||
(const gpu::Byte*) state.clusterMatrices.constData());
|
||||
} else {
|
||||
state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4),
|
||||
(const gpu::Byte*) state.clusterMatrices.constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// post the blender if we're not currently waiting for one to finish
|
||||
if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||
DependencyManager::get<ModelBlender>()->noteRequiresBlend(this);
|
||||
}
|
||||
}
|
42
interface/src/avatar/SoftAttachmentModel.h
Normal file
42
interface/src/avatar/SoftAttachmentModel.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// SoftAttachmentModel.h
|
||||
// interface/src/avatar
|
||||
//
|
||||
// Created by Anthony J. Thibault on 12/17/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
|
||||
//
|
||||
|
||||
#ifndef hifi_SoftAttachmentModel_h
|
||||
#define hifi_SoftAttachmentModel_h
|
||||
|
||||
#include <Model.h>
|
||||
|
||||
// A model that allows the creator to specify a secondary rig instance.
|
||||
// When the cluster matrices are created for rendering, the
|
||||
// cluster matrices will use the secondary rig for the joint poses
|
||||
// instead of the primary rig.
|
||||
//
|
||||
// This is used by Avatar instances to wear clothing that follows the same
|
||||
// animated pose as the SkeletonModel.
|
||||
|
||||
class SoftAttachmentModel : public Model {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride);
|
||||
~SoftAttachmentModel();
|
||||
|
||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override;
|
||||
virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) override;
|
||||
|
||||
protected:
|
||||
int getJointIndexOverride(int i) const;
|
||||
|
||||
RigPointer _rigOverride;
|
||||
};
|
||||
|
||||
#endif // hifi_SoftAttachmentModel_h
|
|
@ -139,7 +139,7 @@ void EyeTracker::onStreamStarted() {
|
|||
qCWarning(interfaceapp) << "Eye Tracker: Error starting streaming:" << smiReturnValueToString(result);
|
||||
// Display error dialog unless SMI SDK has already displayed an error message.
|
||||
if (result != SMI_ERROR_HMD_NOT_SUPPORTED) {
|
||||
QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result));
|
||||
OffscreenUi::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result));
|
||||
}
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Eye Tracker: Started streaming";
|
||||
|
@ -152,7 +152,7 @@ void EyeTracker::onStreamStarted() {
|
|||
result = smi_loadCalibration(HIGH_FIDELITY_EYE_TRACKER_CALIBRATION);
|
||||
if (result != SMI_RET_SUCCESS) {
|
||||
qCWarning(interfaceapp) << "Eye Tracker: Error loading calibration:" << smiReturnValueToString(result);
|
||||
QMessageBox::warning(nullptr, "Eye Tracker Error", "Error loading calibration"
|
||||
OffscreenUi::warning(nullptr, "Eye Tracker Error", "Error loading calibration"
|
||||
+ smiReturnValueToString(result));
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "Eye Tracker: Loaded calibration";
|
||||
|
@ -168,7 +168,7 @@ void EyeTracker::setEnabled(bool enabled, bool simulate) {
|
|||
int result = smi_setCallback(eyeTrackerCallback);
|
||||
if (result != SMI_RET_SUCCESS) {
|
||||
qCWarning(interfaceapp) << "Eye Tracker: Error setting callback:" << smiReturnValueToString(result);
|
||||
QMessageBox::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result));
|
||||
OffscreenUi::warning(nullptr, "Eye Tracker Error", smiReturnValueToString(result));
|
||||
} else {
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ void EyeTracker::calibrate(int points) {
|
|||
}
|
||||
|
||||
if (result != SMI_RET_SUCCESS) {
|
||||
QMessageBox::warning(nullptr, "Eye Tracker Error", "Calibration error: " + smiReturnValueToString(result));
|
||||
OffscreenUi::warning(nullptr, "Eye Tracker Error", "Calibration error: " + smiReturnValueToString(result));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -17,7 +17,6 @@ AccountScriptingInterface::AccountScriptingInterface() {
|
|||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
connect(&accountManager, &AccountManager::balanceChanged, this,
|
||||
&AccountScriptingInterface::updateBalance);
|
||||
|
||||
}
|
||||
|
||||
AccountScriptingInterface* AccountScriptingInterface::getInstance() {
|
||||
|
@ -39,3 +38,12 @@ void AccountScriptingInterface::updateBalance() {
|
|||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
emit balanceChanged(accountManager.getAccountInfo().getBalanceInSatoshis());
|
||||
}
|
||||
|
||||
QString AccountScriptingInterface::getUsername() {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
if (accountManager.isLoggedIn()) {
|
||||
return accountManager.getAccountInfo().getUsername();
|
||||
} else {
|
||||
return "Unknown user";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ signals:
|
|||
public slots:
|
||||
static AccountScriptingInterface* getInstance();
|
||||
float getBalance();
|
||||
QString getUsername();
|
||||
bool isLoggedIn();
|
||||
void updateBalance();
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "DomainHandler.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Menu.h"
|
||||
#include "OffscreenUi.h"
|
||||
#include "ui/ModelsBrowser.h"
|
||||
|
||||
#include "WindowScriptingInterface.h"
|
||||
|
@ -153,12 +154,11 @@ QScriptValue WindowScriptingInterface::peekNonBlockingFormResult(QScriptValue fo
|
|||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
/// Display an alert box
|
||||
/// \param const QString& message message to display
|
||||
/// \return QScriptValue::UndefinedValue
|
||||
QScriptValue WindowScriptingInterface::showAlert(const QString& message) {
|
||||
QMessageBox::warning(qApp->getWindow(), "", message);
|
||||
OffscreenUi::warning("", message);
|
||||
return QScriptValue::UndefinedValue;
|
||||
}
|
||||
|
||||
|
@ -166,8 +166,11 @@ QScriptValue WindowScriptingInterface::showAlert(const QString& message) {
|
|||
/// \param const QString& message message to display
|
||||
/// \return QScriptValue `true` if 'Yes' was clicked, `false` otherwise
|
||||
QScriptValue WindowScriptingInterface::showConfirm(const QString& message) {
|
||||
QMessageBox::StandardButton response = QMessageBox::question(qApp->getWindow(), "", message);
|
||||
return QScriptValue(response == QMessageBox::Yes);
|
||||
bool confirm = false;
|
||||
OffscreenUi::question("", message, [&](QMessageBox::StandardButton response){
|
||||
confirm = (response == QMessageBox::Yes);
|
||||
});
|
||||
return QScriptValue(confirm);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::chooseDirectory() {
|
||||
|
@ -185,7 +188,7 @@ void WindowScriptingInterface::chooseDirectory() {
|
|||
}
|
||||
|
||||
if (!validateAs.exactMatch(directory)) {
|
||||
QMessageBox::warning(NULL, "Invalid Directory", errorMessage);
|
||||
OffscreenUi::warning(NULL, "Invalid Directory", errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -597,7 +600,6 @@ QScriptValue WindowScriptingInterface::showPrompt(const QString& message, const
|
|||
if (promptDialog.exec() == QDialog::Accepted) {
|
||||
return QScriptValue(promptDialog.textValue());
|
||||
}
|
||||
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) {
|
|||
|
||||
// Now render the overlay components together into a single texture
|
||||
renderDomainConnectionStatusBorder(renderArgs); // renders the connected domain line
|
||||
renderAudioScope(renderArgs); // audio scope in the very back
|
||||
renderAudioScope(renderArgs); // audio scope in the very back - NOTE: this is the debug audio scope, not the VU meter
|
||||
renderRearView(renderArgs); // renders the mirror view selfie
|
||||
renderQmlUi(renderArgs); // renders a unit quad with the QML UI texture, and the text overlays from scripts
|
||||
renderOverlays(renderArgs); // renders Scripts Overlay and AudioScope
|
||||
|
@ -158,7 +158,7 @@ void ApplicationOverlay::renderRearViewToFbo(RenderArgs* renderArgs) {
|
|||
}
|
||||
|
||||
void ApplicationOverlay::renderRearView(RenderArgs* renderArgs) {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
if (!qApp->isHMDMode() && Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) {
|
||||
gpu::Batch& batch = *renderArgs->_batch;
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <AssetUpload.h>
|
||||
#include <AssetUtils.h>
|
||||
#include <NodeList.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <ResourceManager.h>
|
||||
|
||||
AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() {
|
||||
|
@ -146,5 +147,5 @@ void AssetUploadDialogFactory::showErrorDialog(AssetUpload* upload, QWidget* dia
|
|||
|
||||
dialogMessage += errorMessage;
|
||||
|
||||
QMessageBox::warning(dialogParent, "Failed Upload", dialogMessage);
|
||||
OffscreenUi::warning(dialogParent, "Failed Upload", dialogMessage);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <avatar/MyAvatar.h>
|
||||
|
@ -27,13 +28,13 @@
|
|||
|
||||
AttachmentsDialog::AttachmentsDialog(QWidget* parent) :
|
||||
QDialog(parent) {
|
||||
|
||||
|
||||
setWindowTitle("Edit Attachments");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
setLayout(layout);
|
||||
|
||||
|
||||
QScrollArea* area = new QScrollArea();
|
||||
layout->addWidget(area);
|
||||
area->setWidgetResizable(true);
|
||||
|
@ -42,26 +43,26 @@ AttachmentsDialog::AttachmentsDialog(QWidget* parent) :
|
|||
container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
||||
area->setWidget(container);
|
||||
_attachments->addStretch(1);
|
||||
|
||||
|
||||
foreach (const AttachmentData& data, DependencyManager::get<AvatarManager>()->getMyAvatar()->getAttachmentData()) {
|
||||
addAttachment(data);
|
||||
}
|
||||
|
||||
|
||||
QPushButton* newAttachment = new QPushButton("New Attachment");
|
||||
connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment()));
|
||||
layout->addWidget(newAttachment);
|
||||
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||
layout->addWidget(buttons);
|
||||
connect(buttons, SIGNAL(accepted()), SLOT(deleteLater()));
|
||||
_ok = buttons->button(QDialogButtonBox::Ok);
|
||||
|
||||
|
||||
setMinimumSize(600, 600);
|
||||
}
|
||||
|
||||
void AttachmentsDialog::setVisible(bool visible) {
|
||||
QDialog::setVisible(visible);
|
||||
|
||||
|
||||
// un-default the OK button
|
||||
if (visible) {
|
||||
_ok->setDefault(false);
|
||||
|
@ -104,11 +105,11 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
|
|||
_dialog(dialog),
|
||||
_applying(false) {
|
||||
setFrameStyle(QFrame::StyledPanel);
|
||||
|
||||
|
||||
QFormLayout* layout = new QFormLayout();
|
||||
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||
setLayout(layout);
|
||||
|
||||
|
||||
QHBoxLayout* urlBox = new QHBoxLayout();
|
||||
layout->addRow("Model URL:", urlBox);
|
||||
urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1);
|
||||
|
@ -117,7 +118,7 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
|
|||
QPushButton* chooseURL = new QPushButton("Choose");
|
||||
urlBox->addWidget(chooseURL);
|
||||
connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL()));
|
||||
|
||||
|
||||
layout->addRow("Joint:", _jointName = new QComboBox());
|
||||
QSharedPointer<NetworkGeometry> geometry = DependencyManager::get<AvatarManager>()->getMyAvatar()->getSkeletonModel().getGeometry();
|
||||
if (geometry && geometry->isLoaded()) {
|
||||
|
@ -127,26 +128,30 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
|
|||
}
|
||||
_jointName->setCurrentText(data.jointName);
|
||||
connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(jointNameChanged()));
|
||||
|
||||
|
||||
QHBoxLayout* translationBox = new QHBoxLayout();
|
||||
translationBox->addWidget(_translationX = createTranslationBox(this, data.translation.x));
|
||||
translationBox->addWidget(_translationY = createTranslationBox(this, data.translation.y));
|
||||
translationBox->addWidget(_translationZ = createTranslationBox(this, data.translation.z));
|
||||
layout->addRow("Translation:", translationBox);
|
||||
|
||||
|
||||
QHBoxLayout* rotationBox = new QHBoxLayout();
|
||||
glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation));
|
||||
rotationBox->addWidget(_rotationX = createRotationBox(this, eulers.x));
|
||||
rotationBox->addWidget(_rotationY = createRotationBox(this, eulers.y));
|
||||
rotationBox->addWidget(_rotationZ = createRotationBox(this, eulers.z));
|
||||
layout->addRow("Rotation:", rotationBox);
|
||||
|
||||
|
||||
layout->addRow("Scale:", _scale = new QDoubleSpinBox());
|
||||
_scale->setSingleStep(0.01);
|
||||
_scale->setMaximum(FLT_MAX);
|
||||
_scale->setValue(data.scale);
|
||||
connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
|
||||
|
||||
|
||||
layout->addRow("Is Soft:", _isSoft = new QCheckBox());
|
||||
_isSoft->setChecked(data.isSoft);
|
||||
connect(_isSoft, SIGNAL(stateChanged(int)), SLOT(updateAttachmentData()));
|
||||
|
||||
QPushButton* remove = new QPushButton("Delete");
|
||||
layout->addRow(remove);
|
||||
connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater()));
|
||||
|
@ -160,6 +165,7 @@ AttachmentData AttachmentPanel::getAttachmentData() const {
|
|||
data.translation = glm::vec3(_translationX->value(), _translationY->value(), _translationZ->value());
|
||||
data.rotation = glm::quat(glm::radians(glm::vec3(_rotationX->value(), _rotationY->value(), _rotationZ->value())));
|
||||
data.scale = _scale->value();
|
||||
data.isSoft = _isSoft->isChecked();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -227,6 +233,7 @@ void AttachmentPanel::applyAttachmentData(const AttachmentData& attachment) {
|
|||
_rotationY->setValue(eulers.y);
|
||||
_rotationZ->setValue(eulers.z);
|
||||
_scale->setValue(attachment.scale);
|
||||
_isSoft->setChecked(attachment.isSoft);
|
||||
_applying = false;
|
||||
_dialog->updateAttachmentData();
|
||||
}
|
||||
|
|
|
@ -36,11 +36,11 @@ public slots:
|
|||
void updateAttachmentData();
|
||||
|
||||
private slots:
|
||||
|
||||
|
||||
void addAttachment(const AttachmentData& data = AttachmentData());
|
||||
|
||||
private:
|
||||
|
||||
|
||||
QVBoxLayout* _attachments;
|
||||
QPushButton* _ok;
|
||||
};
|
||||
|
@ -50,7 +50,7 @@ class AttachmentPanel : public QFrame {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
|
||||
AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData());
|
||||
|
||||
AttachmentData getAttachmentData() const;
|
||||
|
@ -64,9 +64,9 @@ private slots:
|
|||
void updateAttachmentData();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
void applyAttachmentData(const AttachmentData& attachment);
|
||||
|
||||
|
||||
AttachmentsDialog* _dialog;
|
||||
QLineEdit* _modelURL;
|
||||
QComboBox* _jointName;
|
||||
|
@ -77,6 +77,7 @@ private:
|
|||
QDoubleSpinBox* _rotationY;
|
||||
QDoubleSpinBox* _rotationZ;
|
||||
QDoubleSpinBox* _scale;
|
||||
QCheckBox* _isSoft;
|
||||
bool _applying;
|
||||
};
|
||||
|
||||
|
|
|
@ -62,10 +62,11 @@ void AvatarInputs::update() {
|
|||
if (!Menu::getInstance()) {
|
||||
return;
|
||||
}
|
||||
AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && !qApp->isHMDMode()
|
||||
AI_UPDATE(mirrorVisible, Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror) && !qApp->isHMDMode()
|
||||
&& !Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror));
|
||||
AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking));
|
||||
AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking));
|
||||
AI_UPDATE(isHMD, qApp->isHMDMode());
|
||||
|
||||
auto audioIO = DependencyManager::get<AudioClient>();
|
||||
const float AUDIO_METER_AVERAGING = 0.5;
|
||||
|
@ -128,7 +129,7 @@ void AvatarInputs::toggleZoom() {
|
|||
}
|
||||
|
||||
void AvatarInputs::closeMirror() {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::Mirror);
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) {
|
||||
Menu::getInstance()->triggerOption(MenuOption::MiniMirror);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ class AvatarInputs : public QQuickItem {
|
|||
AI_PROPERTY(float, audioLevel, 0)
|
||||
AI_PROPERTY(bool, mirrorVisible, false)
|
||||
AI_PROPERTY(bool, mirrorZoomed, true)
|
||||
AI_PROPERTY(bool, isHMD, false)
|
||||
|
||||
public:
|
||||
static AvatarInputs* getInstance();
|
||||
|
@ -44,6 +45,7 @@ signals:
|
|||
void audioLevelChanged();
|
||||
void mirrorVisibleChanged();
|
||||
void mirrorZoomedChanged();
|
||||
void isHMDChanged();
|
||||
|
||||
protected:
|
||||
Q_INVOKABLE void resetSensors();
|
||||
|
|
|
@ -176,20 +176,6 @@ void DialogsManager::showScriptEditor() {
|
|||
_scriptEditor->raise();
|
||||
}
|
||||
|
||||
void DialogsManager::showIRCLink() {
|
||||
if (!_ircInfoBox) {
|
||||
_ircInfoBox = new QMessageBox(QMessageBox::NoIcon,
|
||||
"High Fidelity IRC",
|
||||
"High Fidelity has an IRC channel on irc.freenode.net at #highfidelity.<br/><br/>Web chat is available <a href='http://webchat.freenode.net/?channels=highfidelity&uio=d4'>here</a>.",
|
||||
QMessageBox::Ok);
|
||||
_ircInfoBox->setTextFormat(Qt::RichText);
|
||||
_ircInfoBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
_ircInfoBox->show();
|
||||
}
|
||||
|
||||
_ircInfoBox->raise();
|
||||
}
|
||||
|
||||
void DialogsManager::showDomainConnectionDialog() {
|
||||
// if the dialog already exists we delete it so the connection data is refreshed
|
||||
if (_domainConnectionDialog) {
|
||||
|
|
|
@ -57,7 +57,6 @@ public slots:
|
|||
void lodTools();
|
||||
void hmdTools(bool showTools);
|
||||
void showScriptEditor();
|
||||
void showIRCLink();
|
||||
void showDomainConnectionDialog();
|
||||
|
||||
// Application Update
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <NetworkAccessManager.h>
|
||||
|
||||
#include "DiskCacheEditor.h"
|
||||
#include "OffscreenUi.h"
|
||||
|
||||
DiskCacheEditor::DiskCacheEditor(QWidget* parent) : QObject(parent) {
|
||||
|
||||
|
@ -136,7 +137,7 @@ void DiskCacheEditor::refresh() {
|
|||
|
||||
void DiskCacheEditor::clear() {
|
||||
QMessageBox::StandardButton buttonClicked =
|
||||
QMessageBox::question(_dialog, "Clearing disk cache",
|
||||
OffscreenUi::question(_dialog, "Clearing disk cache",
|
||||
"You are about to erase all the content of the disk cache,"
|
||||
"are you sure you want to do that?");
|
||||
if (buttonClicked == QMessageBox::Yes) {
|
||||
|
|
|
@ -48,6 +48,7 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
|
|||
|
||||
connect(ui.buttonChangeAppearance, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser);
|
||||
connect(ui.appearanceDescription, &QLineEdit::editingFinished, this, &PreferencesDialog::changeFullAvatarURL);
|
||||
connect(ui.useAcuityCheckBox, &QCheckBox::clicked, this, &PreferencesDialog::changeUseAcuity);
|
||||
|
||||
connect(qApp, &Application::fullAvatarURLChanged, this, &PreferencesDialog::fullAvatarURLChanged);
|
||||
|
||||
|
@ -58,6 +59,17 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
|
|||
UIUtil::scaleWidgetFontSizes(this);
|
||||
}
|
||||
|
||||
void PreferencesDialog::changeUseAcuity() {
|
||||
bool useAcuity = ui.useAcuityCheckBox->isChecked();
|
||||
ui.label_desktopMinimumFPSSpin->setEnabled(useAcuity);
|
||||
ui.desktopMinimumFPSSpin->setEnabled(useAcuity);
|
||||
ui.label_hmdMinimumFPSSpin->setEnabled(useAcuity);
|
||||
ui.hmdMinimumFPSSpin->setEnabled(useAcuity);
|
||||
ui.label_smallestReasonableRenderHorizon->setEnabled(!useAcuity);
|
||||
ui.smallestReasonableRenderHorizon->setEnabled(!useAcuity);
|
||||
Menu::getInstance()->getActionForOption(MenuOption::LodTools)->setEnabled(useAcuity);
|
||||
Menu::getInstance()->getSubMenuFromName(MenuOption::RenderResolution, Menu::getInstance()->getSubMenuFromName("Render", Menu::getInstance()->getMenu("Developer")))->setEnabled(useAcuity);
|
||||
}
|
||||
void PreferencesDialog::changeFullAvatarURL() {
|
||||
DependencyManager::get<AvatarManager>()->getMyAvatar()->useFullAvatarURL(ui.appearanceDescription->text(), "");
|
||||
this->fullAvatarURLChanged(ui.appearanceDescription->text(), "");
|
||||
|
@ -127,22 +139,22 @@ void PreferencesDialog::openFullAvatarModelBrowser() {
|
|||
}
|
||||
|
||||
void PreferencesDialog::resizeEvent(QResizeEvent *resizeEvent) {
|
||||
|
||||
|
||||
// keep buttons panel at the bottom
|
||||
ui.buttonsPanel->setGeometry(0,
|
||||
size().height() - ui.buttonsPanel->height(),
|
||||
size().width(),
|
||||
ui.buttonsPanel->height());
|
||||
|
||||
|
||||
// set width and height of srcollarea to match bottom panel and width
|
||||
ui.scrollArea->setGeometry(ui.scrollArea->geometry().x(), ui.scrollArea->geometry().y(),
|
||||
size().width(),
|
||||
size().height() - ui.buttonsPanel->height() - ui.scrollArea->geometry().y());
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PreferencesDialog::loadPreferences() {
|
||||
|
||||
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
Menu* menuInstance = Menu::getInstance();
|
||||
|
||||
|
@ -163,14 +175,14 @@ void PreferencesDialog::loadPreferences() {
|
|||
|
||||
ui.pupilDilationSlider->setValue(myAvatar->getHead()->getPupilDilation() *
|
||||
ui.pupilDilationSlider->maximum());
|
||||
|
||||
|
||||
auto dde = DependencyManager::get<DdeFaceTracker>();
|
||||
ui.ddeEyeClosingThresholdSlider->setValue(dde->getEyeClosingThreshold() *
|
||||
ui.ddeEyeClosingThresholdSlider->setValue(dde->getEyeClosingThreshold() *
|
||||
ui.ddeEyeClosingThresholdSlider->maximum());
|
||||
|
||||
ui.faceTrackerEyeDeflectionSider->setValue(FaceTracker::getEyeDeflection() *
|
||||
ui.faceTrackerEyeDeflectionSider->maximum());
|
||||
|
||||
|
||||
auto faceshift = DependencyManager::get<Faceshift>();
|
||||
ui.faceshiftHostnameEdit->setText(faceshift->getHostname());
|
||||
|
||||
|
@ -196,12 +208,12 @@ void PreferencesDialog::loadPreferences() {
|
|||
ui.realWorldFieldOfViewSpin->setValue(myAvatar->getRealWorldFieldOfView());
|
||||
|
||||
ui.fieldOfViewSpin->setValue(qApp->getFieldOfView());
|
||||
|
||||
|
||||
ui.leanScaleSpin->setValue(myAvatar->getLeanScale());
|
||||
|
||||
ui.avatarScaleSpin->setValue(myAvatar->getUniformScale());
|
||||
ui.avatarAnimationEdit->setText(myAvatar->getAnimGraphUrl().toString());
|
||||
|
||||
|
||||
ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond());
|
||||
|
||||
#if 0
|
||||
|
@ -212,17 +224,19 @@ void PreferencesDialog::loadPreferences() {
|
|||
|
||||
// LOD items
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
ui.useAcuityCheckBox->setChecked(lodManager->getUseAcuity());
|
||||
ui.desktopMinimumFPSSpin->setValue(lodManager->getDesktopLODDecreaseFPS());
|
||||
ui.hmdMinimumFPSSpin->setValue(lodManager->getHMDLODDecreaseFPS());
|
||||
ui.avatarRenderSmallestReasonableHorizon->setValue(1.0f / DependencyManager::get<AvatarManager>()->getRenderDistanceInverseHighLimit());
|
||||
ui.smallestReasonableRenderHorizon->setValue(1.0f / lodManager->getRenderDistanceInverseHighLimit());
|
||||
changeUseAcuity();
|
||||
}
|
||||
|
||||
void PreferencesDialog::savePreferences() {
|
||||
|
||||
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
bool shouldDispatchIdentityPacket = false;
|
||||
|
||||
|
||||
QString displayNameStr(ui.displayNameEdit->text());
|
||||
if (displayNameStr != _displayNameString) {
|
||||
myAvatar->setDisplayName(displayNameStr);
|
||||
|
@ -233,7 +247,7 @@ void PreferencesDialog::savePreferences() {
|
|||
if (shouldDispatchIdentityPacket) {
|
||||
myAvatar->sendIdentityPacket();
|
||||
}
|
||||
|
||||
|
||||
myAvatar->setCollisionSoundURL(ui.collisionSoundURLEdit->text());
|
||||
|
||||
// MyAvatar persists its own data. If it doesn't agree with what the user has explicitly accepted, set it back to old values.
|
||||
|
@ -262,28 +276,28 @@ void PreferencesDialog::savePreferences() {
|
|||
}
|
||||
|
||||
myAvatar->setRealWorldFieldOfView(ui.realWorldFieldOfViewSpin->value());
|
||||
|
||||
|
||||
qApp->setFieldOfView(ui.fieldOfViewSpin->value());
|
||||
|
||||
|
||||
auto dde = DependencyManager::get<DdeFaceTracker>();
|
||||
dde->setEyeClosingThreshold(ui.ddeEyeClosingThresholdSlider->value() /
|
||||
dde->setEyeClosingThreshold(ui.ddeEyeClosingThresholdSlider->value() /
|
||||
(float)ui.ddeEyeClosingThresholdSlider->maximum());
|
||||
|
||||
FaceTracker::setEyeDeflection(ui.faceTrackerEyeDeflectionSider->value() /
|
||||
(float)ui.faceTrackerEyeDeflectionSider->maximum());
|
||||
|
||||
|
||||
auto faceshift = DependencyManager::get<Faceshift>();
|
||||
faceshift->setHostname(ui.faceshiftHostnameEdit->text());
|
||||
|
||||
|
||||
qApp->setMaxOctreePacketsPerSecond(ui.maxOctreePPSSpin->value());
|
||||
|
||||
qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());
|
||||
|
||||
|
||||
controller::InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
|
||||
|
||||
auto audio = DependencyManager::get<AudioClient>();
|
||||
MixedProcessedAudioStream& stream = audio->getReceivedAudioStream();
|
||||
|
||||
|
||||
stream.setDynamicJitterBuffers(ui.dynamicJitterBuffersCheckBox->isChecked());
|
||||
stream.setStaticDesiredJitterBufferFrames(ui.staticDesiredJitterBufferFramesSpin->value());
|
||||
stream.setMaxFramesOverDesired(ui.maxFramesOverDesiredSpin->value());
|
||||
|
@ -303,7 +317,8 @@ void PreferencesDialog::savePreferences() {
|
|||
|
||||
// LOD items
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
lodManager->setUseAcuity(ui.useAcuityCheckBox->isChecked());
|
||||
lodManager->setDesktopLODDecreaseFPS(ui.desktopMinimumFPSSpin->value());
|
||||
lodManager->setHMDLODDecreaseFPS(ui.hmdMinimumFPSSpin->value());
|
||||
DependencyManager::get<AvatarManager>()->setRenderDistanceInverseHighLimit(1.0f / ui.avatarRenderSmallestReasonableHorizon->value());
|
||||
lodManager->setRenderDistanceInverseHighLimit(1.0f / ui.smallestReasonableRenderHorizon->value());
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ private slots:
|
|||
void openScriptsLocationBrowser();
|
||||
void changeFullAvatarURL();
|
||||
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
|
||||
void changeUseAcuity();
|
||||
};
|
||||
|
||||
#endif // hifi_PreferencesDialog_h
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <QWidget>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "ScriptHighlighting.h"
|
||||
|
@ -120,7 +121,7 @@ bool ScriptEditorWidget::setRunning(bool run) {
|
|||
bool ScriptEditorWidget::saveFile(const QString &scriptPath) {
|
||||
QFile file(scriptPath);
|
||||
if (!file.open(QFile::WriteOnly | QFile::Text)) {
|
||||
QMessageBox::warning(this, tr("Interface"), tr("Cannot write script %1:\n%2.").arg(scriptPath)
|
||||
OffscreenUi::warning(this, tr("Interface"), tr("Cannot write script %1:\n%2.").arg(scriptPath)
|
||||
.arg(file.errorString()));
|
||||
return false;
|
||||
}
|
||||
|
@ -141,7 +142,7 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) {
|
|||
if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
|
||||
QFile file(scriptPath);
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QMessageBox::warning(this, tr("Interface"), tr("Cannot read script %1:\n%2.").arg(scriptPath)
|
||||
OffscreenUi::warning(this, tr("Interface"), tr("Cannot read script %1:\n%2.").arg(scriptPath)
|
||||
.arg(file.errorString()));
|
||||
return;
|
||||
}
|
||||
|
@ -208,7 +209,7 @@ void ScriptEditorWidget::setScriptFile(const QString& scriptPath) {
|
|||
|
||||
bool ScriptEditorWidget::questionSave() {
|
||||
if (_scriptEditorWidgetUI->scriptEdit->document()->isModified()) {
|
||||
QMessageBox::StandardButton button = QMessageBox::warning(this, tr("Interface"),
|
||||
QMessageBox::StandardButton button = OffscreenUi::warning(this, tr("Interface"),
|
||||
tr("The script has been modified.\nDo you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard |
|
||||
QMessageBox::Cancel, QMessageBox::Save);
|
||||
return button == QMessageBox::Save ? save() : (button == QMessageBox::Discard);
|
||||
|
@ -222,7 +223,7 @@ void ScriptEditorWidget::onWindowActivated() {
|
|||
|
||||
if (QFileInfo(_currentScript).lastModified() > _currentScriptModified) {
|
||||
if (static_cast<ScriptEditorWindow*>(this->parent()->parent()->parent())->autoReloadScripts()
|
||||
|| QMessageBox::warning(this, _currentScript,
|
||||
|| OffscreenUi::warning(this, _currentScript,
|
||||
tr("This file has been modified outside of the Interface editor.") + "\n\n"
|
||||
+ (isModified()
|
||||
? tr("Do you want to reload it and lose the changes you've made in the Interface editor?")
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <QMessageBox>
|
||||
#include <QUrlQuery>
|
||||
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
const int NARROW_SNAPSHOT_DIALOG_SIZE = 500;
|
||||
const int WIDE_SNAPSHOT_DIALOG_WIDTH = 650;
|
||||
|
@ -87,7 +88,7 @@ void SnapshotShareDialog::accept() {
|
|||
void SnapshotShareDialog::uploadSnapshot() {
|
||||
|
||||
if (AccountManager::getInstance().getAccountInfo().getDiscourseApiKey().isEmpty()) {
|
||||
QMessageBox::warning(this, "",
|
||||
OffscreenUi::warning(this, "",
|
||||
"Your Discourse API key is missing, you cannot share snapshots. Please try to relog.");
|
||||
return;
|
||||
}
|
||||
|
@ -178,7 +179,7 @@ void SnapshotShareDialog::postRequestFinished() {
|
|||
errorMessage = errorArray.first().toString();
|
||||
}
|
||||
}
|
||||
QMessageBox::warning(this, "", errorMessage);
|
||||
OffscreenUi::warning(this, "", errorMessage);
|
||||
_ui.shareButton->setEnabled(true);
|
||||
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_ENABLED_STYLE);
|
||||
}
|
||||
|
@ -193,7 +194,7 @@ void SnapshotShareDialog::uploadRequestFinished() {
|
|||
if (responseObject.contains("url")) {
|
||||
sendForumPost(responseObject["url"].toString());
|
||||
} else {
|
||||
QMessageBox::warning(this, "", SHARE_DEFAULT_ERROR);
|
||||
OffscreenUi::warning(this, "", SHARE_DEFAULT_ERROR);
|
||||
_ui.shareButton->setEnabled(true);
|
||||
_ui.shareButton->setStyleSheet(SHARE_BUTTON_STYLE + SHARE_BUTTON_ENABLED_STYLE);
|
||||
}
|
||||
|
|
|
@ -116,8 +116,6 @@ void Stats::updateStats(bool force) {
|
|||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
// we need to take one avatar out so we don't include ourselves
|
||||
STAT_UPDATE(avatarCount, avatarManager->size() - 1);
|
||||
STAT_UPDATE(avatarRenderableCount, avatarManager->getNumberInRenderRange());
|
||||
STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating
|
||||
STAT_UPDATE(serverCount, (int)nodeList->size());
|
||||
STAT_UPDATE(renderrate, (int)qApp->getFps());
|
||||
if (qApp->getActiveDisplayPlugin()) {
|
||||
|
@ -285,7 +283,9 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(localLeaves, (int)OctreeElement::getLeafNodeCount());
|
||||
// LOD Details
|
||||
STAT_UPDATE(lodStatus, "You can see " + DependencyManager::get<LODManager>()->getLODFeedbackText());
|
||||
STAT_UPDATE(lodStatsRenderText, DependencyManager::get<LODManager>()->getLODStatsRenderText());
|
||||
}
|
||||
STAT_UPDATE(showAcuity, (_expanded || force) && DependencyManager::get<LODManager>()->getUseAcuity());
|
||||
|
||||
bool performanceTimerIsActive = PerformanceTimer::isActive();
|
||||
bool displayPerf = _expanded && Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails);
|
||||
|
|
|
@ -30,6 +30,7 @@ class Stats : public QQuickItem {
|
|||
Q_PROPERTY(QString monospaceFont READ monospaceFont CONSTANT)
|
||||
Q_PROPERTY(float audioPacketlossUpstream READ getAudioPacketLossUpstream)
|
||||
Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream)
|
||||
Q_PROPERTY(bool showAcuity READ getShowAcuity WRITE setShowAcuity NOTIFY showAcuityChanged)
|
||||
|
||||
STATS_PROPERTY(int, serverCount, 0)
|
||||
STATS_PROPERTY(int, renderrate, 0)
|
||||
|
@ -37,8 +38,6 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, simrate, 0)
|
||||
STATS_PROPERTY(int, avatarSimrate, 0)
|
||||
STATS_PROPERTY(int, avatarCount, 0)
|
||||
STATS_PROPERTY(int, avatarRenderableCount, 0)
|
||||
STATS_PROPERTY(int, avatarRenderDistance, 0)
|
||||
STATS_PROPERTY(int, packetInCount, 0)
|
||||
STATS_PROPERTY(int, packetOutCount, 0)
|
||||
STATS_PROPERTY(float, mbpsIn, 0)
|
||||
|
@ -77,6 +76,7 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(QString, packetStats, QString())
|
||||
STATS_PROPERTY(QString, lodStatus, QString())
|
||||
STATS_PROPERTY(QString, timingStats, QString())
|
||||
STATS_PROPERTY(QString, lodStatsRenderText, QString())
|
||||
STATS_PROPERTY(int, serverElements, 0)
|
||||
STATS_PROPERTY(int, serverInternal, 0)
|
||||
STATS_PROPERTY(int, serverLeaves, 0)
|
||||
|
@ -108,12 +108,15 @@ public:
|
|||
emit expandedChanged();
|
||||
}
|
||||
}
|
||||
bool getShowAcuity() { return _showAcuity; }
|
||||
void setShowAcuity(bool newValue) { _showAcuity = newValue; }
|
||||
|
||||
public slots:
|
||||
void forceUpdateStats() { updateStats(true); }
|
||||
|
||||
signals:
|
||||
void expandedChanged();
|
||||
void showAcuityChanged();
|
||||
void timingExpandedChanged();
|
||||
void serverCountChanged();
|
||||
void renderrateChanged();
|
||||
|
@ -121,8 +124,7 @@ signals:
|
|||
void simrateChanged();
|
||||
void avatarSimrateChanged();
|
||||
void avatarCountChanged();
|
||||
void avatarRenderableCountChanged();
|
||||
void avatarRenderDistanceChanged();
|
||||
void lodStatsRenderTextChanged();
|
||||
void packetInCountChanged();
|
||||
void packetOutCountChanged();
|
||||
void mbpsInChanged();
|
||||
|
@ -172,6 +174,7 @@ private:
|
|||
int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process
|
||||
bool _resetRecentMaxPacketsSoon{ true };
|
||||
bool _expanded{ false };
|
||||
bool _showAcuity{ false };
|
||||
bool _timingExpanded{ false };
|
||||
QString _monospaceFont;
|
||||
const AudioIOStats* _audioStats;
|
||||
|
|
|
@ -742,6 +742,43 @@
|
|||
</widget>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useAcuityCheckBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>28</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Render based on visual acuity</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_111x">
|
||||
<property name="spacing">
|
||||
|
@ -757,7 +794,7 @@
|
|||
<number>7</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9x">
|
||||
<widget class="QLabel" name="label_desktopMinimumFPSSpin">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
|
@ -842,7 +879,7 @@
|
|||
<number>7</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9y">
|
||||
<widget class="QLabel" name="label_hmdMinimumFPSSpin">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
|
@ -927,7 +964,7 @@
|
|||
<number>7</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9yz">
|
||||
<widget class="QLabel" name="label_smallestReasonableRenderHorizon">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
|
@ -937,7 +974,7 @@
|
|||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Minimum Avatar Display Distance</string>
|
||||
<string>Minimum Display Distance</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>0</number>
|
||||
|
@ -963,7 +1000,7 @@
|
|||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="avatarRenderSmallestReasonableHorizon">
|
||||
<widget class="QSpinBox" name="smallestReasonableRenderHorizon">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
|
|
|
@ -245,6 +245,14 @@ int Rig::indexOfJoint(const QString& jointName) const {
|
|||
}
|
||||
}
|
||||
|
||||
QString Rig::nameOfJoint(int jointIndex) const {
|
||||
if (_animSkeleton) {
|
||||
return _animSkeleton->getJointName(jointIndex);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::setModelOffset(const glm::mat4& modelOffsetMat) {
|
||||
AnimPose newModelOffset = AnimPose(modelOffsetMat);
|
||||
if (!isEqual(_modelOffset.trans, newModelOffset.trans) ||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue