mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
fix merge conflicts attemp 1
This commit is contained in:
commit
5cce4edec8
167 changed files with 5157 additions and 2328 deletions
|
@ -40,7 +40,8 @@ if (WIN32)
|
|||
endif ()
|
||||
message (WINDOW_SDK_PATH= ${WINDOW_SDK_PATH})
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOW_SDK_PATH})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
# /wd4351 disables warning C4351: new behavior: elements of array will be default initialized
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4351")
|
||||
# /LARGEADDRESSAWARE enables 32-bit apps to use more than 2GB of memory.
|
||||
# Caveats: http://stackoverflow.com/questions/2288728/drawbacks-of-using-largeaddressaware-for-32-bit-windows-executables
|
||||
# TODO: Remove when building 64-bit.
|
||||
|
@ -177,8 +178,13 @@ option(GET_GVERB "Get Gverb library automatically as external project" 1)
|
|||
option(GET_SOXR "Get Soxr library automatically as external project" 1)
|
||||
option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1)
|
||||
option(GET_LIBOVR "Get LibOVR library automatically as external project" 1)
|
||||
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
|
||||
option(GET_VHACD "Get V-HACD library automatically as external project" 1)
|
||||
option(GET_POLYVOX "Get polyvox library automatically as external project" 1)
|
||||
option(GET_OPENVR "Get OpenVR library automatically as external project" 1)
|
||||
option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1)
|
||||
option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1)
|
||||
|
||||
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
|
||||
|
||||
if (WIN32)
|
||||
option(GET_GLEW "Get GLEW library automatically as external project" 1)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#
|
||||
# OSXTBBInstallNameChange.cmake
|
||||
# cmake/externals/tbb
|
||||
# OSXInstallNameChange.cmake
|
||||
# cmake/macros
|
||||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Stephen Birarda on February 20, 2014
|
||||
|
@ -10,36 +10,33 @@
|
|||
#
|
||||
|
||||
# first find the so files in the source dir
|
||||
set(_TBB_LIBRARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib)
|
||||
file(GLOB_RECURSE _TBB_LIBRARIES "${_TBB_LIBRARY_DIR}/*.dylib")
|
||||
message("INSTALL_NAME_LIBRARY_DIR ${INSTALL_NAME_LIBRARY_DIR}")
|
||||
|
||||
# raise an error if we found none
|
||||
if (NOT _TBB_LIBRARIES)
|
||||
message(FATAL_ERROR "Did not find any TBB libraries")
|
||||
file(GLOB_RECURSE _LIBRARIES "${INSTALL_NAME_LIBRARY_DIR}/*.dylib")
|
||||
|
||||
if (NOT _LIBRARIES)
|
||||
message(FATAL_ERROR "OSXInstallNameChange -- no libraries found: ${INSTALL_NAME_LIBRARY_DIR}")
|
||||
endif ()
|
||||
|
||||
# find the install_name_tool command
|
||||
find_program(INSTALL_NAME_TOOL_COMMAND NAMES install_name_tool DOC "Path to the install_name_tool command")
|
||||
|
||||
# find the lipo command
|
||||
find_program(LIPO_COMMAND NAMES lipo DOC "Path to the lipo command")
|
||||
|
||||
# enumerate the libraries
|
||||
foreach(_TBB_LIBRARY ${_TBB_LIBRARIES})
|
||||
get_filename_component(_TBB_LIBRARY_FILENAME ${_TBB_LIBRARY} NAME)
|
||||
foreach(_LIBRARY ${_LIBRARIES})
|
||||
get_filename_component(_LIBRARY_FILENAME ${_LIBRARY} NAME)
|
||||
|
||||
set(_INSTALL_NAME_ARGS ${INSTALL_NAME_TOOL_COMMAND} -id ${_TBB_LIBRARY} ${_TBB_LIBRARY_FILENAME})
|
||||
set(_INSTALL_NAME_ARGS ${INSTALL_NAME_TOOL_COMMAND} -id ${_LIBRARY} ${_LIBRARY_FILENAME})
|
||||
|
||||
message(STATUS "${INSTALL_NAME_COMMAND} ${_INSTALL_NAME_ARGS}")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${INSTALL_NAME_COMMAND} ${_INSTALL_NAME_ARGS}
|
||||
WORKING_DIRECTORY ${_TBB_LIBRARY_DIR}
|
||||
WORKING_DIRECTORY ${INSTALL_NAME_LIBRARY_DIR}
|
||||
ERROR_VARIABLE _INSTALL_NAME_ERROR
|
||||
)
|
||||
|
||||
if (_INSTALL_NAME_ERROR)
|
||||
message(FATAL_ERROR "There was an error changing install name for ${_TBB_LIBRARY_FILENAME} - ${_INSTALL_NAME_ERROR}")
|
||||
message(FATAL_ERROR "There was an error changing install name for ${_LIBRARY_FILENAME} - ${_INSTALL_NAME_ERROR}")
|
||||
endif ()
|
||||
endforeach()
|
||||
|
18
cmake/externals/boostconfig/CMakeLists.txt
vendored
Normal file
18
cmake/externals/boostconfig/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
set(EXTERNAL_NAME boostconfig)
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://github.com/boostorg/config/archive/boost-1.58.0.zip
|
||||
URL_MD5 42fa673bae2b7645a22736445e80eb8d
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE INTERNAL)
|
||||
|
18
cmake/externals/oglplus/CMakeLists.txt
vendored
Normal file
18
cmake/externals/oglplus/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
set(EXTERNAL_NAME oglplus)
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://softlayer-dal.dl.sourceforge.net/project/oglplus/oglplus-0.61.x/oglplus-0.61.0.zip
|
||||
URL_MD5 bb55038c36c660d2b6c7be380414fa60
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include ${SOURCE_DIR}/implement CACHE TYPE INTERNAL)
|
||||
|
4
cmake/externals/openvr/CMakeLists.txt
vendored
4
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -7,8 +7,8 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://github.com/ValveSoftware/openvr/archive/0.9.0.zip
|
||||
URL_MD5 4fbde7759f604aaa68b9c40d628cc34a
|
||||
URL https://github.com/ValveSoftware/openvr/archive/0.9.1.zip
|
||||
URL_MD5 f986f5a6815e9454c53c5bf58ce02fdc
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
57
cmake/externals/polyvox/CMakeLists.txt
vendored
Normal file
57
cmake/externals/polyvox/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
set(EXTERNAL_NAME polyvox)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/polyvox-master-2015-5-27.zip
|
||||
URL_MD5 e3dd09a24df4db29ba370e3bea753388
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
if (APPLE)
|
||||
set(INSTALL_NAME_LIBRARY_DIR ${INSTALL_DIR}/lib)
|
||||
message(STATUS "in polyvox INSTALL_NAME_LIBRARY_DIR ${INSTALL_NAME_LIBRARY_DIR}")
|
||||
ExternalProject_Add_Step(
|
||||
${EXTERNAL_NAME}
|
||||
change-install-name
|
||||
COMMENT "Calling install_name_tool on libraries to fix install name for dylib linking"
|
||||
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${INSTALL_NAME_LIBRARY_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
|
||||
DEPENDEES install
|
||||
WORKING_DIRECTORY <SOURCE_DIR>
|
||||
LOG 1
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_CORE_INCLUDE_DIRS ${INSTALL_DIR}/PolyVoxCore/include CACHE FILEPATH
|
||||
"Path to polyvox core include directory")
|
||||
set(${EXTERNAL_NAME_UPPER}_UTIL_INCLUDE_DIRS ${INSTALL_DIR}/PolyVoxUtil/include CACHE FILEPATH
|
||||
"Path to polyvox util include directory")
|
||||
else ()
|
||||
set(${EXTERNAL_NAME_UPPER}_CORE_INCLUDE_DIRS ${INSTALL_DIR}/include/PolyVoxCore CACHE FILEPATH
|
||||
"Path to polyvox core include directory")
|
||||
set(${EXTERNAL_NAME_UPPER}_UTIL_INCLUDE_DIRS ${INSTALL_DIR}/include/PolyVoxUtil CACHE FILEPATH
|
||||
"Path to polyvox util include directory")
|
||||
endif ()
|
||||
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY ${INSTALL_DIR}/PolyVoxCore/lib/PolyVoxCore.lib CACHE FILEPATH "polyvox core library")
|
||||
# set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY ${INSTALL_DIR}/PolyVoxUtil/lib/PolyVoxUtil.lib CACHE FILEPATH "polyvox util library")
|
||||
elseif (APPLE)
|
||||
set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY ${INSTALL_DIR}/lib/libPolyVoxCore.dylib CACHE FILEPATH "polyvox core library")
|
||||
# set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY ${INSTALL_DIR}/lib/libPolyVoxUtil.dylib CACHE FILEPATH "polyvox util library")
|
||||
else ()
|
||||
set(${EXTERNAL_NAME_UPPER}_CORE_LIBRARY ${INSTALL_DIR}/lib/libPolyVoxCore.so CACHE FILEPATH "polyvox core library")
|
||||
# set(${EXTERNAL_NAME_UPPER}_UTIL_LIBRARY ${INSTALL_DIR}/lib/libPolyVoxUtil.so CACHE FILEPATH "polyvox util library")
|
||||
endif ()
|
4
cmake/externals/tbb/CMakeLists.txt
vendored
4
cmake/externals/tbb/CMakeLists.txt
vendored
|
@ -66,7 +66,7 @@ if (APPLE)
|
|||
${EXTERNAL_NAME}
|
||||
change-install-name
|
||||
COMMENT "Calling install_name_tool on TBB libraries to fix install name for dylib linking"
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/OSXTBBInstallNameChange.cmake
|
||||
COMMAND ${CMAKE_COMMAND} -DINSTALL_NAME_LIBRARY_DIR=${_TBB_LIB_DIR} -P ${EXTERNAL_PROJECT_DIR}/OSXInstallNameChange.cmake
|
||||
DEPENDEES install
|
||||
WORKING_DIRECTORY <SOURCE_DIR>
|
||||
LOG 1
|
||||
|
@ -115,4 +115,4 @@ endif ()
|
|||
|
||||
if (DEFINED ${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/include CACHE TYPE "List of tbb include directories")
|
||||
endif ()
|
||||
endif ()
|
||||
|
|
24
cmake/modules/FindBoostConfig.cmake
Normal file
24
cmake/modules/FindBoostConfig.cmake
Normal file
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# Try to find BOOSTCONFIG include path.
|
||||
# Once done this will define
|
||||
#
|
||||
# BOOSTCONFIG_INCLUDE_DIRS
|
||||
#
|
||||
# Created by Bradley Austin Davis on 2015/05/22
|
||||
# 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
|
||||
#
|
||||
|
||||
# setup hints for BOOSTCONFIG search
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("BOOSTCONFIG")
|
||||
|
||||
# locate header
|
||||
find_path(BOOSTCONFIG_INCLUDE_DIRS "boost/config.hpp" HINTS ${BOOSTCONFIG_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(BOOSTCONFIG DEFAULT_MSG BOOSTCONFIG_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(BOOSTCONFIG_INCLUDE_DIRS BOOSTCONFIG_SEARCH_DIRS)
|
24
cmake/modules/FindOGLPLUS.cmake
Normal file
24
cmake/modules/FindOGLPLUS.cmake
Normal file
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# Try to find OGLPLUS include path.
|
||||
# Once done this will define
|
||||
#
|
||||
# OGLPLUS_INCLUDE_DIRS
|
||||
#
|
||||
# Created by Bradley Austin Davis on 2015/05/22
|
||||
# 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
|
||||
#
|
||||
|
||||
# setup hints for OGLPLUS search
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("oglplus")
|
||||
|
||||
# locate header
|
||||
find_path(OGLPLUS_INCLUDE_DIRS "oglplus/fwd.hpp" HINTS ${OGLPLUS_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(OGLPLUS DEFAULT_MSG OGLPLUS_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(OGLPLUS_INCLUDE_DIRS OGLPLUS_SEARCH_DIRS)
|
54
cmake/modules/FindPolyVox.cmake
Normal file
54
cmake/modules/FindPolyVox.cmake
Normal file
|
@ -0,0 +1,54 @@
|
|||
#
|
||||
# FindPolyvox.cmake
|
||||
#
|
||||
# Try to find the libpolyvox resampling library
|
||||
#
|
||||
# You can provide a LIBPOLYVOX_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# POLYVOX_FOUND - system found libpolyvox
|
||||
# POLYVOX_INCLUDE_DIRS - the libpolyvox include directory
|
||||
# POLYVOX_LIBRARIES - link to this to use libpolyvox
|
||||
#
|
||||
# Created on 1/22/2015 by Stephen Birarda
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("polyvox")
|
||||
|
||||
find_path(POLYVOX_CORE_INCLUDE_DIRS PolyVoxCore/SimpleVolume.h PATH_SUFFIXES include include/PolyVoxCore HINTS ${POLYVOX_SEARCH_DIRS})
|
||||
# find_path(POLYVOX_UTIL_INCLUDE_DIRS PolyVoxUtil/Serialization.h PATH_SUFFIXES include include/PolyVoxUtil HINTS ${POLYVOX_SEARCH_DIRS})
|
||||
|
||||
find_library(POLYVOX_CORE_LIBRARY NAMES PolyVoxCore PATH_SUFFIXES lib HINTS ${POLYVOX_SEARCH_DIRS})
|
||||
# find_library(POLYVOX_UTIL_LIBRARY NAMES PolyVoxUtil PATH_SUFFIXES lib HINTS ${POLYVOX_SEARCH_DIRS})
|
||||
|
||||
|
||||
# if (WIN32)
|
||||
# find_path(POLYVOX_DLL_PATH polyvox.dll PATH_SUFFIXES bin HINTS ${POLYVOX_SEARCH_DIRS})
|
||||
# endif()
|
||||
|
||||
# set(POLYVOX_REQUIREMENTS POLYVOX_CORE_INCLUDE_DIRS POLYVOX_UTIL_INCLUDE_DIRS POLYVOX_CORE_LIBRARY POLYVOX_UTIL_LIBRARY)
|
||||
set(POLYVOX_REQUIREMENTS POLYVOX_CORE_INCLUDE_DIRS POLYVOX_CORE_LIBRARY)
|
||||
# if (WIN32)
|
||||
# list(APPEND POLYVOX_REQUIREMENTS POLYVOX_DLL_PATH)
|
||||
# endif ()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Polyvox DEFAULT_MSG ${POLYVOX_REQUIREMENTS})
|
||||
|
||||
# if (WIN32)
|
||||
# add_paths_to_fixup_libs(${POLYVOX_DLL_PATH})
|
||||
# endif ()
|
||||
|
||||
# set(POLYVOX_INCLUDE_DIRS ${POLYVOX_CORE_INCLUDE_DIRS} ${POLYVOX_UTIL_INCLUDE_DIRS})
|
||||
# set(POLYVOX_LIBRARIES ${POLYVOX_CORE_LIBRARY} ${POLYVOX_UTIL_LIBRARY})
|
||||
|
||||
set(POLYVOX_INCLUDE_DIRS ${POLYVOX_CORE_INCLUDE_DIRS})
|
||||
set(POLYVOX_LIBRARIES ${POLYVOX_CORE_LIBRARY})
|
||||
|
||||
mark_as_advanced(POLYVOX_INCLUDE_DIRS POLYVOX_LIBRARIES POLYVOX_SEARCH_DIRS)
|
|
@ -262,7 +262,6 @@ $(document).ready(function(){
|
|||
$('#' + Settings.FORM_ID).on('click', '#' + Settings.CONNECT_ACCOUNT_BTN_ID, function(e){
|
||||
$(this).blur();
|
||||
prepareAccessTokenPrompt();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
var panelsSource = $('#panels-template').html()
|
||||
|
|
|
@ -65,9 +65,13 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
_settingsManager(),
|
||||
_iceServerSocket(ICE_SERVER_DEFAULT_HOSTNAME, ICE_SERVER_DEFAULT_PORT)
|
||||
{
|
||||
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
||||
|
||||
LogUtils::init();
|
||||
Setting::init();
|
||||
|
||||
connect(this, &QCoreApplication::aboutToQuit, this, &DomainServer::aboutToQuit);
|
||||
|
||||
setOrganizationName("High Fidelity");
|
||||
setOrganizationDomain("highfidelity.io");
|
||||
setApplicationName("domain-server");
|
||||
|
@ -106,6 +110,11 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::aboutToQuit() {
|
||||
// clear the log handler so that Qt doesn't call the destructor on LogHandler
|
||||
qInstallMessageHandler(0);
|
||||
}
|
||||
|
||||
void DomainServer::restart() {
|
||||
qDebug() << "domain-server is restarting.";
|
||||
|
||||
|
@ -354,37 +363,23 @@ bool DomainServer::optionallySetupAssignmentPayment() {
|
|||
void DomainServer::setupAutomaticNetworking() {
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
const int STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS = 10 * 1000;
|
||||
const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000;
|
||||
|
||||
// setup our timer to check our IP via stun every X seconds
|
||||
QTimer* dynamicIPTimer = new QTimer(this);
|
||||
connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN);
|
||||
|
||||
_automaticNetworkingSetting =
|
||||
_settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString();
|
||||
|
||||
if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) {
|
||||
dynamicIPTimer->start(STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS);
|
||||
|
||||
// setup a timer to heartbeat with the ice-server every so often
|
||||
QTimer* iceHeartbeatTimer = new QTimer(this);
|
||||
connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates);
|
||||
iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
|
||||
|
||||
// call our sendHeartbeatToIceServer immediately anytime a local or public socket changes
|
||||
connect(nodeList.data(), &LimitedNodeList::localSockAddrChanged,
|
||||
this, &DomainServer::sendHeartbeatToIceServer);
|
||||
connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged,
|
||||
this, &DomainServer::sendHeartbeatToIceServer);
|
||||
|
||||
// attempt to update our public socket now, this will send a heartbeat once we get public socket
|
||||
requestCurrentPublicSocketViaSTUN();
|
||||
|
||||
// in case the STUN lookup is still happening we should re-request a public socket once we get that address
|
||||
connect(&nodeList->getSTUNSockAddr(), &HifiSockAddr::lookupCompleted,
|
||||
this, &DomainServer::requestCurrentPublicSocketViaSTUN);
|
||||
// we need this DS to know what our public IP is - start trying to figure that out now
|
||||
nodeList->startSTUNPublicSocketUpdate();
|
||||
|
||||
// setup a timer to heartbeat with the ice-server every so often
|
||||
QTimer* iceHeartbeatTimer = new QTimer(this);
|
||||
connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToIceServer);
|
||||
iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS);
|
||||
}
|
||||
|
||||
if (!didSetupAccountManagerWithAccessToken()) {
|
||||
|
@ -404,14 +399,12 @@ void DomainServer::setupAutomaticNetworking() {
|
|||
<< uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
|
||||
|
||||
if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) {
|
||||
dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS);
|
||||
|
||||
// send public socket changes to the data server so nodes can find us at our new IP
|
||||
// send any public socket changes to the data server so nodes can find us at our new IP
|
||||
connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged,
|
||||
this, &DomainServer::performIPAddressUpdate);
|
||||
|
||||
// attempt to update our sockets now
|
||||
requestCurrentPublicSocketViaSTUN();
|
||||
// have the LNL enable public socket updating via STUN
|
||||
nodeList->startSTUNPublicSocketUpdate();
|
||||
} else {
|
||||
// send our heartbeat to data server so it knows what our network settings are
|
||||
sendHeartbeatToDataServer();
|
||||
|
@ -574,7 +567,6 @@ const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer
|
|||
<< NodeType::AvatarMixer << NodeType::EntityServer;
|
||||
|
||||
void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
|
||||
|
||||
NodeType_t nodeType;
|
||||
HifiSockAddr publicSockAddr, localSockAddr;
|
||||
|
||||
|
@ -640,9 +632,17 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
|
||||
QUuid nodeUUID;
|
||||
|
||||
if (_connectingICEPeers.contains(packetUUID) || _connectedICEPeers.contains(packetUUID)) {
|
||||
HifiSockAddr discoveredSocket = senderSockAddr;
|
||||
SharedNetworkPeer connectedPeer = _icePeers.value(packetUUID);
|
||||
|
||||
if (connectedPeer) {
|
||||
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
||||
nodeUUID = packetUUID;
|
||||
|
||||
if (connectedPeer->getActiveSocket()) {
|
||||
// set their discovered socket to whatever the activated socket on the network peer object was
|
||||
discoveredSocket = *connectedPeer->getActiveSocket();
|
||||
}
|
||||
} else {
|
||||
// we got a packetUUID we didn't recognize, just add the node
|
||||
nodeUUID = QUuid::createUuid();
|
||||
|
@ -670,6 +670,10 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeUUID, nodeType,
|
||||
publicSockAddr, localSockAddr,
|
||||
canAdjustLocks, canRez);
|
||||
|
||||
// So that we can send messages to this node at will - we need to activate the correct socket on this node now
|
||||
newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket);
|
||||
|
||||
// when the newNode is created the linked data is also created
|
||||
// if this was a static assignment set the UUID, set the sendingSockAddr
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(newNode->getLinkedData());
|
||||
|
@ -699,10 +703,12 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock
|
|||
|
||||
// reply back to the user with a PacketTypeDomainList
|
||||
sendDomainListToNode(newNode, senderSockAddr, nodeInterestList.toSet());
|
||||
|
||||
// send out this node to our other connected nodes
|
||||
broadcastNewNode(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned int DomainServer::countConnectedUsers() {
|
||||
unsigned int result = 0;
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
@ -715,9 +721,9 @@ unsigned int DomainServer::countConnectedUsers() {
|
|||
}
|
||||
|
||||
|
||||
bool DomainServer::verifyUsersKey (const QString& username,
|
||||
const QByteArray& usernameSignature,
|
||||
QString& reasonReturn) {
|
||||
bool DomainServer::verifyUsersKey(const QString& username,
|
||||
const QByteArray& usernameSignature,
|
||||
QString& reasonReturn) {
|
||||
// it's possible this user can be allowed to connect, but we need to check their username signature
|
||||
|
||||
QByteArray publicKeyArray = _userPublicKeys.value(username);
|
||||
|
@ -912,26 +918,8 @@ int DomainServer::parseNodeDataFromByteArray(QDataStream& packetStream, NodeType
|
|||
return packetStream.device()->pos();
|
||||
}
|
||||
|
||||
NodeSet DomainServer::nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes) {
|
||||
QDataStream packetStream(packet);
|
||||
packetStream.skipRawData(numPreceedingBytes);
|
||||
|
||||
quint8 numInterestTypes = 0;
|
||||
packetStream >> numInterestTypes;
|
||||
|
||||
quint8 nodeType;
|
||||
NodeSet nodeInterestSet;
|
||||
|
||||
for (int i = 0; i < numInterestTypes; i++) {
|
||||
packetStream >> nodeType;
|
||||
nodeInterestSet.insert((NodeType_t) nodeType);
|
||||
}
|
||||
|
||||
return nodeInterestSet;
|
||||
}
|
||||
|
||||
void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr,
|
||||
const NodeSet& nodeInterestList) {
|
||||
const NodeSet& nodeInterestSet) {
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
QByteArray broadcastPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainList);
|
||||
|
||||
|
@ -945,14 +933,10 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
// if we've established a connection via ICE with this peer, use that socket
|
||||
// otherwise just try to reply back to them on their sending socket (although that may not work)
|
||||
HifiSockAddr destinationSockAddr = _connectedICEPeers.value(node->getUUID());
|
||||
if (destinationSockAddr.isNull()) {
|
||||
destinationSockAddr = senderSockAddr;
|
||||
}
|
||||
// store the nodeInterestSet on this DomainServerNodeData, in case it has changed
|
||||
nodeData->setNodeInterestSet(nodeInterestSet);
|
||||
|
||||
if (nodeInterestList.size() > 0) {
|
||||
if (nodeInterestSet.size() > 0) {
|
||||
|
||||
// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL;
|
||||
int dataMTU = MAX_PACKET_SIZE;
|
||||
|
@ -964,27 +948,13 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
QByteArray nodeByteArray;
|
||||
QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append);
|
||||
|
||||
if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) {
|
||||
if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) {
|
||||
|
||||
// don't send avatar nodes to other avatars, that will come from avatar mixer
|
||||
nodeDataStream << *otherNode.data();
|
||||
|
||||
// pack the secret that these two nodes will use to communicate with each other
|
||||
QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID());
|
||||
if (secretUUID.isNull()) {
|
||||
// generate a new secret UUID these two nodes can use
|
||||
secretUUID = QUuid::createUuid();
|
||||
|
||||
// set that on the current Node's sessionSecretHash
|
||||
nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID);
|
||||
|
||||
// set it on the other Node's sessionSecretHash
|
||||
reinterpret_cast<DomainServerNodeData*>(otherNode->getLinkedData())
|
||||
->getSessionSecretHash().insert(node->getUUID(), secretUUID);
|
||||
|
||||
}
|
||||
|
||||
nodeDataStream << secretUUID;
|
||||
nodeDataStream << connectionSecretForNodes(node, otherNode);
|
||||
|
||||
if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) {
|
||||
// we need to break here and start a new packet
|
||||
|
@ -1005,7 +975,62 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
}
|
||||
|
||||
// always write the last broadcastPacket
|
||||
limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr);
|
||||
limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node);
|
||||
}
|
||||
|
||||
QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) {
|
||||
DomainServerNodeData* nodeAData = dynamic_cast<DomainServerNodeData*>(nodeA->getLinkedData());
|
||||
DomainServerNodeData* nodeBData = dynamic_cast<DomainServerNodeData*>(nodeB->getLinkedData());
|
||||
|
||||
if (nodeAData && nodeBData) {
|
||||
QUuid& secretUUID = nodeAData->getSessionSecretHash()[nodeB->getUUID()];
|
||||
|
||||
if (secretUUID.isNull()) {
|
||||
// generate a new secret UUID these two nodes can use
|
||||
secretUUID = QUuid::createUuid();
|
||||
|
||||
// set it on the other Node's sessionSecretHash
|
||||
reinterpret_cast<DomainServerNodeData*>(nodeBData)->getSessionSecretHash().insert(nodeA->getUUID(), secretUUID);
|
||||
}
|
||||
|
||||
return secretUUID;
|
||||
}
|
||||
|
||||
return QUuid();
|
||||
}
|
||||
|
||||
void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) {
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
// setup the add packet for this new node
|
||||
QByteArray addNodePacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainServerAddedNode);
|
||||
QDataStream addNodeStream(&addNodePacket, QIODevice::Append);
|
||||
|
||||
addNodeStream << *addedNode.data();
|
||||
|
||||
int connectionSecretIndex = addNodePacket.size();
|
||||
|
||||
limitedNodeList->eachMatchingNode(
|
||||
[&](const SharedNodePointer& node)->bool {
|
||||
if (node->getLinkedData() && node->getActiveSocket() && node != addedNode) {
|
||||
// is the added Node in this node's interest list?
|
||||
DomainServerNodeData* nodeData = dynamic_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
return nodeData->getNodeInterestSet().contains(addedNode->getType());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[&](const SharedNodePointer& node) {
|
||||
QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122();
|
||||
|
||||
// replace the bytes at the end of the packet for the connection secret between these nodes
|
||||
addNodePacket.replace(connectionSecretIndex, NUM_BYTES_RFC4122_UUID, rfcConnectionSecret);
|
||||
|
||||
// send off this packet to the node
|
||||
limitedNodeList->writeUnverifiedDatagram(addNodePacket, node);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void DomainServer::readAvailableDatagrams() {
|
||||
|
@ -1216,10 +1241,6 @@ void DomainServer::transactionJSONCallback(const QJsonObject& data) {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::requestCurrentPublicSocketViaSTUN() {
|
||||
DependencyManager::get<LimitedNodeList>()->sendSTUNRequest();
|
||||
}
|
||||
|
||||
QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) {
|
||||
const QString SOCKET_NETWORK_ADDRESS_KEY = "network_address";
|
||||
const QString SOCKET_PORT_KEY = "port";
|
||||
|
@ -1284,82 +1305,84 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) {
|
|||
domainUpdateJSON.toUtf8());
|
||||
}
|
||||
|
||||
// todo: have data-web respond with ice-server hostname to use
|
||||
|
||||
void DomainServer::performICEUpdates() {
|
||||
sendHeartbeatToIceServer();
|
||||
sendICEPingPackets();
|
||||
}
|
||||
// TODO: have data-web respond with ice-server hostname to use
|
||||
|
||||
void DomainServer::sendHeartbeatToIceServer() {
|
||||
DependencyManager::get<LimitedNodeList>()->sendHeartbeatToIceServer(_iceServerSocket);
|
||||
}
|
||||
|
||||
void DomainServer::sendICEPingPackets() {
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
const int NUM_PEER_PINGS_BEFORE_DELETE = 2000 / UDP_PUNCH_PING_INTERVAL_MS;
|
||||
|
||||
QHash<QUuid, NetworkPeer>::iterator peer = _connectingICEPeers.begin();
|
||||
void DomainServer::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) {
|
||||
|
||||
while (peer != _connectingICEPeers.end()) {
|
||||
if (peer->getConnectionAttempts() > 0 && peer->getConnectionAttempts() % NUM_PEER_PINGS_BEFORE_DELETE == 0) {
|
||||
// we've reached the maximum number of ping attempts
|
||||
qDebug() << "Maximum number of ping attempts reached for peer with ID" << peer->getUUID();
|
||||
qDebug() << "Removing from list of connecting peers.";
|
||||
|
||||
if (peer->getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) {
|
||||
// we've already tried to connect to this peer enough times
|
||||
// remove it from our list - if it wants to re-connect it'll come back through ice-server
|
||||
peer = _connectingICEPeers.erase(peer);
|
||||
} else {
|
||||
// send ping packets to this peer's interfaces
|
||||
qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID"
|
||||
<< peer->getUUID();
|
||||
_icePeers.remove(peer->getUUID());
|
||||
} else {
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
QByteArray localPingPacket = nodeList->constructPingPacket(PingType::Local, false);
|
||||
nodeList->writeUnverifiedDatagram(localPingPacket, peer->getLocalSocket());
|
||||
// send the ping packet to the local and public sockets for this node
|
||||
QByteArray localPingPacket = nodeList->constructPingPacket(PingType::Local, false);
|
||||
nodeList->writeUnverifiedDatagram(localPingPacket, peer->getLocalSocket());
|
||||
|
||||
QByteArray publicPingPacket = nodeList->constructPingPacket(PingType::Public, false);
|
||||
nodeList->writeUnverifiedDatagram(publicPingPacket, peer->getPublicSocket());
|
||||
QByteArray publicPingPacket = nodeList->constructPingPacket(PingType::Public, false);
|
||||
nodeList->writeUnverifiedDatagram(publicPingPacket, peer->getPublicSocket());
|
||||
|
||||
peer->incrementConnectionAttempts();
|
||||
peer->incrementConnectionAttempts();
|
||||
}
|
||||
}
|
||||
|
||||
// go to next peer in hash
|
||||
++peer;
|
||||
void DomainServer::handlePeerPingTimeout() {
|
||||
NetworkPeer* senderPeer = qobject_cast<NetworkPeer*>(sender());
|
||||
|
||||
if (senderPeer) {
|
||||
SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID());
|
||||
|
||||
if (sharedPeer && !sharedPeer->getActiveSocket()) {
|
||||
pingPunchForConnectingPeer(sharedPeer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processICEHeartbeatResponse(const QByteArray& packet) {
|
||||
void DomainServer::processICEPeerInformation(const QByteArray& packet) {
|
||||
// loop through the packet and pull out network peers
|
||||
// any peer we don't have we add to the hash, otherwise we update
|
||||
QDataStream iceResponseStream(packet);
|
||||
iceResponseStream.skipRawData(numBytesForPacketHeader(packet));
|
||||
|
||||
NetworkPeer receivedPeer;
|
||||
NetworkPeer* receivedPeer = new NetworkPeer;
|
||||
iceResponseStream >> *receivedPeer;
|
||||
|
||||
while (!iceResponseStream.atEnd()) {
|
||||
iceResponseStream >> receivedPeer;
|
||||
if (!_icePeers.contains(receivedPeer->getUUID())) {
|
||||
qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer;
|
||||
SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer);
|
||||
_icePeers[receivedPeer->getUUID()] = newPeer;
|
||||
|
||||
if (!_connectedICEPeers.contains(receivedPeer.getUUID())) {
|
||||
if (!_connectingICEPeers.contains(receivedPeer.getUUID())) {
|
||||
qDebug() << "New peer requesting connection being added to hash -" << receivedPeer;
|
||||
}
|
||||
// make sure we know when we should ping this peer
|
||||
connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainServer::handlePeerPingTimeout);
|
||||
|
||||
_connectingICEPeers[receivedPeer.getUUID()] = receivedPeer;
|
||||
}
|
||||
// immediately ping the new peer, and start a timer to continue pinging it until we connect to it
|
||||
newPeer->startPingTimer();
|
||||
|
||||
qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID"
|
||||
<< newPeer->getUUID();
|
||||
|
||||
pingPunchForConnectingPeer(newPeer);
|
||||
} else {
|
||||
delete receivedPeer;
|
||||
}
|
||||
}
|
||||
|
||||
void DomainServer::processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr) {
|
||||
QUuid nodeUUID = uuidFromPacketHeader(packet);
|
||||
NetworkPeer sendingPeer = _connectingICEPeers.take(nodeUUID);
|
||||
SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID);
|
||||
|
||||
if (!sendingPeer.isNull()) {
|
||||
if (sendingPeer) {
|
||||
// we had this NetworkPeer in our connecting list - add the right sock addr to our connected list
|
||||
if (senderSockAddr == sendingPeer.getLocalSocket()) {
|
||||
qDebug() << "Activating local socket for communication with network peer -" << sendingPeer;
|
||||
_connectedICEPeers.insert(nodeUUID, sendingPeer.getLocalSocket());
|
||||
} else if (senderSockAddr == sendingPeer.getPublicSocket()) {
|
||||
qDebug() << "Activating public socket for communication with network peer -" << sendingPeer;
|
||||
_connectedICEPeers.insert(nodeUUID, sendingPeer.getPublicSocket());
|
||||
}
|
||||
sendingPeer->activateMatchingOrNewSymmetricSocket(senderSockAddr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1427,8 +1450,8 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS
|
|||
processICEPingReply(receivedPacket, senderSockAddr);
|
||||
break;
|
||||
}
|
||||
case PacketTypeIceServerHeartbeatResponse:
|
||||
processICEHeartbeatResponse(receivedPacket);
|
||||
case PacketTypeIceServerPeerInformation:
|
||||
processICEPeerInformation(receivedPacket);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -2087,9 +2110,8 @@ void DomainServer::nodeAdded(SharedNodePointer node) {
|
|||
|
||||
void DomainServer::nodeKilled(SharedNodePointer node) {
|
||||
|
||||
// remove this node from the connecting / connected ICE lists (if they exist)
|
||||
_connectingICEPeers.remove(node->getUUID());
|
||||
_connectedICEPeers.remove(node->getUUID());
|
||||
// if this peer connected via ICE then remove them from our ICE peers hash
|
||||
_icePeers.remove(node->getUUID());
|
||||
|
||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
|
|
|
@ -57,17 +57,17 @@ public slots:
|
|||
void restart();
|
||||
|
||||
private slots:
|
||||
void aboutToQuit();
|
||||
|
||||
void loginFailed();
|
||||
void readAvailableDatagrams();
|
||||
void setupPendingAssignmentCredits();
|
||||
void sendPendingTransactionsToServer();
|
||||
|
||||
void requestCurrentPublicSocketViaSTUN();
|
||||
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||
void performICEUpdates();
|
||||
void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); }
|
||||
void sendHeartbeatToIceServer();
|
||||
void sendICEPingPackets();
|
||||
void handlePeerPingTimeout();
|
||||
private:
|
||||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||
bool optionallySetupOAuth();
|
||||
|
@ -80,7 +80,9 @@ private:
|
|||
void setupAutomaticNetworking();
|
||||
void sendHeartbeatToDataServer(const QString& networkAddress);
|
||||
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||
void processICEHeartbeatResponse(const QByteArray& packet);
|
||||
void processICEPeerInformation(const QByteArray& packet);
|
||||
|
||||
void pingPunchForConnectingPeer(const SharedNetworkPeer& peer);
|
||||
|
||||
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
|
@ -98,9 +100,11 @@ private:
|
|||
HifiSockAddr& publicSockAddr,
|
||||
HifiSockAddr& localSockAddr,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes);
|
||||
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr,
|
||||
const NodeSet& nodeInterestList);
|
||||
const NodeSet& nodeInterestSet);
|
||||
|
||||
QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB);
|
||||
void broadcastNewNode(const SharedNodePointer& node);
|
||||
|
||||
void parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes);
|
||||
void addStaticAssignmentToAssignmentHash(Assignment* newAssignment);
|
||||
|
@ -151,8 +155,7 @@ private:
|
|||
|
||||
QHash<QString, QByteArray> _userPublicKeys;
|
||||
|
||||
QHash<QUuid, NetworkPeer> _connectingICEPeers;
|
||||
QHash<QUuid, HifiSockAddr> _connectedICEPeers;
|
||||
QHash<QUuid, SharedNetworkPeer> _icePeers;
|
||||
|
||||
QString _automaticNetworkingSetting;
|
||||
|
||||
|
|
|
@ -12,44 +12,47 @@
|
|||
#ifndef hifi_DomainServerNodeData_h
|
||||
#define hifi_DomainServerNodeData_h
|
||||
|
||||
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
#include <HifiSockAddr.h>
|
||||
#include <NodeData.h>
|
||||
#include <NodeType.h>
|
||||
|
||||
class DomainServerNodeData : public NodeData {
|
||||
public:
|
||||
DomainServerNodeData();
|
||||
int parseData(const QByteArray& packet) { return 0; }
|
||||
|
||||
|
||||
const QJsonObject& getStatsJSONObject() const { return _statsJSONObject; }
|
||||
|
||||
|
||||
void parseJSONStatsPacket(const QByteArray& statsPacket);
|
||||
|
||||
|
||||
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
|
||||
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
|
||||
|
||||
|
||||
void setWalletUUID(const QUuid& walletUUID) { _walletUUID = walletUUID; }
|
||||
const QUuid& getWalletUUID() const { return _walletUUID; }
|
||||
|
||||
|
||||
void setUsername(const QString& username) { _username = username; }
|
||||
const QString& getUsername() const { return _username; }
|
||||
|
||||
|
||||
QElapsedTimer& getPaymentIntervalTimer() { return _paymentIntervalTimer; }
|
||||
|
||||
|
||||
void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; }
|
||||
const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; }
|
||||
|
||||
|
||||
void setIsAuthenticated(bool isAuthenticated) { _isAuthenticated = isAuthenticated; }
|
||||
bool isAuthenticated() const { return _isAuthenticated; }
|
||||
|
||||
|
||||
QHash<QUuid, QUuid>& getSessionSecretHash() { return _sessionSecretHash; }
|
||||
|
||||
const NodeSet& getNodeInterestSet() const { return _nodeInterestSet; }
|
||||
void setNodeInterestSet(const NodeSet& nodeInterestSet) { _nodeInterestSet = nodeInterestSet; }
|
||||
private:
|
||||
QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject);
|
||||
|
||||
|
||||
QHash<QUuid, QUuid> _sessionSecretHash;
|
||||
QUuid _assignmentUUID;
|
||||
QUuid _walletUUID;
|
||||
|
@ -58,6 +61,7 @@ private:
|
|||
QJsonObject _statsJSONObject;
|
||||
HifiSockAddr _sendingSockAddr;
|
||||
bool _isAuthenticated;
|
||||
NodeSet _nodeInterestSet;
|
||||
};
|
||||
|
||||
#endif // hifi_DomainServerNodeData_h
|
||||
|
|
|
@ -26,18 +26,16 @@ int main(int argc, char* argv[]) {
|
|||
#ifndef WIN32
|
||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||
#endif
|
||||
|
||||
qInstallMessageHandler(LogHandler::verboseMessageHandler);
|
||||
|
||||
|
||||
int currentExitCode = 0;
|
||||
|
||||
|
||||
// use a do-while to handle domain-server restart
|
||||
do {
|
||||
DomainServer domainServer(argc, argv);
|
||||
currentExitCode = domainServer.exec();
|
||||
} while (currentExitCode == DomainServer::EXIT_CODE_REBOOT);
|
||||
|
||||
|
||||
|
||||
|
||||
return currentExitCode;
|
||||
}
|
||||
|
||||
|
|
201
examples/blockWorld.js
Normal file
201
examples/blockWorld.js
Normal file
|
@ -0,0 +1,201 @@
|
|||
// blockWorld.js
|
||||
// examples
|
||||
//
|
||||
// Created by Eric Levin on May 26, 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Creates a floor of tiles and then drops planky blocks at random points above the tile floor
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var TILE_SIZE = 7
|
||||
var GENERATE_INTERVAL = 50;
|
||||
var NUM_ROWS = 10;
|
||||
var angVelRange = 4;
|
||||
|
||||
var floorTiles = [];
|
||||
var blocks = [];
|
||||
var blockSpawner;
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
|
||||
|
||||
var floorPos = Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: -2,
|
||||
z: 0
|
||||
});
|
||||
var x = floorPos.x;
|
||||
|
||||
var currentRowIndex = 0;
|
||||
var currentColumnIndex = 0;
|
||||
|
||||
var DROP_HEIGHT = floorPos.y + 5;
|
||||
var BLOCK_GRAVITY = {
|
||||
x: 0,
|
||||
y: -9,
|
||||
z: 0
|
||||
};
|
||||
var BLOCK_SIZE = {
|
||||
x: 0.2,
|
||||
y: 0.1,
|
||||
z: 0.8
|
||||
};
|
||||
|
||||
var bounds = {
|
||||
xMin: floorPos.x,
|
||||
xMax: floorPos.x + (TILE_SIZE * NUM_ROWS) - TILE_SIZE,
|
||||
zMin: floorPos.z,
|
||||
zMax: floorPos.z + (TILE_SIZE * NUM_ROWS) - TILE_SIZE
|
||||
};
|
||||
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
|
||||
var BUTTON_SIZE = 32;
|
||||
var PADDING = 3;
|
||||
|
||||
var offButton = Overlays.addOverlay("image", {
|
||||
x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING,
|
||||
y: screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/close.png",
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var deleteButton = Overlays.addOverlay("image", {
|
||||
x: screenSize.x / 2 - BUTTON_SIZE,
|
||||
y: screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/delete.png",
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
|
||||
function generateFloor() {
|
||||
for (var z = floorPos.z; currentColumnIndex < NUM_ROWS; z += TILE_SIZE, currentColumnIndex++) {
|
||||
floorTiles.push(Entities.addEntity({
|
||||
type: 'Box',
|
||||
position: {
|
||||
x: x,
|
||||
y: floorPos.y,
|
||||
z: z
|
||||
},
|
||||
dimensions: {
|
||||
x: TILE_SIZE,
|
||||
y: 2,
|
||||
z: TILE_SIZE
|
||||
},
|
||||
color: {
|
||||
red: randFloat(70, 120),
|
||||
green: randFloat(70, 71),
|
||||
blue: randFloat(70, 80)
|
||||
},
|
||||
// collisionsWillMove: true
|
||||
}));
|
||||
}
|
||||
|
||||
currentRowIndex++;
|
||||
if (currentRowIndex < NUM_ROWS) {
|
||||
currentColumnIndex = 0;
|
||||
x += TILE_SIZE;
|
||||
Script.setTimeout(generateFloor, GENERATE_INTERVAL);
|
||||
} else {
|
||||
//Once we're done generating floor, drop planky blocks at random points on floor
|
||||
blockSpawner = Script.setInterval(function() {
|
||||
dropBlock();
|
||||
}, GENERATE_INTERVAL)
|
||||
}
|
||||
}
|
||||
|
||||
function dropBlock() {
|
||||
var dropPos = floorPos;
|
||||
dropPos.y = DROP_HEIGHT;
|
||||
dropPos.x = randFloat(bounds.xMin, bounds.xMax);
|
||||
dropPos.z = randFloat(bounds.zMin, bounds.zMax);
|
||||
blocks.push(Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: 'http://s3.amazonaws.com/hifi-public/marketplace/hificontent/Games/blocks/block.fbx',
|
||||
shapeType: 'box',
|
||||
position: dropPos,
|
||||
dimensions: BLOCK_SIZE,
|
||||
collisionsWillMove: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: .1,
|
||||
z: 0
|
||||
},
|
||||
angularVelocity: {
|
||||
x: randFloat(-angVelRange, angVelRange),
|
||||
y: randFloat(-angVelRange, angVelRange),
|
||||
z: randFloat(-angVelRange, angVelRange),
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({
|
||||
x: event.x,
|
||||
y: event.y
|
||||
});
|
||||
if (clickedOverlay == offButton) {
|
||||
Script.clearInterval(blockSpawner);
|
||||
}
|
||||
if(clickedOverlay == deleteButton){
|
||||
destroyStuff();
|
||||
}
|
||||
}
|
||||
|
||||
generateFloor();
|
||||
|
||||
function cleanup() {
|
||||
// for (var i = 0; i < floorTiles.length; i++) {
|
||||
// Entities.deleteEntity(floorTiles[i]);
|
||||
// }
|
||||
// for (var i = 0; i < blocks.length; i++) {
|
||||
// Entities.deleteEntity(blocks[i]);
|
||||
// }
|
||||
Overlays.deleteOverlay(offButton);
|
||||
Overlays.deleteOverlay(deleteButton)
|
||||
Script.clearInterval(blockSpawner);
|
||||
}
|
||||
|
||||
function destroyStuff() {
|
||||
for (var i = 0; i < floorTiles.length; i++) {
|
||||
Entities.deleteEntity(floorTiles[i]);
|
||||
}
|
||||
for (var i = 0; i < blocks.length; i++) {
|
||||
Entities.deleteEntity(blocks[i]);
|
||||
}
|
||||
Script.clearInterval(blockSpawner);
|
||||
|
||||
}
|
||||
|
||||
function randFloat(low, high) {
|
||||
return Math.floor(low + Math.random() * (high - low));
|
||||
}
|
||||
|
||||
function map(value, min1, max1, min2, max2) {
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
|
@ -153,6 +153,26 @@ if (showScore) {
|
|||
|
||||
var BULLET_VELOCITY = 10.0;
|
||||
|
||||
function entityCollisionWithEntity(entity1, entity2, collision) {
|
||||
if (entity2 === targetID) {
|
||||
score++;
|
||||
if (showScore) {
|
||||
Overlays.editOverlay(text, { text: "Score: " + score } );
|
||||
}
|
||||
|
||||
// We will delete the bullet and target in 1/2 sec, but for now we can see them bounce!
|
||||
Script.setTimeout(deleteBulletAndTarget, 500);
|
||||
|
||||
// Turn the target and the bullet white
|
||||
Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }});
|
||||
Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }});
|
||||
|
||||
// play the sound near the camera so the shooter can hear it
|
||||
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
|
||||
Audio.playSound(targetHitSound, audioOptions);
|
||||
}
|
||||
}
|
||||
|
||||
function shootBullet(position, velocity, grenade) {
|
||||
var BULLET_SIZE = 0.10;
|
||||
var BULLET_LIFETIME = 10.0;
|
||||
|
@ -178,6 +198,7 @@ function shootBullet(position, velocity, grenade) {
|
|||
ignoreCollisions: false,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity);
|
||||
|
||||
// Play firing sounds
|
||||
audioOptions.position = position;
|
||||
|
@ -310,27 +331,6 @@ function makePlatform(gravity, scale, size) {
|
|||
|
||||
}
|
||||
|
||||
function entityCollisionWithEntity(entity1, entity2, collision) {
|
||||
if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) &&
|
||||
((entity2.id == bulletID.id) || (entity2.id == targetID.id))) {
|
||||
score++;
|
||||
if (showScore) {
|
||||
Overlays.editOverlay(text, { text: "Score: " + score } );
|
||||
}
|
||||
|
||||
// We will delete the bullet and target in 1/2 sec, but for now we can see them bounce!
|
||||
Script.setTimeout(deleteBulletAndTarget, 500);
|
||||
|
||||
// Turn the target and the bullet white
|
||||
Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }});
|
||||
Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }});
|
||||
|
||||
// play the sound near the camera so the shooter can hear it
|
||||
audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
|
||||
Audio.playSound(targetHitSound, audioOptions);
|
||||
}
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
// if our tools are off, then don't do anything
|
||||
if (event.text == "t") {
|
||||
|
@ -505,7 +505,6 @@ function scriptEnding() {
|
|||
clearPose();
|
||||
}
|
||||
|
||||
Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Script.update.connect(update);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
|
|
247
examples/example/brownianFun.js
Normal file
247
examples/example/brownianFun.js
Normal file
|
@ -0,0 +1,247 @@
|
|||
var HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
var SOUND_PATH = HIFI_PUBLIC_BUCKET + "sounds/Collisions-hitsandslaps/";
|
||||
var soundURLS = ["67LCollision01.wav", "67LCollision02.wav", "airhockey_hit1.wav"];
|
||||
var FLOOR_SIZE = 10;
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(FLOOR_SIZE * 1.5, Quat.getFront(Camera.getOrientation())));
|
||||
var WALL_WIDTH = .1;
|
||||
var FLOOR_HEIGHT_OFFSET = -2;
|
||||
var WALL_HEIGHT = FLOOR_SIZE / 4;
|
||||
var BALL_DROP_HEIGHT = center.y + WALL_HEIGHT;
|
||||
var NUM_BALLS = 50;
|
||||
var BALL_RADIUS = 1;
|
||||
var BROWNIAN_INTERVAL_TIME = 16;
|
||||
var BROWNIAN_FORCE_RANGE = 5;
|
||||
var SPAWN_TIME = 50;
|
||||
var spawnCount = 0;
|
||||
|
||||
var brownianMotionActivated = false;
|
||||
var buttonOffColor = {
|
||||
red: 250,
|
||||
green: 10,
|
||||
blue: 10
|
||||
};
|
||||
var buttonOnColor = {
|
||||
red: 10,
|
||||
green: 200,
|
||||
blue: 100
|
||||
};
|
||||
|
||||
var bounds = {
|
||||
xMin: center.x - FLOOR_SIZE / 2,
|
||||
xMax: center.x + FLOOR_SIZE / 2,
|
||||
zMin: center.z - FLOOR_SIZE / 2,
|
||||
zMax: center.z + FLOOR_SIZE / 2
|
||||
};
|
||||
var balls = [];
|
||||
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
var BUTTON_SIZE = 32;
|
||||
var PADDING = 3;
|
||||
|
||||
var brownianButton = Overlays.addOverlay("image", {
|
||||
x: screenSize.x / 2 - BUTTON_SIZE,
|
||||
y: screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/blocks.png",
|
||||
color: buttonOffColor,
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var floor = Entities.addEntity({
|
||||
type: 'Box',
|
||||
position: Vec3.sum(center, {
|
||||
x: 0,
|
||||
y: FLOOR_HEIGHT_OFFSET,
|
||||
z: 0
|
||||
}),
|
||||
dimensions: {
|
||||
x: FLOOR_SIZE,
|
||||
y: WALL_WIDTH,
|
||||
z: FLOOR_SIZE
|
||||
},
|
||||
color: {
|
||||
red: 100,
|
||||
green: 100,
|
||||
blue: 100
|
||||
}
|
||||
});
|
||||
|
||||
var rightWall = Entities.addEntity({
|
||||
type: 'Box',
|
||||
position: Vec3.sum(center, {
|
||||
x: FLOOR_SIZE / 2,
|
||||
y: FLOOR_HEIGHT_OFFSET / 2,
|
||||
z: 0
|
||||
}),
|
||||
dimensions: {
|
||||
x: WALL_WIDTH,
|
||||
y: WALL_HEIGHT,
|
||||
z: FLOOR_SIZE
|
||||
},
|
||||
color: {
|
||||
red: 120,
|
||||
green: 100,
|
||||
blue: 120
|
||||
}
|
||||
});
|
||||
|
||||
var leftWall = Entities.addEntity({
|
||||
type: 'Box',
|
||||
position: Vec3.sum(center, {
|
||||
x: -FLOOR_SIZE / 2,
|
||||
y: FLOOR_HEIGHT_OFFSET / 2,
|
||||
z: 0
|
||||
}),
|
||||
dimensions: {
|
||||
x: WALL_WIDTH,
|
||||
y: WALL_HEIGHT,
|
||||
z: FLOOR_SIZE
|
||||
},
|
||||
color: {
|
||||
red: 120,
|
||||
green: 100,
|
||||
blue: 120
|
||||
}
|
||||
});
|
||||
|
||||
var backWall = Entities.addEntity({
|
||||
type: 'Box',
|
||||
position: Vec3.sum(center, {
|
||||
x: 0,
|
||||
y: FLOOR_HEIGHT_OFFSET / 2,
|
||||
z: -FLOOR_SIZE / 2,
|
||||
}),
|
||||
dimensions: {
|
||||
x: FLOOR_SIZE,
|
||||
y: WALL_HEIGHT,
|
||||
z: WALL_WIDTH
|
||||
},
|
||||
color: {
|
||||
red: 120,
|
||||
green: 100,
|
||||
blue: 120
|
||||
}
|
||||
});
|
||||
|
||||
var frontWall = Entities.addEntity({
|
||||
type: 'Box',
|
||||
position: Vec3.sum(center, {
|
||||
x: 0,
|
||||
y: FLOOR_HEIGHT_OFFSET / 2,
|
||||
z: FLOOR_SIZE / 2,
|
||||
}),
|
||||
dimensions: {
|
||||
x: FLOOR_SIZE,
|
||||
y: WALL_HEIGHT,
|
||||
z: WALL_WIDTH
|
||||
},
|
||||
color: {
|
||||
red: 120,
|
||||
green: 100,
|
||||
blue: 120
|
||||
}
|
||||
});
|
||||
|
||||
var spawnInterval = Script.setInterval(spawnBalls, SPAWN_TIME);
|
||||
|
||||
function spawnBalls() {
|
||||
balls.push(Entities.addEntity({
|
||||
type: "Sphere",
|
||||
shapeType: "sphere",
|
||||
position: {
|
||||
x: randFloat(bounds.xMin, bounds.xMax),
|
||||
y: BALL_DROP_HEIGHT,
|
||||
z: randFloat(bounds.zMin, bounds.zMax)
|
||||
},
|
||||
dimensions: {
|
||||
x: BALL_RADIUS,
|
||||
y: BALL_RADIUS,
|
||||
z: BALL_RADIUS
|
||||
},
|
||||
color: {
|
||||
red: randFloat(100, 150),
|
||||
green: randFloat(20, 80),
|
||||
blue: randFloat(10, 180)
|
||||
},
|
||||
ignoreCollisions: false,
|
||||
collisionsWillMove: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -9.9,
|
||||
z: 0
|
||||
},
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: -.25,
|
||||
z: 0
|
||||
},
|
||||
collisionSoundURL: SOUND_PATH + soundURLS[randInt(0, soundURLS.length - 1)]
|
||||
}));
|
||||
spawnCount++;
|
||||
if (spawnCount === NUM_BALLS) {
|
||||
Script.clearInterval(spawnInterval);
|
||||
}
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({
|
||||
x: event.x,
|
||||
y: event.y
|
||||
});
|
||||
if (clickedOverlay == brownianButton) {
|
||||
brownianMotionActivated = !brownianMotionActivated;
|
||||
if (brownianMotionActivated) {
|
||||
brownianInterval = Script.setInterval(bumpBalls, BROWNIAN_INTERVAL_TIME);
|
||||
Overlays.editOverlay(brownianButton, {
|
||||
color: buttonOnColor
|
||||
})
|
||||
} else {
|
||||
Script.clearInterval(brownianInterval);
|
||||
Overlays.editOverlay(brownianButton, {
|
||||
color: buttonOffColor
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bumpBalls() {
|
||||
balls.forEach(function(ball) {
|
||||
var props = Entities.getEntityProperties(ball);
|
||||
var newVelocity = Vec3.sum(props.velocity, {
|
||||
x: (Math.random() - 0.5) * BROWNIAN_FORCE_RANGE,
|
||||
y: 0,
|
||||
z: (Math.random() - 0.5) * BROWNIAN_FORCE_RANGE
|
||||
});
|
||||
Entities.editEntity(ball, {
|
||||
velocity: newVelocity
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(floor);
|
||||
Entities.deleteEntity(rightWall);
|
||||
Entities.deleteEntity(leftWall);
|
||||
Entities.deleteEntity(backWall);
|
||||
Entities.deleteEntity(frontWall);
|
||||
balls.forEach(function(ball) {
|
||||
Entities.deleteEntity(ball);
|
||||
});
|
||||
Overlays.deleteOverlay(brownianButton);
|
||||
}
|
||||
|
||||
function randFloat(low, high) {
|
||||
return Math.floor(low + Math.random() * (high - low));
|
||||
}
|
||||
|
||||
|
||||
function randInt(low, high) {
|
||||
return Math.floor(randFloat(low, high));
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
53
examples/example/entityCollisionExample.js
Normal file
53
examples/example/entityCollisionExample.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// entityCollisionExample.js
|
||||
// examples
|
||||
//
|
||||
// Created by Howard Stearns on 5/25/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// This is an example script that demonstrates use of the per-entity event handlers.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function someCollisionFunction(entityA, entityB, collision) {
|
||||
print("collision: " + JSON.stringify({a: entityA, b: entityB, c: collision}));
|
||||
}
|
||||
|
||||
var position = Vec3.sum(MyAvatar.position, Quat.getFront(MyAvatar.orientation));
|
||||
var properties = {
|
||||
type: "Box",
|
||||
position: position,
|
||||
collisionsWillMove: true,
|
||||
color: { red: 200, green: 0, blue: 0 }
|
||||
};
|
||||
var collider = Entities.addEntity(properties);
|
||||
var armed = false;
|
||||
function togglePrinting() {
|
||||
print('togglePrinting from ' + armed + ' on ' + collider);
|
||||
if (armed) {
|
||||
Script.removeEventHandler(collider, "collisionWithEntity", someCollisionFunction);
|
||||
} else {
|
||||
Script.addEventHandler(collider, "collisionWithEntity", someCollisionFunction);
|
||||
}
|
||||
armed = !armed;
|
||||
print("Red box " + (armed ? "will" : "will not") + " print on collision.");
|
||||
}
|
||||
togglePrinting();
|
||||
|
||||
properties.position.y += 0.2;
|
||||
properties.color.blue += 200;
|
||||
// A handy target for the collider to hit.
|
||||
var target = Entities.addEntity(properties);
|
||||
|
||||
properties.position.y += 0.2;
|
||||
properties.color.green += 200;
|
||||
var button = Entities.addEntity(properties);
|
||||
Script.addEventHandler(button, "clickReleaseOnEntity", togglePrinting);
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
Entities.deleteEntity(collider);
|
||||
Entities.deleteEntity(target);
|
||||
Entities.deleteEntity(button);
|
||||
});
|
|
@ -33,8 +33,8 @@ var cuePosition;
|
|||
var startStroke = 0;
|
||||
|
||||
// Sounds to use
|
||||
hitSounds = [];
|
||||
hitSounds.push(SoundCache.getSound(HIFI_PUBLIC_BUCKET + "Collisions-ballhitsandcatches/billiards/collision1.wav"));
|
||||
var hitSound = HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav";
|
||||
SoundCache.getSound(hitSound);
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
|
@ -127,6 +127,7 @@ function makeBalls(pos) {
|
|||
ignoreCollisions: false,
|
||||
damping: 0.50,
|
||||
shapeType: "sphere",
|
||||
collisionSoundURL: hitSound,
|
||||
collisionsWillMove: true }));
|
||||
ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE;
|
||||
ballNumber++;
|
||||
|
@ -225,26 +226,11 @@ function update(deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
function entityCollisionWithEntity(entity1, entity2, collision) {
|
||||
/*
|
||||
NOT WORKING YET
|
||||
if ((entity1.id == cueBall.id) || (entity2.id == cueBall.id)) {
|
||||
print("Cue ball collision!");
|
||||
//audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
|
||||
//Audio.playSound(hitSounds[0], { position: Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())) });
|
||||
}
|
||||
|
||||
else if (isObjectBall(entity1.id) || isObjectBall(entity2.id)) {
|
||||
print("Object ball collision");
|
||||
} */
|
||||
}
|
||||
|
||||
tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
makeTable(tableCenter);
|
||||
makeBalls(tableCenter);
|
||||
|
||||
Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity);
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
|
|
|
@ -29,31 +29,38 @@ var tablePosition = {
|
|||
z: 0
|
||||
}
|
||||
var isGrabbing = false;
|
||||
var isGrabbingPaddle = false;
|
||||
var grabbedEntity = null;
|
||||
var prevMouse = {};
|
||||
var prevMouse = {x: 0, y: 0};
|
||||
var deltaMouse = {
|
||||
z: 0
|
||||
}
|
||||
|
||||
var MAX_GRAB_DISTANCE = 100;
|
||||
var TABLE_SEARCH_RANGE = 10;
|
||||
var entityProps;
|
||||
var moveUpDown = false;
|
||||
var CLOSE_ENOUGH = 0.001;
|
||||
var FULL_STRENGTH = 1.0;
|
||||
var SPRING_RATE = 1.5;
|
||||
var SPRING_TIMESCALE = 0.05;
|
||||
var ANGULAR_SPRING_TIMESCALE = 0.03;
|
||||
var DAMPING_RATE = 0.80;
|
||||
var ANGULAR_DAMPING_RATE = 0.40;
|
||||
var SCREEN_TO_METERS = 0.001;
|
||||
var currentPosition, currentVelocity, cameraEntityDistance, currentRotation;
|
||||
var grabHeight;
|
||||
var initialVerticalGrabPosition;
|
||||
var MAX_VERTICAL_ANGLE = Math.PI / 3;
|
||||
var MIN_VERTICAL_ANGLE = - MAX_VERTICAL_ANGLE;
|
||||
var velocityTowardTarget, desiredVelocity, addedVelocity, newVelocity, dPosition, camYaw, distanceToTarget, targetPosition;
|
||||
var grabOffset;
|
||||
var originalGravity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
var shouldRotate = false;
|
||||
var dQ, theta, axisAngle, dT;
|
||||
var dQ, dT;
|
||||
var angularVelocity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
@ -94,6 +101,34 @@ function nearLinePoint(targetPosition) {
|
|||
return Vec3.sum(handPosition, along);
|
||||
}
|
||||
|
||||
function xzPickRayIntersetion(pointOnPlane, mouseX, mouseY) {
|
||||
var relativePoint = Vec3.subtract(pointOnPlane, Camera.getPosition());
|
||||
var pickRay = Camera.computePickRay(mouseX, mouseY);
|
||||
if (Math.abs(pickRay.direction.y) > 0.001) {
|
||||
var distance = relativePoint.y / pickRay.direction.y;
|
||||
if (distance < 0.001) {
|
||||
return pointOnPlane;
|
||||
}
|
||||
var pickInersection = Vec3.multiply(pickRay.direction, distance);
|
||||
pickInersection = Vec3.sum(Camera.getPosition(), pickInersection);
|
||||
return pickInersection;
|
||||
}
|
||||
// point and line are more-or-less co-planar: compute closest approach of pickRay and pointOnPlane
|
||||
var distance = Vec3.dot(relativePoint, pickRay.direction);
|
||||
var pickInersection = Vec3.multiply(pickRay.direction, distance);
|
||||
return pickInersection;
|
||||
}
|
||||
|
||||
function forwardPickRayIntersection(pointOnPlane, mouseX, mouseY) {
|
||||
var relativePoint = Vec3.subtract(pointOnPlane, Camera.getPosition());
|
||||
var pickRay = Camera.computePickRay(mouseX, mouseY);
|
||||
var planeNormal = Quat.getFront(Camera.getOrientation());
|
||||
var distance = Vec3.dot(planeNormal, relativePoint);
|
||||
var rayDistance = Vec3.dot(planeNormal, pickRay.direction);
|
||||
var pickIntersection = Vec3.multiply(pickRay.direction, distance / rayDistance);
|
||||
pickIntersection = Vec3.sum(pickIntersection, Camera.getPosition())
|
||||
return pickIntersection;
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (!event.isLeftButton) {
|
||||
|
@ -103,23 +138,35 @@ function mousePressEvent(event) {
|
|||
prevMouse.y = event.y;
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
|
||||
if (!intersection.intersects) {
|
||||
var pickResults = Entities.findRayIntersection(pickRay, true); // accurate picking
|
||||
if (!pickResults.intersects) {
|
||||
return;
|
||||
}
|
||||
if (intersection.properties.collisionsWillMove) {
|
||||
grabbedEntity = intersection.entityID;
|
||||
if (pickResults.properties.collisionsWillMove) {
|
||||
grabbedEntity = pickResults.entityID;
|
||||
var props = Entities.getEntityProperties(grabbedEntity)
|
||||
isGrabbing = true;
|
||||
originalGravity = props.gravity;
|
||||
targetPosition = props.position;
|
||||
var objectPosition = props.position;
|
||||
currentPosition = props.position;
|
||||
if (Vec3.distance(currentPosition, Camera.getPosition()) > MAX_GRAB_DISTANCE) {
|
||||
// don't allow grabs of things far away
|
||||
return;
|
||||
}
|
||||
|
||||
isGrabbing = true;
|
||||
isGrabbingPaddle = (props.name == "air-hockey-paddle-23j4h1jh82jsjfw91jf232n2k");
|
||||
|
||||
currentVelocity = props.velocity;
|
||||
updateDropLine(targetPosition);
|
||||
updateDropLine(objectPosition);
|
||||
|
||||
var pointOnPlane = xzPickRayIntersetion(objectPosition, event.x, event.y);
|
||||
grabOffset = Vec3.subtract(pointOnPlane, objectPosition);
|
||||
targetPosition = objectPosition;
|
||||
|
||||
// remember the height of the object when first grabbed
|
||||
// we'll try to maintain this height during the rest of this grab
|
||||
grabHeight = currentPosition.y;
|
||||
initialVerticalGrabPosition = currentPosition;
|
||||
|
||||
Entities.editEntity(grabbedEntity, {
|
||||
gravity: {
|
||||
|
@ -194,7 +241,6 @@ function mouseReleaseEvent() {
|
|||
}
|
||||
}
|
||||
|
||||
// new mouseMoveEvent
|
||||
function mouseMoveEvent(event) {
|
||||
if (isGrabbing) {
|
||||
// see if something added/restored gravity
|
||||
|
@ -204,71 +250,78 @@ function mouseMoveEvent(event) {
|
|||
}
|
||||
|
||||
if (shouldRotate) {
|
||||
targetPosition = currentPosition;
|
||||
deltaMouse.x = event.x - prevMouse.x;
|
||||
if (!moveUpDown) {
|
||||
deltaMouse.z = event.y - prevMouse.y;
|
||||
deltaMouse.y = 0;
|
||||
} else {
|
||||
deltaMouse.y = (event.y - prevMouse.y) * -1;
|
||||
deltaMouse.z = 0;
|
||||
}
|
||||
deltaMouse.z = event.y - prevMouse.y;
|
||||
deltaMouse.y = 0;
|
||||
|
||||
var transformedDeltaMouse = {
|
||||
x: deltaMouse.z,
|
||||
y: deltaMouse.x,
|
||||
z: deltaMouse.y
|
||||
};
|
||||
transformedDeltaMouse = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), transformedDeltaMouse);
|
||||
dQ = Quat.fromVec3Degrees(transformedDeltaMouse);
|
||||
theta = 2 * Math.acos(dQ.w);
|
||||
axisAngle = Quat.axis(dQ);
|
||||
angularVelocity = Vec3.multiply((theta / dT), axisAngle);
|
||||
var theta = 2 * Math.acos(dQ.w);
|
||||
var axis = Quat.axis(dQ);
|
||||
angularVelocity = Vec3.multiply((theta / ANGULAR_SPRING_TIMESCALE), axis);
|
||||
} else {
|
||||
var relativePosition = Vec3.subtract(currentPosition, Camera.getPosition());
|
||||
if (relativePosition.y < 0) {
|
||||
// grabee is below camera, so movement is valid
|
||||
// compute intersectionPoint where mouse ray hits grabee's current x-z plane
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var mousePosition = pickRay.direction;
|
||||
var length = relativePosition.y / mousePosition.y;
|
||||
mousePosition = Vec3.multiply(mousePosition, length);
|
||||
mousePosition = Vec3.sum(Camera.getPosition(), mousePosition);
|
||||
if (moveUpDown) {
|
||||
targetPosition = forwardPickRayIntersection(currentPosition, event.x, event.y);
|
||||
grabHeight = targetPosition.y;
|
||||
} else {
|
||||
var pointOnPlane = xzPickRayIntersetion(currentPosition, event.x, event.y);
|
||||
pointOnPlane = Vec3.subtract(pointOnPlane, grabOffset);
|
||||
|
||||
// translate mousePosition into local-frame
|
||||
mousePosition = Vec3.subtract(mousePosition, tablePosition);
|
||||
if (isGrabbingPaddle) {
|
||||
// translate pointOnPlane into local-frame
|
||||
pointOnPlane = Vec3.subtract(pointOnPlane, tablePosition);
|
||||
|
||||
// clamp local mousePosition to table field
|
||||
if (mousePosition.x > fieldMaxOffset.x) {
|
||||
mousePosition.x = fieldMaxOffset.x;
|
||||
} else if (mousePosition.x < fieldMinOffset.x) {
|
||||
mousePosition.x = fieldMinOffset.x;
|
||||
// clamp local pointOnPlane to table field
|
||||
if (pointOnPlane.x > fieldMaxOffset.x) {
|
||||
pointOnPlane.x = fieldMaxOffset.x;
|
||||
} else if (pointOnPlane.x < fieldMinOffset.x) {
|
||||
pointOnPlane.x = fieldMinOffset.x;
|
||||
}
|
||||
if (pointOnPlane.z > fieldMaxOffset.z) {
|
||||
pointOnPlane.z = fieldMaxOffset.z;
|
||||
} else if (pointOnPlane.z < fieldMinOffset.z) {
|
||||
pointOnPlane.z = fieldMinOffset.z;
|
||||
}
|
||||
|
||||
// clamp to rotated square (for cut corners)
|
||||
var rotation = Quat.angleAxis(45, { x:0, y:1, z:0 });
|
||||
pointOnPlane = Vec3.multiplyQbyV(rotation, pointOnPlane);
|
||||
if (pointOnPlane.x > halfCornerBoxWidth) {
|
||||
pointOnPlane.x = halfCornerBoxWidth;
|
||||
} else if (pointOnPlane.x < -halfCornerBoxWidth) {
|
||||
pointOnPlane.x = -halfCornerBoxWidth;
|
||||
}
|
||||
if (pointOnPlane.z > halfCornerBoxWidth) {
|
||||
pointOnPlane.z = halfCornerBoxWidth;
|
||||
} else if (pointOnPlane.z < -halfCornerBoxWidth) {
|
||||
pointOnPlane.z = -halfCornerBoxWidth;
|
||||
}
|
||||
// rotate back into local frame
|
||||
rotation.y = -rotation.y;
|
||||
pointOnPlane = Vec3.multiplyQbyV(rotation, pointOnPlane);
|
||||
|
||||
// translate into world-frame
|
||||
pointOnPlane = Vec3.sum(tablePosition, pointOnPlane);
|
||||
} else {
|
||||
// we're grabbing a non-paddle object
|
||||
|
||||
// clamp distance
|
||||
var relativePosition = Vec3.subtract(pointOnPlane, Camera.getPosition());
|
||||
var length = Vec3.length(relativePosition);
|
||||
if (length > MAX_GRAB_DISTANCE) {
|
||||
relativePosition = Vec3.multiply(relativePosition, MAX_GRAB_DISTANCE / length);
|
||||
pointOnPlane = Vec3.sum(relativePosition, Camera.getPosition());
|
||||
}
|
||||
}
|
||||
if (mousePosition.z > fieldMaxOffset.z) {
|
||||
mousePosition.z = fieldMaxOffset.z;
|
||||
} else if (mousePosition.z < fieldMinOffset.z) {
|
||||
mousePosition.z = fieldMinOffset.z;
|
||||
}
|
||||
|
||||
// clamp to rotated square (for cut corners)
|
||||
var rotation = Quat.angleAxis(45, { x:0, y:1, z:0 });
|
||||
mousePosition = Vec3.multiplyQbyV(rotation, mousePosition);
|
||||
if (mousePosition.x > halfCornerBoxWidth) {
|
||||
mousePosition.x = halfCornerBoxWidth;
|
||||
} else if (mousePosition.x < -halfCornerBoxWidth) {
|
||||
mousePosition.x = -halfCornerBoxWidth;
|
||||
}
|
||||
if (mousePosition.z > halfCornerBoxWidth) {
|
||||
mousePosition.z = halfCornerBoxWidth;
|
||||
} else if (mousePosition.z < -halfCornerBoxWidth) {
|
||||
mousePosition.z = -halfCornerBoxWidth;
|
||||
}
|
||||
// rotate back into local frame
|
||||
rotation.y = -rotation.y;
|
||||
mousePosition = Vec3.multiplyQbyV(rotation, mousePosition);
|
||||
// translate into world-frame
|
||||
mousePosition = Vec3.sum(tablePosition, mousePosition);
|
||||
|
||||
mousePosition.y = grabHeight;
|
||||
targetPosition = mousePosition;
|
||||
// clamp to grabHeight
|
||||
pointOnPlane.y = grabHeight;
|
||||
targetPosition = pointOnPlane;
|
||||
initialVerticalGrabPosition = targetPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -297,57 +350,32 @@ function keyPressEvent(event) {
|
|||
function update(deltaTime) {
|
||||
dT = deltaTime;
|
||||
if (isGrabbing) {
|
||||
|
||||
entityProps = Entities.getEntityProperties(grabbedEntity);
|
||||
currentPosition = entityProps.position;
|
||||
currentVelocity = entityProps.velocity;
|
||||
currentRotation = entityProps.rotation;
|
||||
|
||||
var dPosition = Vec3.subtract(targetPosition, currentPosition);
|
||||
|
||||
distanceToTarget = Vec3.length(dPosition);
|
||||
if (distanceToTarget > CLOSE_ENOUGH) {
|
||||
// compute current velocity in the direction we want to move
|
||||
velocityTowardTarget = Vec3.dot(currentVelocity, Vec3.normalize(dPosition));
|
||||
velocityTowardTarget = Vec3.multiply(Vec3.normalize(dPosition), velocityTowardTarget);
|
||||
// compute the speed we would like to be going toward the target position
|
||||
|
||||
desiredVelocity = Vec3.multiply(dPosition, (1.0 / deltaTime) * SPRING_RATE);
|
||||
// compute how much we want to add to the existing velocity
|
||||
addedVelocity = Vec3.subtract(desiredVelocity, velocityTowardTarget);
|
||||
// If target is too far, roll off the force as inverse square of distance
|
||||
if (distanceToTarget / cameraEntityDistance > FULL_STRENGTH) {
|
||||
addedVelocity = Vec3.multiply(addedVelocity, Math.pow(FULL_STRENGTH / distanceToTarget, 2.0));
|
||||
}
|
||||
newVelocity = Vec3.sum(currentVelocity, addedVelocity);
|
||||
// Add Damping
|
||||
newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE));
|
||||
// Update entity
|
||||
} else {
|
||||
newVelocity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
}
|
||||
if (shouldRotate) {
|
||||
angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE));
|
||||
Entities.editEntity(grabbedEntity, { angularVelocity: angularVelocity, });
|
||||
} else {
|
||||
angularVelocity = entityProps.angularVelocity;
|
||||
var dPosition = Vec3.subtract(targetPosition, currentPosition);
|
||||
var delta = Vec3.length(dPosition);
|
||||
if (delta > CLOSE_ENOUGH) {
|
||||
var MAX_POSITION_DELTA = 0.50;
|
||||
if (delta > MAX_POSITION_DELTA) {
|
||||
dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta);
|
||||
}
|
||||
// desired speed is proportional to displacement by the inverse of timescale
|
||||
// (for critically damped motion)
|
||||
newVelocity = Vec3.multiply(dPosition, 1.0 / SPRING_TIMESCALE);
|
||||
} else {
|
||||
newVelocity = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
}
|
||||
Entities.editEntity(grabbedEntity, { velocity: newVelocity, });
|
||||
}
|
||||
|
||||
// enforce that grabee's rotation is only about y-axis while being grabbed
|
||||
currentRotation.x = 0;
|
||||
currentRotation.z = 0;
|
||||
currentRotation.y = Math.sqrt(1.0 - currentRotation.w * currentRotation.w);
|
||||
// Hrm... slamming the currentRotation doesn't seem to work
|
||||
|
||||
Entities.editEntity(grabbedEntity, {
|
||||
position: currentPosition,
|
||||
rotation: currentRotation,
|
||||
velocity: newVelocity,
|
||||
angularVelocity: angularVelocity
|
||||
});
|
||||
updateDropLine(targetPosition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,16 @@ var gameOver = false;
|
|||
var invaderStepsPerCycle = 120; // the number of update steps it takes then invaders to move one column to the right
|
||||
var invaderStepOfCycle = 0; // current iteration in the cycle
|
||||
var invaderMoveDirection = 1; // 1 for moving to right, -1 for moving to left
|
||||
var LEFT = ",";
|
||||
var RIGHT = ".";
|
||||
var FIRE = "SPACE";
|
||||
var QUIT = "q";
|
||||
|
||||
print("Use:");
|
||||
print(LEFT + " to move left");
|
||||
print(RIGHT + " to move right");
|
||||
print(FIRE + " to fire");
|
||||
print(QUIT + " to quit");
|
||||
|
||||
// game length...
|
||||
var itemLifetimes = 60; // 1 minute
|
||||
|
@ -65,8 +75,8 @@ var myShipProperties;
|
|||
|
||||
// create the rows of space invaders
|
||||
var invaders = new Array();
|
||||
var numberOfRows = 5;
|
||||
var invadersPerRow = 8;
|
||||
var numberOfRows = 3 // FIXME 5;
|
||||
var invadersPerRow = 3 // FIXME 8;
|
||||
var emptyColumns = 2; // number of invader width columns not filled with invaders
|
||||
var invadersBottomCorner = { x: gameAt.x, y: middleY , z: gameAt.z };
|
||||
var rowHeight = ((gameAt.y + gameSize.y) - invadersBottomCorner.y) / numberOfRows;
|
||||
|
@ -80,7 +90,6 @@ var stepsToGround = (middleY - gameAt.y) / yPerStep;
|
|||
var maxInvaderRowOffset=stepsToGround;
|
||||
|
||||
// missile related items
|
||||
var missileFired = false;
|
||||
var myMissile;
|
||||
|
||||
// sounds
|
||||
|
@ -174,6 +183,7 @@ function initializeInvaders() {
|
|||
invaderPosition = getInvaderPosition(row, column);
|
||||
invaders[row][column] = Entities.addEntity({
|
||||
type: "Model",
|
||||
shapeType: "box",
|
||||
position: invaderPosition,
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
|
@ -181,6 +191,7 @@ function initializeInvaders() {
|
|||
dimensions: { x: invaderSize * 2, y: invaderSize * 2, z: invaderSize * 2 },
|
||||
color: { red: 255, green: 0, blue: 0 },
|
||||
modelURL: invaderModels[row].modelURL,
|
||||
collisionsWillMove: true,
|
||||
lifetime: itemLifetimes
|
||||
});
|
||||
}
|
||||
|
@ -264,17 +275,17 @@ Script.update.connect(update);
|
|||
function cleanupGame() {
|
||||
print("cleaning up game...");
|
||||
Entities.deleteEntity(myShip);
|
||||
print("cleanupGame() ... Entities.deleteEntity(myShip)... myShip.id="+myShip.id);
|
||||
print("cleanupGame() ... Entities.deleteEntity(myShip)... myShip="+myShip);
|
||||
for (var row = 0; row < numberOfRows; row++) {
|
||||
for (var column = 0; column < invadersPerRow; column++) {
|
||||
Entities.deleteEntity(invaders[row][column]);
|
||||
print("cleanupGame() ... Entities.deleteEntity(invaders[row][column])... invaders[row][column].id="
|
||||
+invaders[row][column].id);
|
||||
print("cleanupGame() ... Entities.deleteEntity(invaders[row][column])... invaders[row][column]="
|
||||
+invaders[row][column]);
|
||||
}
|
||||
}
|
||||
|
||||
// clean up our missile
|
||||
if (missileFired) {
|
||||
if (myMissile) {
|
||||
Entities.deleteEntity(myMissile);
|
||||
}
|
||||
|
||||
|
@ -293,15 +304,23 @@ function moveShipTo(position) {
|
|||
Entities.editEntity(myShip, { position: position });
|
||||
}
|
||||
|
||||
function entityCollisionWithEntity(entityA, entityB, collision) {
|
||||
print("entityCollisionWithEntity() a="+entityA + " b=" + entityB);
|
||||
Vec3.print('entityCollisionWithEntity() penetration=', collision.penetration);
|
||||
Vec3.print('entityCollisionWithEntity() contactPoint=', collision.contactPoint);
|
||||
|
||||
deleteIfInvader(entityB);
|
||||
}
|
||||
|
||||
function fireMissile() {
|
||||
// we only allow one missile at a time...
|
||||
var canFire = false;
|
||||
|
||||
// If we've fired a missile, then check to see if it's still alive
|
||||
if (missileFired) {
|
||||
if (myMissile) {
|
||||
var missileProperties = Entities.getEntityProperties(myMissile);
|
||||
|
||||
if (!missileProperties) {
|
||||
if (!missileProperties || (missileProperties.type === 'Unknown')) {
|
||||
print("canFire = true");
|
||||
canFire = true;
|
||||
}
|
||||
|
@ -322,11 +341,12 @@ function fireMissile() {
|
|||
velocity: { x: 0, y: 5, z: 0},
|
||||
gravity: { x: 0, y: 0, z: 0 },
|
||||
damping: 0,
|
||||
dimensions: { x: missileSize * 2, y: missileSize * 2, z: missileSize * 2 },
|
||||
collisionsWillMove: true,
|
||||
dimensions: { x: missileSize, y: missileSize, z: missileSize },
|
||||
color: { red: 0, green: 0, blue: 255 },
|
||||
lifetime: 5
|
||||
});
|
||||
|
||||
Script.addEventHandler(myMissile, "collisionWithEntity", entityCollisionWithEntity);
|
||||
var options = {}
|
||||
if (soundInMyHead) {
|
||||
options.position = { x: MyAvatar.position.x + 0.0,
|
||||
|
@ -335,30 +355,30 @@ function fireMissile() {
|
|||
} else {
|
||||
options.position = missilePosition;
|
||||
}
|
||||
|
||||
|
||||
Audio.playSound(shootSound, options);
|
||||
|
||||
missileFired = true;
|
||||
}
|
||||
}
|
||||
|
||||
function keyPressEvent(key) {
|
||||
//print("keyPressEvent key.text="+key.text);
|
||||
if (key.text == ",") {
|
||||
|
||||
if (key.text == LEFT) {
|
||||
myShipProperties.position.x -= 0.1;
|
||||
if (myShipProperties.position.x < gameAt.x) {
|
||||
myShipProperties.position.x = gameAt.x;
|
||||
}
|
||||
moveShipTo(myShipProperties.position);
|
||||
} else if (key.text == ".") {
|
||||
} else if (key.text == RIGHT) {
|
||||
myShipProperties.position.x += 0.1;
|
||||
if (myShipProperties.position.x > gameAt.x + gameSize.x) {
|
||||
myShipProperties.position.x = gameAt.x + gameSize.x;
|
||||
}
|
||||
moveShipTo(myShipProperties.position);
|
||||
} else if (key.text == "f") {
|
||||
} else if (key.text == FIRE) {
|
||||
fireMissile();
|
||||
} else if (key.text == "q") {
|
||||
} else if (key.text == QUIT) {
|
||||
endGame();
|
||||
}
|
||||
}
|
||||
|
@ -370,7 +390,7 @@ Controller.captureKeyEvents({text: " "});
|
|||
function deleteIfInvader(possibleInvaderEntity) {
|
||||
for (var row = 0; row < numberOfRows; row++) {
|
||||
for (var column = 0; column < invadersPerRow; column++) {
|
||||
if (invaders[row][column].id && invaders[row][column].id == possibleInvaderEntity.id) {
|
||||
if (invaders[row][column] == possibleInvaderEntity) {
|
||||
Entities.deleteEntity(possibleInvaderEntity);
|
||||
Entities.deleteEntity(myMissile);
|
||||
|
||||
|
@ -390,20 +410,6 @@ function deleteIfInvader(possibleInvaderEntity) {
|
|||
}
|
||||
}
|
||||
|
||||
function entityCollisionWithEntity(entityA, entityB, collision) {
|
||||
print("entityCollisionWithEntity() a.id="+entityA.id + " b.id=" + entityB.id);
|
||||
Vec3.print('entityCollisionWithEntity() penetration=', collision.penetration);
|
||||
Vec3.print('entityCollisionWithEntity() contactPoint=', collision.contactPoint);
|
||||
if (missileFired) {
|
||||
if (myMissile.id == entityA.id) {
|
||||
deleteIfInvader(entityB);
|
||||
} else if (myMissile.id == entityB.id) {
|
||||
deleteIfInvader(entityA);
|
||||
}
|
||||
}
|
||||
}
|
||||
Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity);
|
||||
|
||||
|
||||
// initialize the game...
|
||||
initializeMyShip();
|
||||
|
|
|
@ -12,17 +12,6 @@
|
|||
//
|
||||
|
||||
|
||||
print("hello...");
|
||||
|
||||
|
||||
function entityCollisionWithEntity(entityA, entityB, collision) {
|
||||
print("entityCollisionWithParticle()..");
|
||||
print(" entityA.getID()=" + entityA.id);
|
||||
print(" entityB.getID()=" + entityB.id);
|
||||
Vec3.print('penetration=', collision.penetration);
|
||||
Vec3.print('contactPoint=', collision.contactPoint);
|
||||
}
|
||||
|
||||
Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity);
|
||||
|
||||
print("here... hello...");
|
||||
print("The global collision event is obsolete. Please instead use:");
|
||||
print(" the collisionSoundURL property on entities, or");
|
||||
print(" entityCollisionExample.js");
|
||||
|
|
|
@ -36,8 +36,6 @@ var angularVelocity = {
|
|||
z: 0
|
||||
};
|
||||
|
||||
var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav");
|
||||
var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav");
|
||||
|
||||
var DROP_DISTANCE = 5.0;
|
||||
var DROP_COLOR = {
|
||||
|
@ -90,10 +88,6 @@ function mousePressEvent(event) {
|
|||
gravity: {x: 0, y: 0, z: 0}
|
||||
});
|
||||
|
||||
Audio.playSound(grabSound, {
|
||||
position: props.position,
|
||||
volume: 0.4
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,11 +129,6 @@ function mouseReleaseEvent() {
|
|||
});
|
||||
targetPosition = null;
|
||||
|
||||
Audio.playSound(grabSound, {
|
||||
position: entityProps.position,
|
||||
volume: 0.25
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,74 @@
|
|||
// pointer.js
|
||||
// examples
|
||||
//
|
||||
// Created by Eric Levin on May 26, 2015
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Provides a pointer with option to draw on surfaces
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var lineEntityID = null;
|
||||
var lineIsRezzed = false;
|
||||
var altHeld = false;
|
||||
var lineCreated = false;
|
||||
var position, positionOffset, prevPosition;
|
||||
var nearLinePosition;
|
||||
var strokes = [];
|
||||
var STROKE_ADJUST = 0.005;
|
||||
var DISTANCE_DRAW_THRESHOLD = .02;
|
||||
var drawDistance = 0;
|
||||
|
||||
function nearLinePoint(targetPosition) {
|
||||
var LINE_WIDTH = 20;
|
||||
|
||||
var userCanPoint = false;
|
||||
var userCanDraw = false;
|
||||
|
||||
var BUTTON_SIZE = 32;
|
||||
var PADDING = 3;
|
||||
|
||||
var buttonOffColor = {
|
||||
red: 250,
|
||||
green: 10,
|
||||
blue: 10
|
||||
};
|
||||
var buttonOnColor = {
|
||||
red: 10,
|
||||
green: 200,
|
||||
blue: 100
|
||||
};
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
|
||||
var drawButton = Overlays.addOverlay("image", {
|
||||
x: screenSize.x / 2 - BUTTON_SIZE + PADDING * 2,
|
||||
y: screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/pencil.png?v2",
|
||||
color: buttonOffColor,
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
var pointerButton = Overlays.addOverlay("image", {
|
||||
x: screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING,
|
||||
y: screenSize.y - (BUTTON_SIZE + PADDING),
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/laser.png",
|
||||
color: buttonOffColor,
|
||||
alpha: 1
|
||||
})
|
||||
|
||||
|
||||
|
||||
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(2.0, Quat.getFront(Camera.getOrientation())));
|
||||
center.y += 0.5;
|
||||
|
||||
function calculateNearLinePosition(targetPosition) {
|
||||
var handPosition = MyAvatar.getRightPalmPosition();
|
||||
var along = Vec3.subtract(targetPosition, handPosition);
|
||||
along = Vec3.normalize(along);
|
||||
|
@ -22,30 +87,39 @@ function removeLine() {
|
|||
|
||||
|
||||
function createOrUpdateLine(event) {
|
||||
if (!userCanPoint) {
|
||||
return;
|
||||
}
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
|
||||
var props = Entities.getEntityProperties(intersection.entityID);
|
||||
|
||||
if (intersection.intersects) {
|
||||
var dim = Vec3.subtract(intersection.intersection, nearLinePoint(intersection.intersection));
|
||||
startPosition = intersection.intersection;
|
||||
var subtractVec = Vec3.multiply(Vec3.normalize(pickRay.direction), STROKE_ADJUST);
|
||||
startPosition = Vec3.subtract(startPosition, subtractVec);
|
||||
nearLinePosition = calculateNearLinePosition(intersection.intersection);
|
||||
positionOffset = Vec3.subtract(startPosition, nearLinePosition);
|
||||
if (lineIsRezzed) {
|
||||
Entities.editEntity(lineEntityID, {
|
||||
position: nearLinePoint(intersection.intersection),
|
||||
dimensions: dim,
|
||||
lifetime: 15 + props.lifespan // renew lifetime
|
||||
position: nearLinePosition,
|
||||
dimensions: positionOffset,
|
||||
});
|
||||
if (userCanDraw) {
|
||||
draw();
|
||||
}
|
||||
} else {
|
||||
lineIsRezzed = true;
|
||||
prevPosition = startPosition;
|
||||
lineEntityID = Entities.addEntity({
|
||||
type: "Line",
|
||||
position: nearLinePoint(intersection.intersection),
|
||||
dimensions: dim,
|
||||
position: nearLinePosition,
|
||||
dimensions: positionOffset,
|
||||
color: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
},
|
||||
lifetime: 15 // if someone crashes while pointing, don't leave the line there forever.
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
@ -53,8 +127,69 @@ function createOrUpdateLine(event) {
|
|||
}
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
||||
//We only want to draw line if distance between starting and previous point is large enough
|
||||
drawDistance = Vec3.distance(startPosition, prevPosition);
|
||||
if (drawDistance < DISTANCE_DRAW_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
|
||||
var offset = Vec3.subtract(startPosition, prevPosition);
|
||||
strokes.push(Entities.addEntity({
|
||||
type: "Line",
|
||||
position: prevPosition,
|
||||
dimensions: offset,
|
||||
color: {
|
||||
red: 200,
|
||||
green: 40,
|
||||
blue: 200
|
||||
},
|
||||
lineWidth: LINE_WIDTH
|
||||
}));
|
||||
prevPosition = startPosition;
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({
|
||||
x: event.x,
|
||||
y: event.y
|
||||
});
|
||||
if (clickedOverlay == drawButton) {
|
||||
userCanDraw = !userCanDraw;
|
||||
if (userCanDraw === true) {
|
||||
Overlays.editOverlay(drawButton, {
|
||||
color: buttonOnColor
|
||||
});
|
||||
} else {
|
||||
Overlays.editOverlay(drawButton, {
|
||||
color: buttonOffColor
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (clickedOverlay == pointerButton) {
|
||||
userCanPoint = !userCanPoint;
|
||||
if (userCanPoint === true) {
|
||||
Overlays.editOverlay(pointerButton, {
|
||||
color: buttonOnColor
|
||||
});
|
||||
if (userCanDraw === true) {
|
||||
|
||||
Overlays.editOverlay(drawButton, {
|
||||
color: buttonOnColor
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Overlays.editOverlay(pointerButton, {
|
||||
color: buttonOffColor
|
||||
});
|
||||
Overlays.editOverlay(drawButton, {
|
||||
color: buttonOffColor
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!event.isLeftButton || altHeld) {
|
||||
return;
|
||||
}
|
||||
|
@ -64,11 +199,13 @@ function mousePressEvent(event) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
createOrUpdateLine(event);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
if (!lineCreated) {
|
||||
return;
|
||||
|
@ -91,7 +228,17 @@ function keyReleaseEvent(event) {
|
|||
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
for (var i = 0; i < strokes.length; i++) {
|
||||
Entities.deleteEntity(strokes[i]);
|
||||
}
|
||||
|
||||
Overlays.deleteOverlay(drawButton);
|
||||
Overlays.deleteOverlay(pointerButton);
|
||||
}
|
||||
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
|
||||
|
|
43
examples/voxels.js
Normal file
43
examples/voxels.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
|
||||
var controlHeld = false;
|
||||
|
||||
|
||||
function mousePressEvent(event) {
|
||||
if (!event.isLeftButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
|
||||
// var props = Entities.getEntityProperties(intersection.entityID);
|
||||
if (intersection.intersects) {
|
||||
var ids = Entities.findEntities(intersection.intersection, 10);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var id = ids[i];
|
||||
if (controlHeld) {
|
||||
Entities.setVoxelSphere(id, intersection.intersection, 1.2, 0);
|
||||
} else {
|
||||
Entities.setVoxelSphere(id, intersection.intersection, 1.2, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function keyPressEvent(event) {
|
||||
if (event.text == "CONTROL") {
|
||||
controlHeld = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
if (event.text == "CONTROL") {
|
||||
controlHeld = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
|
@ -33,133 +33,121 @@ IceServer::IceServer(int argc, char* argv[]) :
|
|||
qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT;
|
||||
qDebug() << "monitoring http endpoint is listening on " << ICE_SERVER_MONITORING_PORT;
|
||||
_serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT);
|
||||
|
||||
|
||||
// call our process datagrams slot when the UDP socket has packets ready
|
||||
connect(&_serverSocket, &QUdpSocket::readyRead, this, &IceServer::processDatagrams);
|
||||
|
||||
|
||||
// setup our timer to clear inactive peers
|
||||
QTimer* inactivePeerTimer = new QTimer(this);
|
||||
connect(inactivePeerTimer, &QTimer::timeout, this, &IceServer::clearInactivePeers);
|
||||
inactivePeerTimer->start(CLEAR_INACTIVE_PEERS_INTERVAL_MSECS);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void IceServer::processDatagrams() {
|
||||
HifiSockAddr sendingSockAddr;
|
||||
QByteArray incomingPacket;
|
||||
|
||||
|
||||
while (_serverSocket.hasPendingDatagrams()) {
|
||||
incomingPacket.resize(_serverSocket.pendingDatagramSize());
|
||||
|
||||
|
||||
_serverSocket.readDatagram(incomingPacket.data(), incomingPacket.size(),
|
||||
sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer());
|
||||
|
||||
|
||||
if (packetTypeForPacket(incomingPacket) == PacketTypeIceServerHeartbeat) {
|
||||
|
||||
PacketType packetType = packetTypeForPacket(incomingPacket);
|
||||
|
||||
if (packetType == PacketTypeIceServerHeartbeat) {
|
||||
SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(incomingPacket);
|
||||
|
||||
// so that we can send packets to the heartbeating peer when we need, we need to activate a socket now
|
||||
peer->activateMatchingOrNewSymmetricSocket(sendingSockAddr);
|
||||
} else if (packetType == PacketTypeIceServerQuery) {
|
||||
|
||||
// this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer?
|
||||
QUuid senderUUID = uuidFromPacketHeader(incomingPacket);
|
||||
|
||||
|
||||
// pull the public and private sock addrs for this peer
|
||||
HifiSockAddr publicSocket, localSocket;
|
||||
|
||||
|
||||
QDataStream hearbeatStream(incomingPacket);
|
||||
hearbeatStream.skipRawData(numBytesForPacketHeader(incomingPacket));
|
||||
|
||||
|
||||
hearbeatStream >> publicSocket >> localSocket;
|
||||
|
||||
// make sure we have this sender in our peer hash
|
||||
SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID);
|
||||
|
||||
if (!matchingPeer) {
|
||||
// if we don't have this sender we need to create them now
|
||||
matchingPeer = SharedNetworkPeer(new NetworkPeer(senderUUID, publicSocket, localSocket));
|
||||
_activePeers.insert(senderUUID, matchingPeer);
|
||||
|
||||
qDebug() << "Added a new network peer" << *matchingPeer;
|
||||
} else {
|
||||
// we already had the peer so just potentially update their sockets
|
||||
matchingPeer->setPublicSocket(publicSocket);
|
||||
matchingPeer->setLocalSocket(localSocket);
|
||||
|
||||
qDebug() << "Matched hearbeat to existing network peer" << *matchingPeer;
|
||||
}
|
||||
|
||||
// update our last heard microstamp for this network peer to now
|
||||
matchingPeer->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
|
||||
// check if this node also included a UUID that they would like to connect to
|
||||
QUuid connectRequestID;
|
||||
hearbeatStream >> connectRequestID;
|
||||
|
||||
// get the peers asking for connections with this peer
|
||||
QSet<QUuid>& requestingConnections = _currentConnections[senderUUID];
|
||||
|
||||
if (!connectRequestID.isNull()) {
|
||||
qDebug() << "Peer wants to connect to peer with ID" << uuidStringWithoutCurlyBraces(connectRequestID);
|
||||
|
||||
// ensure this peer is in the set of current connections for the peer with ID it wants to connect with
|
||||
_currentConnections[connectRequestID].insert(senderUUID);
|
||||
|
||||
// add the ID of the node they have said they would like to connect to
|
||||
requestingConnections.insert(connectRequestID);
|
||||
}
|
||||
|
||||
if (requestingConnections.size() > 0) {
|
||||
// send a heartbeart response based on the set of connections
|
||||
qDebug() << "Sending a heartbeat response to" << senderUUID << "who has" << requestingConnections.size()
|
||||
<< "potential connections";
|
||||
sendHeartbeatResponse(sendingSockAddr, requestingConnections);
|
||||
|
||||
SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID);
|
||||
|
||||
if (matchingPeer) {
|
||||
// we have the peer they want to connect to - send them pack the information for that peer
|
||||
sendPeerInformationPacket(*(matchingPeer.data()), &sendingSockAddr);
|
||||
|
||||
// we also need to send them to the active peer they are hoping to connect to
|
||||
// create a dummy peer object we can pass to sendPeerInformationPacket
|
||||
|
||||
NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket);
|
||||
sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IceServer::sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, QSet<QUuid>& connections) {
|
||||
QSet<QUuid>::iterator peerID = connections.begin();
|
||||
|
||||
SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(const QByteArray& incomingPacket) {
|
||||
QUuid senderUUID = uuidFromPacketHeader(incomingPacket);
|
||||
|
||||
// pull the public and private sock addrs for this peer
|
||||
HifiSockAddr publicSocket, localSocket;
|
||||
|
||||
QDataStream hearbeatStream(incomingPacket);
|
||||
hearbeatStream.skipRawData(numBytesForPacketHeader(incomingPacket));
|
||||
|
||||
hearbeatStream >> publicSocket >> localSocket;
|
||||
|
||||
// make sure we have this sender in our peer hash
|
||||
SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID);
|
||||
|
||||
if (!matchingPeer) {
|
||||
// if we don't have this sender we need to create them now
|
||||
matchingPeer = SharedNetworkPeer(new NetworkPeer(senderUUID, publicSocket, localSocket));
|
||||
_activePeers.insert(senderUUID, matchingPeer);
|
||||
|
||||
qDebug() << "Added a new network peer" << *matchingPeer;
|
||||
} else {
|
||||
// we already had the peer so just potentially update their sockets
|
||||
matchingPeer->setPublicSocket(publicSocket);
|
||||
matchingPeer->setLocalSocket(localSocket);
|
||||
}
|
||||
|
||||
// update our last heard microstamp for this network peer to now
|
||||
matchingPeer->setLastHeardMicrostamp(usecTimestampNow());
|
||||
|
||||
return matchingPeer;
|
||||
}
|
||||
|
||||
void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) {
|
||||
QByteArray outgoingPacket(MAX_PACKET_SIZE, 0);
|
||||
int currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id);
|
||||
int currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerPeerInformation, _id);
|
||||
int numHeaderBytes = currentPacketSize;
|
||||
|
||||
// go through the connections, sending packets containing connection information for those nodes
|
||||
while (peerID != connections.end()) {
|
||||
SharedNetworkPeer matchingPeer = _activePeers.value(*peerID);
|
||||
// if this node is inactive we remove it from the set
|
||||
if (!matchingPeer) {
|
||||
peerID = connections.erase(peerID);
|
||||
} else {
|
||||
// get the byte array for this peer
|
||||
QByteArray peerBytes = matchingPeer->toByteArray();
|
||||
|
||||
if (currentPacketSize + peerBytes.size() > MAX_PACKET_SIZE) {
|
||||
// write the current packet
|
||||
_serverSocket.writeDatagram(outgoingPacket.data(), currentPacketSize,
|
||||
destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||
|
||||
// reset the packet size to our number of header bytes
|
||||
currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id);
|
||||
}
|
||||
|
||||
// append the current peer bytes
|
||||
outgoingPacket.insert(currentPacketSize, peerBytes);
|
||||
currentPacketSize += peerBytes.size();
|
||||
|
||||
++peerID;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentPacketSize > numHeaderBytes) {
|
||||
// write the last packet, if there is data in it
|
||||
_serverSocket.writeDatagram(outgoingPacket.data(), currentPacketSize,
|
||||
destinationSockAddr.getAddress(), destinationSockAddr.getPort());
|
||||
}
|
||||
|
||||
// get the byte array for this peer
|
||||
QByteArray peerBytes = peer.toByteArray();
|
||||
outgoingPacket.replace(numHeaderBytes, peerBytes.size(), peerBytes);
|
||||
|
||||
currentPacketSize += peerBytes.size();
|
||||
|
||||
// write the current packet
|
||||
_serverSocket.writeDatagram(outgoingPacket.data(), outgoingPacket.size(),
|
||||
destinationSockAddr->getAddress(), destinationSockAddr->getPort());
|
||||
}
|
||||
|
||||
void IceServer::clearInactivePeers() {
|
||||
NetworkPeerHash::iterator peerItem = _activePeers.begin();
|
||||
|
||||
|
||||
while (peerItem != _activePeers.end()) {
|
||||
SharedNetworkPeer peer = peerItem.value();
|
||||
|
||||
|
||||
if ((usecTimestampNow() - peer->getLastHeardMicrostamp()) > (PEER_SILENCE_THRESHOLD_MSECS * 1000)) {
|
||||
qDebug() << "Removing peer from memory for inactivity -" << *peer;
|
||||
peerItem = _activePeers.erase(peerItem);
|
||||
|
@ -171,11 +159,9 @@ void IceServer::clearInactivePeers() {
|
|||
}
|
||||
|
||||
bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {
|
||||
//
|
||||
// We need an HTTP handler in order to monitor the health of the ice server
|
||||
// The correct functioning of the ICE server will be determined by its HTTP availability,
|
||||
//
|
||||
|
||||
|
||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
|
||||
if (url.path() == "/status") {
|
||||
connection->respond(HTTPConnection::StatusCode200, QByteArray::number(_activePeers.size()));
|
||||
|
|
|
@ -31,14 +31,14 @@ private slots:
|
|||
void processDatagrams();
|
||||
void clearInactivePeers();
|
||||
private:
|
||||
|
||||
void sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, QSet<QUuid>& connections);
|
||||
|
||||
|
||||
SharedNetworkPeer addOrUpdateHeartbeatingPeer(const QByteArray& incomingPacket);
|
||||
void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr);
|
||||
|
||||
QUuid _id;
|
||||
QUdpSocket _serverSocket;
|
||||
NetworkPeerHash _activePeers;
|
||||
QHash<QUuid, QSet<QUuid> > _currentConnections;
|
||||
HTTPManager _httpManager;
|
||||
};
|
||||
|
||||
#endif // hifi_IceServer_h
|
||||
#endif // hifi_IceServer_h
|
||||
|
|
|
@ -1948,7 +1948,9 @@ FaceTracker* Application::getSelectedFaceTracker() {
|
|||
}
|
||||
|
||||
void Application::setActiveFaceTracker() {
|
||||
#if defined(HAVE_FACESHIFT) || defined(HAVE_DDE)
|
||||
bool isMuted = Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking);
|
||||
#endif
|
||||
#ifdef HAVE_FACESHIFT
|
||||
auto faceshiftTracker = DependencyManager::get<Faceshift>();
|
||||
faceshiftTracker->setIsMuted(isMuted);
|
||||
|
@ -1974,7 +1976,7 @@ void Application::toggleFaceTrackerMute() {
|
|||
}
|
||||
|
||||
bool Application::exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs) {
|
||||
QVector<EntityItem*> entities;
|
||||
QVector<EntityItemPointer> entities;
|
||||
|
||||
auto entityTree = _entities.getTree();
|
||||
EntityTree exportTree;
|
||||
|
@ -2015,7 +2017,7 @@ bool Application::exportEntities(const QString& filename, const QVector<EntityIt
|
|||
}
|
||||
|
||||
bool Application::exportEntities(const QString& filename, float x, float y, float z, float scale) {
|
||||
QVector<EntityItem*> entities;
|
||||
QVector<EntityItemPointer> entities;
|
||||
_entities.getTree()->findEntities(AACube(glm::vec3(x, y, z), scale), entities);
|
||||
|
||||
if (entities.size() > 0) {
|
||||
|
@ -2159,9 +2161,6 @@ void Application::init() {
|
|||
|
||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||
|
||||
connect(&_entitySimulation, &EntitySimulation::entityCollisionWithEntity,
|
||||
entityScriptingInterface.data(), &EntityScriptingInterface::entityCollisionWithEntity);
|
||||
|
||||
// connect the _entityCollisionSystem to our EntityTreeRenderer since that's what handles running entity scripts
|
||||
connect(&_entitySimulation, &EntitySimulation::entityCollisionWithEntity,
|
||||
&_entities, &EntityTreeRenderer::entityCollisionWithEntity);
|
||||
|
@ -2184,6 +2183,9 @@ void Application::init() {
|
|||
// initialize the GlowEffect with our widget
|
||||
bool glow = Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect);
|
||||
DependencyManager::get<GlowEffect>()->init(glow);
|
||||
|
||||
// Make sure any new sounds are loaded as soon as know about them.
|
||||
connect(tree, &EntityTree::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
|
||||
}
|
||||
|
||||
void Application::closeMirrorView() {
|
||||
|
@ -2487,19 +2489,20 @@ void Application::update(float deltaTime) {
|
|||
_entitySimulation.unlock();
|
||||
|
||||
avatarManager->handleOutgoingChanges(_physicsEngine.getOutgoingChanges());
|
||||
avatarManager->handleCollisionEvents(_physicsEngine.getCollisionEvents());
|
||||
auto collisionEvents = _physicsEngine.getCollisionEvents();
|
||||
avatarManager->handleCollisionEvents(collisionEvents);
|
||||
|
||||
_physicsEngine.dumpStatsIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
PerformanceTimer perfTimer("entities");
|
||||
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk deadlock.)
|
||||
_entitySimulation.handleCollisionEvents(_physicsEngine.getCollisionEvents());
|
||||
// NOTE: the _entities.update() call below will wait for lock
|
||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||
_entities.update(); // update the models...
|
||||
if (!_aboutToQuit) {
|
||||
PerformanceTimer perfTimer("entities");
|
||||
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk deadlock.)
|
||||
_entitySimulation.handleCollisionEvents(collisionEvents);
|
||||
// NOTE: the _entities.update() call below will wait for lock
|
||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||
_entities.update(); // update the models...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -3100,20 +3103,27 @@ PickRay Application::computePickRay(float x, float y) const {
|
|||
QImage Application::renderAvatarBillboard() {
|
||||
auto primaryFramebuffer = DependencyManager::get<TextureCache>()->getPrimaryFramebuffer();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gpu::GLBackend::getFramebufferID(primaryFramebuffer));
|
||||
|
||||
|
||||
// clear the alpha channel so the background is transparent
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
|
||||
|
||||
// the "glow" here causes an alpha of one
|
||||
Glower glower;
|
||||
|
||||
|
||||
const int BILLBOARD_SIZE = 64;
|
||||
renderRearViewMirror(QRect(0, _glWidget->getDeviceHeight() - BILLBOARD_SIZE,
|
||||
BILLBOARD_SIZE, BILLBOARD_SIZE),
|
||||
true);
|
||||
|
||||
|
||||
QImage image(BILLBOARD_SIZE, BILLBOARD_SIZE, QImage::Format_ARGB32);
|
||||
glReadPixels(0, 0, BILLBOARD_SIZE, BILLBOARD_SIZE, GL_BGRA, GL_UNSIGNED_BYTE, image.bits());
|
||||
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
@ -3157,7 +3167,7 @@ const ViewFrustum* Application::getDisplayViewFrustum() const {
|
|||
return &_displayViewFrustum;
|
||||
}
|
||||
|
||||
void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs::RenderSide renderSide) {
|
||||
void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, bool billboard, RenderArgs::RenderSide renderSide) {
|
||||
activeRenderingThread = QThread::currentThread();
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
PerformanceTimer perfTimer("display");
|
||||
|
@ -3373,7 +3383,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
|||
false, selfAvatarOnly);
|
||||
}
|
||||
|
||||
{
|
||||
if (!billboard) {
|
||||
DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());
|
||||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity());
|
||||
|
@ -3576,7 +3586,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) {
|
|||
|
||||
// render rear mirror view
|
||||
glPushMatrix();
|
||||
displaySide(_mirrorCamera, true);
|
||||
displaySide(_mirrorCamera, true, billboard);
|
||||
glPopMatrix();
|
||||
|
||||
if (!billboard) {
|
||||
|
|
|
@ -270,7 +270,7 @@ public:
|
|||
|
||||
QImage renderAvatarBillboard();
|
||||
|
||||
void displaySide(Camera& whichCamera, bool selfAvatarOnly = false, RenderArgs::RenderSide renderSide = RenderArgs::MONO);
|
||||
void displaySide(Camera& whichCamera, bool selfAvatarOnly = false, bool billboard = false, RenderArgs::RenderSide renderSide = RenderArgs::MONO);
|
||||
|
||||
/// Stores the current modelview matrix as the untranslated view matrix to use for transforms and the supplied vector as
|
||||
/// the view matrix translation.
|
||||
|
|
|
@ -71,14 +71,14 @@ Menu::Menu() {
|
|||
|
||||
{
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Login);
|
||||
|
||||
|
||||
// connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item
|
||||
connect(&accountManager, &AccountManager::profileChanged,
|
||||
dialogsManager.data(), &DialogsManager::toggleLoginDialog);
|
||||
connect(&accountManager, &AccountManager::logoutComplete,
|
||||
dialogsManager.data(), &DialogsManager::toggleLoginDialog);
|
||||
}
|
||||
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Scripts");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O,
|
||||
qApp, SLOT(loadDialog()));
|
||||
|
@ -92,7 +92,7 @@ Menu::Menu() {
|
|||
|
||||
addDisabledActionAndSeparator(fileMenu, "Location");
|
||||
qApp->getBookmarks()->setupMenus(this, fileMenu);
|
||||
|
||||
|
||||
addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::AddressBar,
|
||||
Qt::CTRL | Qt::Key_L,
|
||||
|
@ -148,8 +148,8 @@ Menu::Menu() {
|
|||
SLOT(setEnabled(bool)));
|
||||
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
|
||||
#endif
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
|
||||
0, // QML Qt::Key_Backslash,
|
||||
dialogsManager.data(), SLOT(showIRCLink()));
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0,
|
||||
|
@ -175,7 +175,7 @@ Menu::Menu() {
|
|||
discoverabilityManager.data(), SLOT(setVisibility()));
|
||||
visibilityGroup->addAction(visibleToNoOne);
|
||||
|
||||
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
|
||||
connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged,
|
||||
discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged);
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ Menu::Menu() {
|
|||
0, // QML Qt::Key_Apostrophe,
|
||||
qApp,
|
||||
SLOT(resetSensors()));
|
||||
|
||||
|
||||
addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0,
|
||||
qApp, SLOT(packageModel()));
|
||||
|
||||
|
@ -245,17 +245,17 @@ Menu::Menu() {
|
|||
qApp,
|
||||
SLOT(setFullscreen(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson,
|
||||
0, // QML Qt::Key_P,
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson,
|
||||
0, // QML Qt::Key_P,
|
||||
true, qApp, SLOT(cameraMenuChanged()));
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
|
||||
0, //QML Qt::SHIFT | Qt::Key_H,
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
|
||||
0, //QML Qt::SHIFT | Qt::Key_H,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror,
|
||||
0, // QML Qt::Key_H,
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror,
|
||||
0, // QML Qt::Key_H,
|
||||
false, qApp, SLOT(cameraMenuChanged()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools,
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools,
|
||||
#ifdef Q_OS_MAC
|
||||
Qt::META | Qt::Key_H,
|
||||
#else
|
||||
|
@ -285,8 +285,8 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats);
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
|
||||
qApp, SLOT(toggleLogDialog()));
|
||||
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0,
|
||||
dialogsManager.data(), SLOT(bandwidthDetails()));
|
||||
|
@ -297,8 +297,8 @@ Menu::Menu() {
|
|||
MenuWrapper* developerMenu = addMenu("Developer");
|
||||
|
||||
MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render");
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
|
||||
0, // QML Qt::SHIFT | Qt::Key_A,
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
|
||||
0, // QML Qt::SHIFT | Qt::Key_A,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges);
|
||||
|
@ -317,7 +317,7 @@ Menu::Menu() {
|
|||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight7, 0, false));
|
||||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false));
|
||||
ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false));
|
||||
|
||||
|
||||
MenuWrapper* shadowMenu = renderOptionsMenu->addMenu("Shadows");
|
||||
QActionGroup* shadowGroup = new QActionGroup(shadowMenu);
|
||||
shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true));
|
||||
|
@ -351,14 +351,14 @@ Menu::Menu() {
|
|||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
|
||||
0, // QML Qt::Key_Asterisk,
|
||||
true);
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true,
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true,
|
||||
DependencyManager::get<GlowEffect>().data(), SLOT(toggleGlowEffect(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false);
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
||||
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
|
||||
0, // QML Qt::SHIFT | Qt::Key_L,
|
||||
dialogsManager.data(), SLOT(lodTools()));
|
||||
|
||||
|
@ -384,7 +384,7 @@ Menu::Menu() {
|
|||
faceTrackerGroup->addAction(faceshiftFaceTracker);
|
||||
#endif
|
||||
#ifdef HAVE_DDE
|
||||
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera,
|
||||
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera,
|
||||
0, true,
|
||||
qApp, SLOT(setActiveFaceTracker()));
|
||||
faceTrackerGroup->addAction(ddeFaceTracker);
|
||||
|
@ -404,13 +404,13 @@ Menu::Menu() {
|
|||
#endif
|
||||
#if defined(HAVE_FACESHIFT) || defined(HAVE_DDE)
|
||||
faceTrackingMenu->addSeparator();
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking,
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_F, true, // DDE face tracking is on by default
|
||||
qApp, SLOT(toggleFaceTrackerMute()));
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, true);
|
||||
#endif
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false,
|
||||
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
|
||||
|
||||
|
@ -426,7 +426,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
||||
|
||||
|
||||
MenuWrapper* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
|
||||
#ifdef __APPLE__
|
||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||
|
@ -472,7 +472,11 @@ Menu::Menu() {
|
|||
addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0,
|
||||
dialogsManager.data(), SLOT(toggleDiskCacheEditor()));
|
||||
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0,
|
||||
dialogsManager.data(), SLOT(showDomainConnectionDialog()));
|
||||
|
||||
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);
|
||||
|
@ -510,7 +514,7 @@ Menu::Menu() {
|
|||
0,
|
||||
audioIO.data(),
|
||||
SLOT(sendMuteEnvironmentPacket()));
|
||||
|
||||
|
||||
auto scope = DependencyManager::get<AudioScope>();
|
||||
|
||||
MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope");
|
||||
|
@ -548,7 +552,7 @@ Menu::Menu() {
|
|||
audioScopeFramesGroup->addAction(twentyFrames);
|
||||
audioScopeFramesGroup->addAction(fiftyFrames);
|
||||
}
|
||||
|
||||
|
||||
auto statsRenderer = DependencyManager::get<AudioIOStatsRenderer>();
|
||||
addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStats,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_A,
|
||||
|
@ -767,7 +771,7 @@ QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) {
|
|||
} else {
|
||||
menuActions = actions();
|
||||
}
|
||||
|
||||
|
||||
foreach (QAction* menuAction, menuActions) {
|
||||
QString actionText = menuAction->text();
|
||||
if (menuName == menuAction->text()) {
|
||||
|
@ -868,14 +872,14 @@ MenuWrapper* Menu::addMenu(const QString& menuName) {
|
|||
}
|
||||
addTo = menu;
|
||||
}
|
||||
|
||||
|
||||
QMenuBar::repaint();
|
||||
return menu;
|
||||
}
|
||||
|
||||
void Menu::removeMenu(const QString& menuName) {
|
||||
QAction* action = getMenuAction(menuName);
|
||||
|
||||
|
||||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
QString finalMenuPart;
|
||||
|
@ -885,14 +889,14 @@ void Menu::removeMenu(const QString& menuName) {
|
|||
} else {
|
||||
QMenuBar::removeAction(action);
|
||||
}
|
||||
|
||||
|
||||
QMenuBar::repaint();
|
||||
}
|
||||
}
|
||||
|
||||
bool Menu::menuExists(const QString& menuName) {
|
||||
QAction* action = getMenuAction(menuName);
|
||||
|
||||
|
||||
// only proceed if the menu actually exists
|
||||
if (action) {
|
||||
return true;
|
||||
|
@ -937,7 +941,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
|
|||
if (!properties.shortcutKeySequence.isEmpty()) {
|
||||
shortcut = new QShortcut(properties.shortcutKeySequence, this);
|
||||
}
|
||||
|
||||
|
||||
// check for positioning requests
|
||||
int requestedPosition = properties.position;
|
||||
if (requestedPosition == UNSPECIFIED_POSITION && !properties.beforeItem.isEmpty()) {
|
||||
|
@ -951,7 +955,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) {
|
|||
requestedPosition = afterPosition + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QAction* menuItemAction = NULL;
|
||||
if (properties.isSeparator) {
|
||||
addDisabledActionAndSeparator(menuObj, properties.menuItemName, requestedPosition);
|
||||
|
|
|
@ -58,15 +58,15 @@ class Menu : public QMenuBar {
|
|||
Q_OBJECT
|
||||
public:
|
||||
static Menu* getInstance();
|
||||
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
|
||||
|
||||
MenuWrapper* getMenu(const QString& menuName);
|
||||
|
||||
void triggerOption(const QString& menuOption);
|
||||
QAction* getActionForOption(const QString& menuOption);
|
||||
|
||||
|
||||
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
|
@ -80,9 +80,9 @@ public:
|
|||
const QKeySequence& shortcut = 0,
|
||||
QAction::MenuRole role = QAction::NoRole,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
|
||||
void removeAction(MenuWrapper* menu, const QString& actionName);
|
||||
|
||||
|
||||
public slots:
|
||||
MenuWrapper* addMenu(const QString& menuName);
|
||||
void removeMenu(const QString& menuName);
|
||||
|
@ -94,21 +94,21 @@ public slots:
|
|||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
bool isOptionChecked(const QString& menuOption) const;
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
|
||||
private:
|
||||
static Menu* _instance;
|
||||
Menu();
|
||||
|
||||
|
||||
typedef void(*settingsAction)(Settings&, QAction&);
|
||||
static void loadAction(Settings& settings, QAction& action);
|
||||
static void saveAction(Settings& settings, QAction& action);
|
||||
void scanMenuBar(settingsAction modifySetting);
|
||||
void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings);
|
||||
|
||||
|
||||
/// helper method to have separators with labels that are also compatible with OS X
|
||||
void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
|
||||
QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
|
||||
const QString& actionName,
|
||||
const QKeySequence& shortcut = 0,
|
||||
|
@ -116,15 +116,15 @@ private:
|
|||
const QObject* receiver = NULL,
|
||||
const char* member = NULL,
|
||||
int menuItemLocation = UNSPECIFIED_POSITION);
|
||||
|
||||
|
||||
QAction* getActionFromName(const QString& menuName, MenuWrapper* menu);
|
||||
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
|
||||
MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart);
|
||||
|
||||
|
||||
QAction* getMenuAction(const QString& menuName);
|
||||
int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem);
|
||||
int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition);
|
||||
|
||||
|
||||
QHash<QString, QAction*> _actionHash;
|
||||
};
|
||||
|
||||
|
@ -262,6 +262,7 @@ namespace MenuOption {
|
|||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString ScriptEditor = "Script Editor...";
|
||||
const QString ScriptedMotorControl = "Enable Scripted Motor Control";
|
||||
const QString ShowDSConnectTable = "Show Domain Connection Timing";
|
||||
const QString ShowBordersEntityNodes = "Show Entity Nodes";
|
||||
const QString ShowIKConstraints = "Show IK Constraints";
|
||||
const QString SimpleShadows = "Simple";
|
||||
|
|
|
@ -147,9 +147,9 @@ public:
|
|||
|
||||
Q_INVOKABLE glm::vec3 getNeckPosition() const;
|
||||
|
||||
Q_INVOKABLE const glm::vec3& getAcceleration() const { return _acceleration; }
|
||||
Q_INVOKABLE const glm::vec3& getAngularVelocity() const { return _angularVelocity; }
|
||||
Q_INVOKABLE const glm::vec3& getAngularAcceleration() const { return _angularAcceleration; }
|
||||
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
|
||||
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
||||
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
||||
|
||||
|
||||
/// Scales a world space position vector relative to the avatar position and scale
|
||||
|
|
|
@ -123,17 +123,17 @@ glm::quat AvatarMotionState::getObjectRotation() const {
|
|||
}
|
||||
|
||||
// virtual
|
||||
const glm::vec3& AvatarMotionState::getObjectLinearVelocity() const {
|
||||
glm::vec3 AvatarMotionState::getObjectLinearVelocity() const {
|
||||
return _avatar->getVelocity();
|
||||
}
|
||||
|
||||
// virtual
|
||||
const glm::vec3& AvatarMotionState::getObjectAngularVelocity() const {
|
||||
glm::vec3 AvatarMotionState::getObjectAngularVelocity() const {
|
||||
return _avatar->getAngularVelocity();
|
||||
}
|
||||
|
||||
// virtual
|
||||
const glm::vec3& AvatarMotionState::getObjectGravity() const {
|
||||
glm::vec3 AvatarMotionState::getObjectGravity() const {
|
||||
return _avatar->getAcceleration();
|
||||
}
|
||||
|
||||
|
|
|
@ -48,9 +48,9 @@ public:
|
|||
|
||||
virtual glm::vec3 getObjectPosition() const;
|
||||
virtual glm::quat getObjectRotation() const;
|
||||
virtual const glm::vec3& getObjectLinearVelocity() const;
|
||||
virtual const glm::vec3& getObjectAngularVelocity() const;
|
||||
virtual const glm::vec3& getObjectGravity() const;
|
||||
virtual glm::vec3 getObjectLinearVelocity() const;
|
||||
virtual glm::vec3 getObjectAngularVelocity() const;
|
||||
virtual glm::vec3 getObjectGravity() const;
|
||||
|
||||
virtual const QUuid& getObjectID() const;
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ ModelReferential::ModelReferential(Referential* referential, EntityTree* tree, A
|
|||
return;
|
||||
}
|
||||
|
||||
const EntityItem* item = _tree->findEntityByID(_entityID);
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
if (item != NULL) {
|
||||
_lastRefDimension = item->getDimensions();
|
||||
_refRotation = item->getRotation();
|
||||
|
@ -44,7 +44,7 @@ ModelReferential::ModelReferential(const QUuid& entityID, EntityTree* tree, Avat
|
|||
_entityID(entityID),
|
||||
_tree(tree)
|
||||
{
|
||||
const EntityItem* item = _tree->findEntityByID(_entityID);
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
if (!isValid() || item == NULL) {
|
||||
qCDebug(interfaceapp) << "ModelReferential::constructor(): Not Valid";
|
||||
_isValid = false;
|
||||
|
@ -61,7 +61,7 @@ ModelReferential::ModelReferential(const QUuid& entityID, EntityTree* tree, Avat
|
|||
}
|
||||
|
||||
void ModelReferential::update() {
|
||||
const EntityItem* item = _tree->findEntityByID(_entityID);
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
if (!isValid() || item == NULL || _avatar == NULL) {
|
||||
return;
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ JointReferential::JointReferential(Referential* referential, EntityTree* tree, A
|
|||
return;
|
||||
}
|
||||
|
||||
const EntityItem* item = _tree->findEntityByID(_entityID);
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
const Model* model = getModel(item);
|
||||
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
|
||||
_lastRefDimension = item->getDimensions();
|
||||
|
@ -120,7 +120,7 @@ JointReferential::JointReferential(uint32_t jointIndex, const QUuid& entityID, E
|
|||
_jointIndex(jointIndex)
|
||||
{
|
||||
_type = JOINT;
|
||||
const EntityItem* item = _tree->findEntityByID(_entityID);
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
const Model* model = getModel(item);
|
||||
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
|
||||
qCDebug(interfaceapp) << "JointReferential::constructor(): Not Valid";
|
||||
|
@ -139,7 +139,7 @@ JointReferential::JointReferential(uint32_t jointIndex, const QUuid& entityID, E
|
|||
}
|
||||
|
||||
void JointReferential::update() {
|
||||
const EntityItem* item = _tree->findEntityByID(_entityID);
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
const Model* model = getModel(item);
|
||||
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
|
||||
return;
|
||||
|
@ -163,7 +163,7 @@ void JointReferential::update() {
|
|||
}
|
||||
}
|
||||
|
||||
const Model* JointReferential::getModel(const EntityItem* item) {
|
||||
const Model* JointReferential::getModel(EntityItemPointer item) {
|
||||
EntityItemFBXService* fbxService = _tree->getFBXService();
|
||||
if (item != NULL && fbxService != NULL) {
|
||||
return fbxService->getModelForEntityItem(item);
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
virtual void update();
|
||||
|
||||
protected:
|
||||
const Model* getModel(const EntityItem* item);
|
||||
const Model* getModel(EntityItemPointer item);
|
||||
virtual int packExtraData(unsigned char* destinationBuffer) const;
|
||||
virtual int unpackExtraData(const unsigned char* sourceBuffer, int size);
|
||||
|
||||
|
|
|
@ -141,13 +141,6 @@ static const float STARTING_DDE_MESSAGE_TIME = 0.033f;
|
|||
static const float DEFAULT_DDE_EYE_CLOSING_THRESHOLD = 0.8f;
|
||||
static const int CALIBRATION_SAMPLES = 150;
|
||||
|
||||
#ifdef WIN32
|
||||
// warning C4351: new behavior: elements of array 'DdeFaceTracker::_lastEyeBlinks' will be default initialized
|
||||
// warning C4351: new behavior: elements of array 'DdeFaceTracker::_filteredEyeBlinks' will be default initialized
|
||||
// warning C4351: new behavior: elements of array 'DdeFaceTracker::_lastEyeCoefficients' will be default initialized
|
||||
#pragma warning(disable:4351)
|
||||
#endif
|
||||
|
||||
DdeFaceTracker::DdeFaceTracker() :
|
||||
DdeFaceTracker(QHostAddress::Any, DDE_SERVER_PORT, DDE_CONTROL_PORT)
|
||||
{
|
||||
|
@ -214,10 +207,6 @@ DdeFaceTracker::~DdeFaceTracker() {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma warning(default:4351)
|
||||
#endif
|
||||
|
||||
void DdeFaceTracker::init() {
|
||||
FaceTracker::init();
|
||||
setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::UseCamera) && !_isMuted);
|
||||
|
|
|
@ -356,7 +356,7 @@ void ApplicationOverlay::displayOverlayTextureHmd(Camera& whichCamera) {
|
|||
});
|
||||
|
||||
if (!Application::getInstance()->isMouseHidden()) {
|
||||
renderPointersOculus(myAvatar->getDefaultEyePosition());
|
||||
renderPointersOculus();
|
||||
}
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
@ -486,49 +486,40 @@ void ApplicationOverlay::computeHmdPickRay(glm::vec2 cursorPos, glm::vec3& origi
|
|||
direction = glm::normalize(intersectionWithUi - origin);
|
||||
}
|
||||
|
||||
glm::vec2 getPolarCoordinates(const PalmData& palm) {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
auto avatarOrientation = myAvatar->getOrientation();
|
||||
auto eyePos = myAvatar->getDefaultEyePosition();
|
||||
glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm);
|
||||
// Direction of the tip relative to the eye
|
||||
glm::vec3 tipDirection = tip - eyePos;
|
||||
// orient into avatar space
|
||||
tipDirection = glm::inverse(avatarOrientation) * tipDirection;
|
||||
// Normalize for trig functions
|
||||
tipDirection = glm::normalize(tipDirection);
|
||||
// Convert to polar coordinates
|
||||
glm::vec2 polar(glm::atan(tipDirection.x, -tipDirection.z), glm::asin(tipDirection.y));
|
||||
return polar;
|
||||
}
|
||||
|
||||
//Caculate the click location using one of the sixense controllers. Scale is not applied
|
||||
QPoint ApplicationOverlay::getPalmClickLocation(const PalmData *palm) const {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm);
|
||||
glm::vec3 eyePos = myAvatar->getHead()->getEyePosition();
|
||||
glm::quat invOrientation = glm::inverse(myAvatar->getOrientation());
|
||||
//direction of ray goes towards camera
|
||||
glm::vec3 dir = invOrientation * glm::normalize(qApp->getCamera()->getPosition() - tip);
|
||||
glm::vec3 tipPos = invOrientation * (tip - eyePos);
|
||||
|
||||
QPoint rv;
|
||||
auto canvasSize = qApp->getCanvasSize();
|
||||
if (qApp->isHMDMode()) {
|
||||
float t;
|
||||
|
||||
//We back the ray up by dir to ensure that it will not start inside the UI.
|
||||
glm::vec3 adjustedPos = tipPos - dir;
|
||||
//Find intersection of crosshair ray.
|
||||
if (raySphereIntersect(dir, adjustedPos, _oculusUIRadius * myAvatar->getScale(), &t)){
|
||||
glm::vec3 collisionPos = adjustedPos + dir * t;
|
||||
//Normalize it in case its not a radius of 1
|
||||
collisionPos = glm::normalize(collisionPos);
|
||||
//If we hit the back hemisphere, mark it as not a collision
|
||||
if (collisionPos.z > 0) {
|
||||
rv.setX(INT_MAX);
|
||||
rv.setY(INT_MAX);
|
||||
} else {
|
||||
|
||||
float u = asin(collisionPos.x) / (_textureFov)+0.5f;
|
||||
float v = 1.0 - (asin(collisionPos.y) / (_textureFov)+0.5f);
|
||||
|
||||
rv.setX(u * canvasSize.x);
|
||||
rv.setY(v * canvasSize.y);
|
||||
}
|
||||
} else {
|
||||
//if they did not click on the overlay, just set the coords to INT_MAX
|
||||
rv.setX(INT_MAX);
|
||||
rv.setY(INT_MAX);
|
||||
}
|
||||
glm::vec2 polar = getPolarCoordinates(*palm);
|
||||
glm::vec2 point = sphericalToScreen(-polar);
|
||||
rv.rx() = point.x;
|
||||
rv.ry() = point.y;
|
||||
} else {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
glm::dmat4 projection;
|
||||
qApp->getProjectionMatrix(&projection);
|
||||
glm::quat invOrientation = glm::inverse(myAvatar->getOrientation());
|
||||
glm::vec3 eyePos = myAvatar->getDefaultEyePosition();
|
||||
glm::vec3 tip = myAvatar->getLaserPointerTipPosition(palm);
|
||||
glm::vec3 tipPos = invOrientation * (tip - eyePos);
|
||||
|
||||
glm::vec4 clipSpacePos = glm::vec4(projection * glm::dvec4(tipPos, 1.0));
|
||||
glm::vec3 ndcSpacePos;
|
||||
|
@ -729,7 +720,7 @@ void ApplicationOverlay::renderControllerPointers() {
|
|||
}
|
||||
}
|
||||
|
||||
void ApplicationOverlay::renderPointersOculus(const glm::vec3& eyePos) {
|
||||
void ApplicationOverlay::renderPointersOculus() {
|
||||
glBindTexture(GL_TEXTURE_2D, gpu::GLBackend::getTextureID(_crosshairTexture));
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
@ -737,16 +728,12 @@ void ApplicationOverlay::renderPointersOculus(const glm::vec3& eyePos) {
|
|||
//Controller Pointers
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {
|
||||
|
||||
PalmData& palm = myAvatar->getHand()->getPalms()[i];
|
||||
if (palm.isActive()) {
|
||||
glm::vec3 tip = myAvatar->getLaserPointerTipPosition(&palm);
|
||||
glm::vec3 tipDirection = glm::normalize(glm::inverse(myAvatar->getOrientation()) * (tip - eyePos));
|
||||
float pitch = -glm::asin(tipDirection.y);
|
||||
float yawSign = glm::sign(-tipDirection.x);
|
||||
float yaw = glm::acos(-tipDirection.z) *
|
||||
((yawSign == 0.0f) ? 1.0f : yawSign);
|
||||
glm::quat orientation = glm::quat(glm::vec3(pitch, yaw, 0.0f));
|
||||
glm::vec2 polar = getPolarCoordinates(palm);
|
||||
// Convert to quaternion
|
||||
glm::quat orientation = glm::quat(glm::vec3(polar.y, -polar.x, 0.0f));
|
||||
// Render reticle at location
|
||||
renderReticle(orientation, _alpha);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ private:
|
|||
void renderMagnifier(glm::vec2 magPos, float sizeMult, bool showBorder);
|
||||
|
||||
void renderControllerPointers();
|
||||
void renderPointersOculus(const glm::vec3& eyePos);
|
||||
void renderPointersOculus();
|
||||
|
||||
void renderAudioMeter();
|
||||
void renderCameraToggle();
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "BandwidthDialog.h"
|
||||
#include "CachesSizeDialog.h"
|
||||
#include "DiskCacheEditor.h"
|
||||
#include "DomainConnectionDialog.h"
|
||||
#include "HMDToolsDialog.h"
|
||||
#include "LodToolsDialog.h"
|
||||
#include "LoginDialog.h"
|
||||
|
@ -52,7 +53,7 @@ void DialogsManager::showLoginDialog() {
|
|||
void DialogsManager::octreeStatsDetails() {
|
||||
if (!_octreeStatsDialog) {
|
||||
_octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats());
|
||||
|
||||
|
||||
if (_hmdToolsDialog) {
|
||||
_hmdToolsDialog->watchWindow(_octreeStatsDialog->windowHandle());
|
||||
}
|
||||
|
@ -65,7 +66,7 @@ void DialogsManager::octreeStatsDetails() {
|
|||
void DialogsManager::cachesSizeDialog() {
|
||||
if (!_cachesSizeDialog) {
|
||||
maybeCreateDialog(_cachesSizeDialog);
|
||||
|
||||
|
||||
connect(_cachesSizeDialog, SIGNAL(closed()), _cachesSizeDialog, SLOT(deleteLater()));
|
||||
_cachesSizeDialog->show();
|
||||
}
|
||||
|
@ -112,11 +113,11 @@ void DialogsManager::bandwidthDetails() {
|
|||
if (! _bandwidthDialog) {
|
||||
_bandwidthDialog = new BandwidthDialog(qApp->getWindow());
|
||||
connect(_bandwidthDialog, SIGNAL(closed()), _bandwidthDialog, SLOT(deleteLater()));
|
||||
|
||||
|
||||
if (_hmdToolsDialog) {
|
||||
_hmdToolsDialog->watchWindow(_bandwidthDialog->windowHandle());
|
||||
}
|
||||
|
||||
|
||||
_bandwidthDialog->show();
|
||||
}
|
||||
_bandwidthDialog->raise();
|
||||
|
@ -125,7 +126,7 @@ void DialogsManager::bandwidthDetails() {
|
|||
void DialogsManager::lodTools() {
|
||||
if (!_lodToolsDialog) {
|
||||
maybeCreateDialog(_lodToolsDialog);
|
||||
|
||||
|
||||
connect(_lodToolsDialog, SIGNAL(closed()), _lodToolsDialog, SLOT(deleteLater()));
|
||||
_lodToolsDialog->show();
|
||||
}
|
||||
|
@ -172,7 +173,20 @@ void DialogsManager::showIRCLink() {
|
|||
_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) {
|
||||
_domainConnectionDialog->close();
|
||||
_domainConnectionDialog->deleteLater();
|
||||
_domainConnectionDialog = NULL;
|
||||
}
|
||||
|
||||
maybeCreateDialog(_domainConnectionDialog);
|
||||
|
||||
_domainConnectionDialog->show();
|
||||
_domainConnectionDialog->raise();
|
||||
}
|
||||
|
|
|
@ -34,18 +34,19 @@ class PreferencesDialog;
|
|||
class ScriptEditorWindow;
|
||||
class QMessageBox;
|
||||
class AvatarAppearanceDialog;
|
||||
class DomainConnectionDialog;
|
||||
|
||||
class DialogsManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
|
||||
public:
|
||||
QPointer<BandwidthDialog> getBandwidthDialog() const { return _bandwidthDialog; }
|
||||
QPointer<HMDToolsDialog> getHMDToolsDialog() const { return _hmdToolsDialog; }
|
||||
QPointer<LodToolsDialog> getLodToolsDialog() const { return _lodToolsDialog; }
|
||||
QPointer<OctreeStatsDialog> getOctreeStatsDialog() const { return _octreeStatsDialog; }
|
||||
QPointer<PreferencesDialog> getPreferencesDialog() const { return _preferencesDialog; }
|
||||
|
||||
|
||||
public slots:
|
||||
void toggleAddressBar();
|
||||
void toggleDiskCacheEditor();
|
||||
|
@ -62,14 +63,15 @@ public slots:
|
|||
void showScriptEditor();
|
||||
void showIRCLink();
|
||||
void changeAvatarAppearance();
|
||||
void showDomainConnectionDialog();
|
||||
|
||||
private slots:
|
||||
void toggleToolWindow();
|
||||
void hmdToolsClosed();
|
||||
|
||||
|
||||
private:
|
||||
DialogsManager() {}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void maybeCreateDialog(QPointer<T>& member) {
|
||||
if (!member) {
|
||||
|
@ -77,13 +79,13 @@ private:
|
|||
Q_CHECK_PTR(parent);
|
||||
member = new T(parent);
|
||||
Q_CHECK_PTR(member);
|
||||
|
||||
|
||||
if (_hmdToolsDialog && member->windowHandle()) {
|
||||
_hmdToolsDialog->watchWindow(member->windowHandle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QPointer<AddressBarDialog> _addressBarDialog;
|
||||
QPointer<AnimationsDialog> _animationsDialog;
|
||||
QPointer<AttachmentsDialog> _attachmentsDialog;
|
||||
|
@ -98,6 +100,7 @@ private:
|
|||
QPointer<PreferencesDialog> _preferencesDialog;
|
||||
QPointer<ScriptEditorWindow> _scriptEditor;
|
||||
QPointer<AvatarAppearanceDialog> _avatarAppearanceDialog;
|
||||
QPointer<DomainConnectionDialog> _domainConnectionDialog;
|
||||
};
|
||||
|
||||
#endif // hifi_DialogsManager_h
|
||||
#endif // hifi_DialogsManager_h
|
||||
|
|
96
interface/src/ui/DomainConnectionDialog.cpp
Normal file
96
interface/src/ui/DomainConnectionDialog.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// DomainConnectionDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stephen Birarda on 05/26/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
|
||||
//
|
||||
|
||||
#include <QtCore/QMetaEnum>
|
||||
#include <QtWidgets/QHBoxLayout>
|
||||
#include <QtWidgets/QHeaderView>
|
||||
#include <QtWidgets/QTableWidget>
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include "DomainConnectionDialog.h"
|
||||
|
||||
DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) :
|
||||
QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint)
|
||||
{
|
||||
setWindowTitle("Domain Connection Timing");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
// setup a QTableWidget so we can populate it with our values
|
||||
QTableWidget* timeTable = new QTableWidget;
|
||||
|
||||
timeTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
|
||||
const QStringList TABLE_HEADERS = QStringList() << "Name" << "Timestamp (ms)" << "Delta (ms)" << "Time elapsed (ms)";
|
||||
|
||||
timeTable->setColumnCount(TABLE_HEADERS.size());
|
||||
|
||||
// ask the NodeList for the current values for connection times
|
||||
QMap<quint64, LimitedNodeList::ConnectionStep> times = DependencyManager::get<NodeList>()->getLastConnectionTimes();
|
||||
|
||||
timeTable->setRowCount(times.size());
|
||||
|
||||
timeTable->setHorizontalHeaderLabels(TABLE_HEADERS);
|
||||
|
||||
// setup our data with the values from the NodeList
|
||||
quint64 firstStepTime = times.firstKey() / USECS_PER_MSEC;
|
||||
quint64 lastStepTime = firstStepTime;
|
||||
|
||||
int thisRow = 0;
|
||||
|
||||
const QMetaObject &nodeListMeta = NodeList::staticMetaObject;
|
||||
QMetaEnum stepEnum = nodeListMeta.enumerator(nodeListMeta.indexOfEnumerator("ConnectionStep"));
|
||||
|
||||
foreach(quint64 timestamp, times.keys()) {
|
||||
// When did this step occur, how long since the last step, how long since the start?
|
||||
quint64 stepTime = timestamp / USECS_PER_MSEC;
|
||||
quint64 delta = (stepTime - lastStepTime);
|
||||
quint64 elapsed = stepTime - firstStepTime;
|
||||
|
||||
lastStepTime = stepTime;
|
||||
|
||||
// setup the columns for this row in the table
|
||||
int stepIndex = (int) times.value(timestamp);
|
||||
|
||||
timeTable->setItem(thisRow, 0, new QTableWidgetItem(stepEnum.valueToKey(stepIndex)));
|
||||
timeTable->setItem(thisRow, 1, new QTableWidgetItem(QString::number(stepTime)));
|
||||
timeTable->setItem(thisRow, 2, new QTableWidgetItem(QString::number(delta)));
|
||||
timeTable->setItem(thisRow, 3, new QTableWidgetItem(QString::number(elapsed)));
|
||||
|
||||
++thisRow;
|
||||
}
|
||||
|
||||
// setup a horizontal box layout
|
||||
QHBoxLayout* hBoxLayout = new QHBoxLayout;
|
||||
hBoxLayout->addWidget(timeTable);
|
||||
hBoxLayout->setSizeConstraint(QLayout::SetMinimumSize);
|
||||
|
||||
// resize the table columns
|
||||
timeTable->resizeColumnsToContents();
|
||||
|
||||
// figure out the size of the table so we can size the dialog
|
||||
int tableWidth = timeTable->verticalHeader()->sizeHint().width();
|
||||
for (int i = 0; i < timeTable->columnCount(); i++) {
|
||||
tableWidth += timeTable->columnWidth(i);
|
||||
}
|
||||
|
||||
int tableHeight = timeTable->horizontalHeader()->sizeHint().height();
|
||||
for (int i = 0; i < timeTable->rowCount(); i++) {
|
||||
tableHeight += timeTable->rowHeight(i);
|
||||
}
|
||||
|
||||
// set the minimum size of the table to whatever we got
|
||||
timeTable->setMinimumSize(tableWidth, tableHeight);
|
||||
|
||||
setLayout(hBoxLayout);
|
||||
adjustSize();
|
||||
}
|
25
interface/src/ui/DomainConnectionDialog.h
Normal file
25
interface/src/ui/DomainConnectionDialog.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// DomainConnectionDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Stephen Birarda on 05/26/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_DomainConnectionDialog_h
|
||||
#define hifi_DomainConnectionDialog_h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
class DomainConnectionDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DomainConnectionDialog(QWidget* parent);
|
||||
};
|
||||
|
||||
#endif // hifi_DomainConnectionDialog_h
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include <GlowEffect.h>
|
||||
|
||||
#include "../../Menu.h"
|
||||
|
||||
#include "ModelOverlay.h"
|
||||
|
||||
ModelOverlay::ModelOverlay()
|
||||
|
|
|
@ -116,7 +116,7 @@ AudioClient::AudioClient() :
|
|||
{
|
||||
// clear the array of locally injected samples
|
||||
memset(_localProceduralSamples, 0, AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL);
|
||||
|
||||
|
||||
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples,
|
||||
this, &AudioClient::processReceivedSamples, Qt::DirectConnection);
|
||||
|
||||
|
@ -127,7 +127,7 @@ AudioClient::AudioClient() :
|
|||
QTimer* updateTimer = new QTimer(this);
|
||||
connect(updateTimer, &QTimer::timeout, this, &AudioClient::checkDevices);
|
||||
updateTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
|
||||
|
||||
|
||||
// create GVerb filter
|
||||
_gverb = createGverbFilter();
|
||||
configureGverbFilter(_gverb);
|
||||
|
@ -135,7 +135,7 @@ AudioClient::AudioClient() :
|
|||
|
||||
AudioClient::~AudioClient() {
|
||||
stop();
|
||||
|
||||
|
||||
if (_gverb) {
|
||||
gverb_free(_gverb);
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ void AudioClient::reset() {
|
|||
_toneSource.reset();
|
||||
_sourceGain.reset();
|
||||
_inputGain.reset();
|
||||
|
||||
|
||||
gverb_flush(_gverb);
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,7 @@ int numDestinationSamplesRequired(const QAudioFormat& sourceFormat, const QAudio
|
|||
int numSourceSamples) {
|
||||
float ratio = (float) destinationFormat.channelCount() / sourceFormat.channelCount();
|
||||
ratio *= (float) destinationFormat.sampleRate() / sourceFormat.sampleRate();
|
||||
|
||||
|
||||
return (numSourceSamples * ratio) + 0.5f;
|
||||
}
|
||||
|
||||
|
@ -287,7 +287,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
|
|||
}
|
||||
|
||||
qCDebug(audioclient) << "DEBUG [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]";
|
||||
|
||||
|
||||
return getNamedAudioDeviceForMode(mode, deviceName);
|
||||
#endif
|
||||
|
||||
|
@ -302,7 +302,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
|||
if (!audioDevice.isFormatSupported(desiredAudioFormat)) {
|
||||
qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat;
|
||||
qCDebug(audioclient, "The desired audio format is not supported by this device");
|
||||
|
||||
|
||||
if (desiredAudioFormat.channelCount() == 1) {
|
||||
adjustedAudioFormat = desiredAudioFormat;
|
||||
adjustedAudioFormat.setChannelCount(2);
|
||||
|
@ -313,17 +313,17 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
|||
adjustedAudioFormat.setChannelCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const int FORTY_FOUR = 44100;
|
||||
|
||||
|
||||
adjustedAudioFormat = desiredAudioFormat;
|
||||
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
adjustedAudioFormat.setSampleRate(FORTY_FOUR);
|
||||
#else
|
||||
|
||||
|
||||
const int HALF_FORTY_FOUR = FORTY_FOUR / 2;
|
||||
|
||||
|
||||
if (audioDevice.supportedSampleRates().contains(AudioConstants::SAMPLE_RATE * 2)) {
|
||||
// use 48, which is a sample downsample, upsample
|
||||
adjustedAudioFormat.setSampleRate(AudioConstants::SAMPLE_RATE * 2);
|
||||
|
@ -335,7 +335,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
|
|||
adjustedAudioFormat.setSampleRate(FORTY_FOUR);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (adjustedAudioFormat != desiredAudioFormat) {
|
||||
// return the nearest in case it needs 2 channels
|
||||
adjustedAudioFormat = audioDevice.nearestFormat(adjustedAudioFormat);
|
||||
|
@ -357,18 +357,18 @@ bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationS
|
|||
for (uint i = 0; i < numSourceSamples; i += 2) {
|
||||
destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 1] / 2);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
} else if (sourceAudioFormat.channelCount() == 1 && destinationAudioFormat.channelCount() == 2) {
|
||||
|
||||
|
||||
// loop through the mono input audio and repeat each sample twice
|
||||
for (uint i = 0; i < numSourceSamples; ++i) {
|
||||
destinationSamples[i * 2] = destinationSamples[(i * 2) + 1] = sourceSamples[i];
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -376,7 +376,7 @@ soxr_error_t possibleResampling(soxr_t resampler,
|
|||
const int16_t* sourceSamples, int16_t* destinationSamples,
|
||||
unsigned int numSourceSamples, unsigned int numDestinationSamples,
|
||||
const QAudioFormat& sourceAudioFormat, const QAudioFormat& destinationAudioFormat) {
|
||||
|
||||
|
||||
if (numSourceSamples > 0) {
|
||||
if (!resampler) {
|
||||
if (!sampleChannelConversion(sourceSamples, destinationSamples, numSourceSamples,
|
||||
|
@ -384,41 +384,41 @@ soxr_error_t possibleResampling(soxr_t resampler,
|
|||
// no conversion, we can copy the samples directly across
|
||||
memcpy(destinationSamples, sourceSamples, numSourceSamples * sizeof(int16_t));
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
soxr_error_t resampleError = 0;
|
||||
|
||||
|
||||
if (sourceAudioFormat.channelCount() != destinationAudioFormat.channelCount()) {
|
||||
float channelCountRatio = (float) destinationAudioFormat.channelCount() / sourceAudioFormat.channelCount();
|
||||
|
||||
|
||||
int numChannelCoversionSamples = (int) (numSourceSamples * channelCountRatio);
|
||||
int16_t* channelConversionSamples = new int16_t[numChannelCoversionSamples];
|
||||
|
||||
|
||||
sampleChannelConversion(sourceSamples, channelConversionSamples,
|
||||
numSourceSamples,
|
||||
sourceAudioFormat, destinationAudioFormat);
|
||||
|
||||
|
||||
resampleError = soxr_process(resampler,
|
||||
channelConversionSamples, numChannelCoversionSamples, NULL,
|
||||
destinationSamples, numDestinationSamples, NULL);
|
||||
|
||||
|
||||
delete[] channelConversionSamples;
|
||||
} else {
|
||||
|
||||
|
||||
unsigned int numAdjustedSourceSamples = numSourceSamples;
|
||||
unsigned int numAdjustedDestinationSamples = numDestinationSamples;
|
||||
|
||||
|
||||
if (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2) {
|
||||
numAdjustedSourceSamples /= 2;
|
||||
numAdjustedDestinationSamples /= 2;
|
||||
}
|
||||
|
||||
|
||||
resampleError = soxr_process(resampler,
|
||||
sourceSamples, numAdjustedSourceSamples, NULL,
|
||||
destinationSamples, numAdjustedDestinationSamples, NULL);
|
||||
}
|
||||
|
||||
|
||||
return resampleError;
|
||||
}
|
||||
} else {
|
||||
|
@ -429,30 +429,30 @@ soxr_error_t possibleResampling(soxr_t resampler,
|
|||
soxr_t soxrResamplerFromInputFormatToOutputFormat(const QAudioFormat& sourceAudioFormat,
|
||||
const QAudioFormat& destinationAudioFormat) {
|
||||
soxr_error_t soxrError;
|
||||
|
||||
|
||||
// setup soxr_io_spec_t for input and output
|
||||
soxr_io_spec_t inputToNetworkSpec = soxr_io_spec(soxrDataTypeFromQAudioFormat(sourceAudioFormat),
|
||||
soxrDataTypeFromQAudioFormat(destinationAudioFormat));
|
||||
|
||||
|
||||
// setup soxr_quality_spec_t for quality options
|
||||
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0);
|
||||
|
||||
|
||||
int channelCount = (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2)
|
||||
? 2 : 1;
|
||||
|
||||
|
||||
soxr_t newResampler = soxr_create(sourceAudioFormat.sampleRate(),
|
||||
destinationAudioFormat.sampleRate(),
|
||||
channelCount,
|
||||
&soxrError, &inputToNetworkSpec, &qualitySpec, 0);
|
||||
|
||||
|
||||
if (soxrError) {
|
||||
qCDebug(audioclient) << "There was an error setting up the soxr resampler -" << "soxr error code was " << soxrError;
|
||||
|
||||
|
||||
soxr_delete(newResampler);
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
return newResampler;
|
||||
}
|
||||
|
||||
|
@ -476,7 +476,7 @@ void AudioClient::start() {
|
|||
QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput);
|
||||
qCDebug(audioclient) << "The default audio output device is" << outputDeviceInfo.deviceName();
|
||||
bool outputFormatSupported = switchOutputToAudioDevice(outputDeviceInfo);
|
||||
|
||||
|
||||
if (!inputFormatSupported) {
|
||||
qCDebug(audioclient) << "Unable to set up audio input because of a problem with input format.";
|
||||
qCDebug(audioclient) << "The closest format available is" << inputDeviceInfo.nearestFormat(_desiredInputFormat);
|
||||
|
@ -503,11 +503,11 @@ void AudioClient::stop() {
|
|||
_sourceGain.finalize();
|
||||
_noiseSource.finalize();
|
||||
_toneSource.finalize();
|
||||
|
||||
|
||||
// "switch" to invalid devices in order to shut down the state
|
||||
switchInputToAudioDevice(QAudioDeviceInfo());
|
||||
switchOutputToAudioDevice(QAudioDeviceInfo());
|
||||
|
||||
|
||||
if (_loopbackResampler) {
|
||||
soxr_delete(_loopbackResampler);
|
||||
_loopbackResampler = NULL;
|
||||
|
@ -543,7 +543,7 @@ ty_gverb* AudioClient::createGverbFilter() {
|
|||
_reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
|
||||
_reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
|
||||
_reverbOptions->getTailLevel());
|
||||
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
@ -560,7 +560,7 @@ void AudioClient::configureGverbFilter(ty_gverb* filter) {
|
|||
void AudioClient::updateGverbOptions() {
|
||||
bool reverbChanged = false;
|
||||
if (_receivedAudioStream.hasReverb()) {
|
||||
|
||||
|
||||
if (_zoneReverbOptions.getReverbTime() != _receivedAudioStream.getRevebTime()) {
|
||||
_zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime());
|
||||
reverbChanged = true;
|
||||
|
@ -569,7 +569,7 @@ void AudioClient::updateGverbOptions() {
|
|||
_zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
|
||||
// Not part of actual filter config, no need to set reverbChanged to true
|
||||
}
|
||||
|
||||
|
||||
if (_reverbOptions != &_zoneReverbOptions) {
|
||||
_reverbOptions = &_zoneReverbOptions;
|
||||
reverbChanged = true;
|
||||
|
@ -578,7 +578,7 @@ void AudioClient::updateGverbOptions() {
|
|||
_reverbOptions = &_scriptReverbOptions;
|
||||
reverbChanged = true;
|
||||
}
|
||||
|
||||
|
||||
if (reverbChanged) {
|
||||
gverb_free(_gverb);
|
||||
_gverb = createGverbFilter();
|
||||
|
@ -588,7 +588,7 @@ void AudioClient::updateGverbOptions() {
|
|||
|
||||
void AudioClient::setReverb(bool reverb) {
|
||||
_reverb = reverb;
|
||||
|
||||
|
||||
if (!_reverb) {
|
||||
gverb_flush(_gverb);
|
||||
}
|
||||
|
@ -620,7 +620,7 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reve
|
|||
QAudioFormat& audioFormat, bool noEcho) {
|
||||
float wetFraction = DB_CO(_reverbOptions->getWetLevel());
|
||||
float dryFraction = 1.0f - wetFraction;
|
||||
|
||||
|
||||
float lValue,rValue;
|
||||
for (int sample = 0; sample < numSamples; sample += audioFormat.channelCount()) {
|
||||
// Run GVerb
|
||||
|
@ -634,7 +634,7 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reve
|
|||
int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction),
|
||||
AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE);
|
||||
samplesData[j] = (int16_t)lResult;
|
||||
|
||||
|
||||
if (noEcho) {
|
||||
reverbAlone[j] = (int16_t)lValue * wetFraction;
|
||||
}
|
||||
|
@ -643,7 +643,7 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reve
|
|||
int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction),
|
||||
AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE);
|
||||
samplesData[j] = (int16_t)rResult;
|
||||
|
||||
|
||||
if (noEcho) {
|
||||
reverbAlone[j] = (int16_t)rValue * wetFraction;
|
||||
}
|
||||
|
@ -660,53 +660,53 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
|||
if (_muted || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// if this person wants local loopback add that to the locally injected audio
|
||||
// if there is reverb apply it to local audio and substract the origin samples
|
||||
|
||||
|
||||
if (!_loopbackOutputDevice && _loopbackAudioOutput) {
|
||||
// we didn't have the loopback output device going so set that up now
|
||||
_loopbackOutputDevice = _loopbackAudioOutput->start();
|
||||
|
||||
|
||||
if (!_loopbackOutputDevice) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// do we need to setup a resampler?
|
||||
if (_inputFormat.sampleRate() != _outputFormat.sampleRate() && !_loopbackResampler) {
|
||||
qCDebug(audioclient) << "Attemping to create a resampler for input format to output format for audio loopback.";
|
||||
_loopbackResampler = soxrResamplerFromInputFormatToOutputFormat(_inputFormat, _outputFormat);
|
||||
|
||||
|
||||
if (!_loopbackResampler) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static QByteArray reverbAlone; // Intermediary for local reverb with no echo
|
||||
static QByteArray loopBackByteArray;
|
||||
|
||||
|
||||
int numInputSamples = inputByteArray.size() / sizeof(int16_t);
|
||||
int numLoopbackSamples = numDestinationSamplesRequired(_inputFormat, _outputFormat, numInputSamples);
|
||||
|
||||
|
||||
reverbAlone.resize(numInputSamples * sizeof(int16_t));
|
||||
loopBackByteArray.resize(numLoopbackSamples * sizeof(int16_t));
|
||||
|
||||
|
||||
int16_t* inputSamples = reinterpret_cast<int16_t*>(inputByteArray.data());
|
||||
int16_t* reverbAloneSamples = reinterpret_cast<int16_t*>(reverbAlone.data());
|
||||
int16_t* loopbackSamples = reinterpret_cast<int16_t*>(loopBackByteArray.data());
|
||||
|
||||
|
||||
if (hasReverb) {
|
||||
updateGverbOptions();
|
||||
addReverb(_gverb, inputSamples, reverbAloneSamples, numInputSamples,
|
||||
_inputFormat, !_shouldEchoLocally);
|
||||
}
|
||||
|
||||
|
||||
possibleResampling(_loopbackResampler,
|
||||
(_shouldEchoLocally) ? inputSamples : reverbAloneSamples, loopbackSamples,
|
||||
numInputSamples, numLoopbackSamples,
|
||||
_inputFormat, _outputFormat);
|
||||
|
||||
|
||||
_loopbackOutputDevice->write(loopBackByteArray);
|
||||
}
|
||||
|
||||
|
@ -726,7 +726,7 @@ void AudioClient::handleAudioInput() {
|
|||
int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio);
|
||||
|
||||
QByteArray inputByteArray = _inputDevice->readAll();
|
||||
|
||||
|
||||
// Add audio source injection if enabled
|
||||
if (!_muted && _audioSourceInjectEnabled) {
|
||||
int16_t* inputFrameData = (int16_t*)inputByteArray.data();
|
||||
|
@ -745,11 +745,11 @@ void AudioClient::handleAudioInput() {
|
|||
_sourceGain.render(_inputFrameBuffer); // post gain
|
||||
_inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/);
|
||||
}
|
||||
|
||||
|
||||
handleLocalEchoAndReverb(inputByteArray);
|
||||
|
||||
_inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size());
|
||||
|
||||
|
||||
float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC));
|
||||
_stats.updateInputMsecsRead(audioInputMsecsRead);
|
||||
|
||||
|
@ -763,50 +763,50 @@ void AudioClient::handleAudioInput() {
|
|||
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
|
||||
|
||||
if (!_muted) {
|
||||
|
||||
|
||||
// zero out the monoAudioSamples array and the locally injected audio
|
||||
memset(networkAudioSamples, 0, numNetworkBytes);
|
||||
|
||||
|
||||
// Increment the time since the last clip
|
||||
if (_timeSinceLastClip >= 0.0f) {
|
||||
_timeSinceLastClip += (float) numNetworkSamples / (float) AudioConstants::SAMPLE_RATE;
|
||||
}
|
||||
|
||||
|
||||
int16_t* inputAudioSamples = new int16_t[inputSamplesRequired];
|
||||
_inputRingBuffer.readSamples(inputAudioSamples, inputSamplesRequired);
|
||||
|
||||
|
||||
possibleResampling(_inputToNetworkResampler,
|
||||
inputAudioSamples, networkAudioSamples,
|
||||
inputSamplesRequired, numNetworkSamples,
|
||||
_inputFormat, _desiredInputFormat);
|
||||
|
||||
|
||||
delete[] inputAudioSamples;
|
||||
|
||||
|
||||
// only impose the noise gate and perform tone injection if we are sending mono audio
|
||||
if (!_isStereoInput && !_audioSourceInjectEnabled && _isNoiseGateEnabled) {
|
||||
_inputGate.gateSamples(networkAudioSamples, numNetworkSamples);
|
||||
|
||||
|
||||
// if we performed the noise gate we can get values from it instead of enumerating the samples again
|
||||
_lastInputLoudness = _inputGate.getLastLoudness();
|
||||
|
||||
|
||||
if (_inputGate.clippedInLastFrame()) {
|
||||
_timeSinceLastClip = 0.0f;
|
||||
}
|
||||
} else {
|
||||
float loudness = 0.0f;
|
||||
|
||||
|
||||
for (int i = 0; i < numNetworkSamples; i++) {
|
||||
int thisSample = std::abs(networkAudioSamples[i]);
|
||||
loudness += (float) thisSample;
|
||||
|
||||
|
||||
if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) {
|
||||
_timeSinceLastClip = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_lastInputLoudness = fabs(loudness / numNetworkSamples);
|
||||
}
|
||||
|
||||
|
||||
emit inputReceived(QByteArray(reinterpret_cast<const char*>(networkAudioSamples),
|
||||
AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * sizeof(AudioConstants::AudioSample)));
|
||||
|
||||
|
@ -814,13 +814,13 @@ void AudioClient::handleAudioInput() {
|
|||
// our input loudness is 0, since we're muted
|
||||
_lastInputLoudness = 0;
|
||||
_timeSinceLastClip = 0.0f;
|
||||
|
||||
|
||||
_inputRingBuffer.shiftReadPosition(inputSamplesRequired);
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
|
||||
|
||||
if (audioMixer && audioMixer->getActiveSocket()) {
|
||||
glm::vec3 headPosition = _positionGetter();
|
||||
glm::quat headOrientation = _orientationGetter();
|
||||
|
@ -852,7 +852,7 @@ void AudioClient::handleAudioInput() {
|
|||
// memcpy the three float positions
|
||||
memcpy(currentPacketPtr, &headPosition, sizeof(headPosition));
|
||||
currentPacketPtr += (sizeof(headPosition));
|
||||
|
||||
|
||||
// memcpy our orientation
|
||||
memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation));
|
||||
currentPacketPtr += sizeof(headOrientation);
|
||||
|
@ -864,7 +864,7 @@ void AudioClient::handleAudioInput() {
|
|||
// memcpy the three float positions
|
||||
memcpy(currentPacketPtr, &headPosition, sizeof(headPosition));
|
||||
currentPacketPtr += (sizeof(headPosition));
|
||||
|
||||
|
||||
// memcpy our orientation
|
||||
memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation));
|
||||
currentPacketPtr += sizeof(headOrientation);
|
||||
|
@ -875,6 +875,8 @@ void AudioClient::handleAudioInput() {
|
|||
|
||||
_stats.sentPacket();
|
||||
|
||||
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket);
|
||||
|
||||
int packetBytes = currentPacketPtr - audioDataPacket;
|
||||
nodeList->writeDatagram(audioDataPacket, packetBytes, audioMixer);
|
||||
_outgoingAvatarAudioSequenceNumber++;
|
||||
|
@ -890,7 +892,7 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr
|
|||
outputBuffer.resize(numDeviceOutputSamples * sizeof(int16_t));
|
||||
|
||||
const int16_t* receivedSamples = reinterpret_cast<const int16_t*>(inputBuffer.data());
|
||||
|
||||
|
||||
// copy the packet from the RB to the output
|
||||
possibleResampling(_networkToOutputResampler, receivedSamples,
|
||||
reinterpret_cast<int16_t*>(outputBuffer.data()),
|
||||
|
@ -900,20 +902,20 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr
|
|||
|
||||
void AudioClient::sendMuteEnvironmentPacket() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
||||
QByteArray mutePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeMuteEnvironment);
|
||||
int headerSize = mutePacket.size();
|
||||
|
||||
|
||||
const float MUTE_RADIUS = 50;
|
||||
|
||||
|
||||
glm::vec3 currentSourcePosition = _positionGetter();
|
||||
mutePacket.resize(mutePacket.size() + sizeof(glm::vec3) + sizeof(float));
|
||||
memcpy(mutePacket.data() + headerSize, ¤tSourcePosition, sizeof(glm::vec3));
|
||||
memcpy(mutePacket.data() + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float));
|
||||
|
||||
|
||||
// grab our audio mixer from the NodeList, if it exists
|
||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||
|
||||
|
||||
if (audioMixer) {
|
||||
// send off this mute packet
|
||||
nodeList->writeDatagram(mutePacket, audioMixer);
|
||||
|
@ -921,6 +923,8 @@ void AudioClient::sendMuteEnvironmentPacket() {
|
|||
}
|
||||
|
||||
void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) {
|
||||
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
|
||||
|
||||
if (_audioOutput) {
|
||||
// Audio output must exist and be correctly set up if we're going to process received audio
|
||||
_receivedAudioStream.parseData(audioByteArray);
|
||||
|
@ -930,11 +934,11 @@ void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) {
|
|||
void AudioClient::parseAudioEnvironmentData(const QByteArray &packet) {
|
||||
int numBytesPacketHeader = numBytesForPacketHeader(packet);
|
||||
const char* dataAt = packet.constData() + numBytesPacketHeader;
|
||||
|
||||
|
||||
char bitset;
|
||||
memcpy(&bitset, dataAt, sizeof(char));
|
||||
dataAt += sizeof(char);
|
||||
|
||||
|
||||
bool hasReverb = oneAtBit(bitset, HAS_REVERB_BIT);;
|
||||
if (hasReverb) {
|
||||
float reverbTime, wetLevel;
|
||||
|
@ -956,13 +960,13 @@ void AudioClient::toggleMute() {
|
|||
void AudioClient::setIsStereoInput(bool isStereoInput) {
|
||||
if (isStereoInput != _isStereoInput) {
|
||||
_isStereoInput = isStereoInput;
|
||||
|
||||
|
||||
if (_isStereoInput) {
|
||||
_desiredInputFormat.setChannelCount(2);
|
||||
} else {
|
||||
_desiredInputFormat.setChannelCount(1);
|
||||
}
|
||||
|
||||
|
||||
// change in channel count for desired input format, restart the input device
|
||||
switchInputToAudioDevice(_inputAudioDeviceName);
|
||||
}
|
||||
|
@ -976,7 +980,7 @@ void AudioClient::selectAudioSourcePinkNoise() {
|
|||
_noiseSourceEnabled = true;
|
||||
_toneSourceEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
void AudioClient::selectAudioSourceSine440() {
|
||||
_toneSourceEnabled = true;
|
||||
_noiseSourceEnabled = false;
|
||||
|
@ -986,24 +990,24 @@ bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) {
|
|||
if (injector->getLocalBuffer()) {
|
||||
QAudioFormat localFormat = _desiredOutputFormat;
|
||||
localFormat.setChannelCount(isStereo ? 2 : 1);
|
||||
|
||||
|
||||
QAudioOutput* localOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName),
|
||||
localFormat,
|
||||
injector->getLocalBuffer());
|
||||
|
||||
|
||||
// move the localOutput to the same thread as the local injector buffer
|
||||
localOutput->moveToThread(injector->getLocalBuffer()->thread());
|
||||
|
||||
|
||||
// have it be stopped when that local buffer is about to close
|
||||
connect(injector->getLocalBuffer(), &AudioInjectorLocalBuffer::bufferEmpty, localOutput, &QAudioOutput::stop);
|
||||
connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop);
|
||||
|
||||
|
||||
qCDebug(audioclient) << "Starting QAudioOutput for local injector" << localOutput;
|
||||
|
||||
|
||||
localOutput->start(injector->getLocalBuffer());
|
||||
return localOutput->state() == QAudio::ActiveState;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1015,7 +1019,7 @@ void AudioClient::outputFormatChanged() {
|
|||
|
||||
bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) {
|
||||
bool supportedFormat = false;
|
||||
|
||||
|
||||
// cleanup any previously initialized device
|
||||
if (_audioInput) {
|
||||
// The call to stop() causes _inputDevice to be destructed.
|
||||
|
@ -1030,7 +1034,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
|
|||
|
||||
_inputAudioDeviceName = "";
|
||||
}
|
||||
|
||||
|
||||
if (_inputToNetworkResampler) {
|
||||
// if we were using an input to network resampler, delete it here
|
||||
soxr_delete(_inputToNetworkResampler);
|
||||
|
@ -1040,36 +1044,36 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
|
|||
if (!inputDeviceInfo.isNull()) {
|
||||
qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available.";
|
||||
_inputAudioDeviceName = inputDeviceInfo.deviceName().trimmed();
|
||||
|
||||
|
||||
if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) {
|
||||
qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat;
|
||||
|
||||
|
||||
// we've got the best we can get for input
|
||||
// if required, setup a soxr resampler for this input to our desired network format
|
||||
if (_inputFormat != _desiredInputFormat
|
||||
&& _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) {
|
||||
qCDebug(audioclient) << "Attemping to create a soxr resampler for input format to network format.";
|
||||
_inputToNetworkResampler = soxrResamplerFromInputFormatToOutputFormat(_inputFormat, _desiredInputFormat);
|
||||
|
||||
|
||||
if (!_inputToNetworkResampler) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qCDebug(audioclient) << "No resampling required for audio input to match desired network format.";
|
||||
}
|
||||
|
||||
|
||||
// if the user wants stereo but this device can't provide then bail
|
||||
if (!_isStereoInput || _inputFormat.channelCount() == 2) {
|
||||
if (!_isStereoInput || _inputFormat.channelCount() == 2) {
|
||||
_audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this);
|
||||
_numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat);
|
||||
_audioInput->setBufferSize(_numInputCallbackBytes);
|
||||
|
||||
|
||||
// how do we want to handle input working, but output not working?
|
||||
int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes);
|
||||
_inputRingBuffer.resizeForFrameSize(numFrameSamples);
|
||||
|
||||
|
||||
_inputDevice = _audioInput->start();
|
||||
|
||||
|
||||
if (_inputDevice) {
|
||||
connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput()));
|
||||
supportedFormat = true;
|
||||
|
@ -1079,7 +1083,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return supportedFormat;
|
||||
}
|
||||
|
||||
|
@ -1117,7 +1121,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
// cleanup any previously initialized device
|
||||
if (_audioOutput) {
|
||||
_audioOutput->stop();
|
||||
|
||||
|
||||
delete _audioOutput;
|
||||
_audioOutput = NULL;
|
||||
|
||||
|
@ -1125,13 +1129,13 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
delete _loopbackAudioOutput;
|
||||
_loopbackAudioOutput = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (_networkToOutputResampler) {
|
||||
// if we were using an input to network resampler, delete it here
|
||||
soxr_delete(_networkToOutputResampler);
|
||||
_networkToOutputResampler = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (_loopbackResampler) {
|
||||
// if we were using an input to output resample, delete it here
|
||||
soxr_delete(_loopbackResampler);
|
||||
|
@ -1141,24 +1145,24 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
if (!outputDeviceInfo.isNull()) {
|
||||
qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available.";
|
||||
_outputAudioDeviceName = outputDeviceInfo.deviceName().trimmed();
|
||||
|
||||
|
||||
if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) {
|
||||
qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat;
|
||||
|
||||
|
||||
// we've got the best we can get for input
|
||||
// if required, setup a soxr resampler for this input to our desired network format
|
||||
if (_desiredOutputFormat != _outputFormat
|
||||
&& _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) {
|
||||
qCDebug(audioclient) << "Attemping to create a resampler for network format to output format.";
|
||||
_networkToOutputResampler = soxrResamplerFromInputFormatToOutputFormat(_desiredOutputFormat, _outputFormat);
|
||||
|
||||
|
||||
if (!_networkToOutputResampler) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qCDebug(audioclient) << "No resampling required for network output to match actual output format.";
|
||||
}
|
||||
|
||||
|
||||
outputFormatChanged();
|
||||
|
||||
// setup our general output device for audio-mixer audio
|
||||
|
@ -1168,20 +1172,20 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
|
|||
connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify);
|
||||
|
||||
qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / sizeof(int16_t) / (float)_outputFrameSize;
|
||||
|
||||
|
||||
_audioOutputIODevice.start();
|
||||
_audioOutput->start(&_audioOutputIODevice);
|
||||
|
||||
// setup a loopback audio output device
|
||||
_loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this);
|
||||
|
||||
|
||||
|
||||
_timeSinceLastReceived.start();
|
||||
|
||||
supportedFormat = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return supportedFormat;
|
||||
}
|
||||
|
||||
|
@ -1202,7 +1206,7 @@ void AudioClient::setOutputBufferSize(int numFrames) {
|
|||
|
||||
// The following constant is operating system dependent due to differences in
|
||||
// the way input audio is handled. The audio input buffer size is inversely
|
||||
// proportional to the accelerator ratio.
|
||||
// proportional to the accelerator ratio.
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 0.1f;
|
||||
|
@ -1226,7 +1230,7 @@ int AudioClient::calculateNumberOfInputCallbackBytes(const QAudioFormat& format)
|
|||
}
|
||||
|
||||
float AudioClient::calculateDeviceToNetworkInputRatio() const {
|
||||
float inputToNetworkInputRatio = (int)((_numInputCallbackBytes
|
||||
float inputToNetworkInputRatio = (int)((_numInputCallbackBytes
|
||||
* CALLBACK_ACCELERATOR_RATIO
|
||||
/ AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL) + 0.5f);
|
||||
|
||||
|
@ -1257,7 +1261,7 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
int samplesRequested = maxSize / sizeof(int16_t);
|
||||
int samplesPopped;
|
||||
int bytesWritten;
|
||||
|
||||
|
||||
if ((samplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) {
|
||||
AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput();
|
||||
lastPopOutput.readSamples((int16_t*)data, samplesPopped);
|
||||
|
|
|
@ -301,7 +301,7 @@ public:
|
|||
int getReceiveRate() const;
|
||||
|
||||
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
|
||||
Q_INVOKABLE const glm::vec3& getVelocity() const { return _velocity; }
|
||||
Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; }
|
||||
const glm::vec3& getTargetVelocity() const { return _targetVelocity; }
|
||||
|
||||
bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; }
|
||||
|
|
|
@ -12,4 +12,9 @@ find_package(Bullet REQUIRED)
|
|||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${BULLET_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
||||
|
||||
add_dependency_external_projects(polyvox)
|
||||
find_package(PolyVox REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${POLYVOX_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${POLYVOX_LIBRARIES})
|
||||
|
||||
link_hifi_libraries(shared gpu script-engine render-utils)
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "RenderableWebEntityItem.h"
|
||||
#include "RenderableZoneEntityItem.h"
|
||||
#include "RenderableLineEntityItem.h"
|
||||
#include "RenderablePolyVoxEntityItem.h"
|
||||
#include "EntitiesRendererLogging.h"
|
||||
|
||||
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||
|
@ -65,6 +66,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Zone, RenderableZoneEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Line, RenderableLineEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(PolyVox, RenderablePolyVoxEntityItem::factory)
|
||||
|
||||
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
|
||||
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
|
||||
|
@ -113,7 +115,6 @@ void EntityTreeRenderer::init() {
|
|||
connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity);
|
||||
connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::addingEntity);
|
||||
connect(entityTree, &EntityTree::entityScriptChanging, this, &EntityTreeRenderer::entitySciptChanging);
|
||||
connect(entityTree, &EntityTree::changingEntityID, this, &EntityTreeRenderer::changingEntityID);
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::shutdown() {
|
||||
|
@ -138,7 +139,7 @@ void EntityTreeRenderer::errorInLoadingScript(const QUrl& url) {
|
|||
}
|
||||
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID, bool isPreload) {
|
||||
EntityItem* entity = static_cast<EntityTree*>(_tree)->findEntityByEntityItemID(entityItemID);
|
||||
EntityItemPointer entity = static_cast<EntityTree*>(_tree)->findEntityByEntityItemID(entityItemID);
|
||||
return loadEntityScript(entity, isPreload);
|
||||
}
|
||||
|
||||
|
@ -190,7 +191,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe
|
|||
}
|
||||
|
||||
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity, bool isPreload) {
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool isPreload) {
|
||||
if (_shuttingDown) {
|
||||
return QScriptValue(); // since we're shutting down, we don't load any more scripts
|
||||
}
|
||||
|
@ -324,7 +325,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
|
|||
|
||||
if (avatarPosition != _lastAvatarPosition) {
|
||||
float radius = 1.0f; // for now, assume 1 meter radius
|
||||
QVector<const EntityItem*> foundEntities;
|
||||
QVector<EntityItemPointer> foundEntities;
|
||||
QVector<EntityItemID> entitiesContainingAvatar;
|
||||
|
||||
// find the entities near us
|
||||
|
@ -332,7 +333,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
|
|||
static_cast<EntityTree*>(_tree)->findEntities(avatarPosition, radius, foundEntities);
|
||||
|
||||
// create a list of entities that actually contain the avatar's position
|
||||
foreach(const EntityItem* entity, foundEntities) {
|
||||
foreach(EntityItemPointer entity, foundEntities) {
|
||||
if (entity->contains(avatarPosition)) {
|
||||
entitiesContainingAvatar << entity->getEntityItemID();
|
||||
}
|
||||
|
@ -517,12 +518,12 @@ void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode,
|
|||
deleteReleasedModels(); // seems like as good as any other place to do some memory cleanup
|
||||
}
|
||||
|
||||
const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(const EntityItem* entityItem) {
|
||||
const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) {
|
||||
const FBXGeometry* result = NULL;
|
||||
|
||||
if (entityItem->getType() == EntityTypes::Model) {
|
||||
const RenderableModelEntityItem* constModelEntityItem = dynamic_cast<const RenderableModelEntityItem*>(entityItem);
|
||||
RenderableModelEntityItem* modelEntityItem = const_cast<RenderableModelEntityItem*>(constModelEntityItem);
|
||||
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
||||
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
||||
assert(modelEntityItem); // we need this!!!
|
||||
Model* model = modelEntityItem->getModel(this);
|
||||
if (model) {
|
||||
|
@ -532,23 +533,23 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(const EntityItem* en
|
|||
return result;
|
||||
}
|
||||
|
||||
const Model* EntityTreeRenderer::getModelForEntityItem(const EntityItem* entityItem) {
|
||||
const Model* EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityItem) {
|
||||
const Model* result = NULL;
|
||||
if (entityItem->getType() == EntityTypes::Model) {
|
||||
const RenderableModelEntityItem* constModelEntityItem = dynamic_cast<const RenderableModelEntityItem*>(entityItem);
|
||||
RenderableModelEntityItem* modelEntityItem = const_cast<RenderableModelEntityItem*>(constModelEntityItem);
|
||||
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
||||
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
||||
result = modelEntityItem->getModel(this);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(const EntityItem* entityItem) {
|
||||
const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemPointer entityItem) {
|
||||
const FBXGeometry* result = NULL;
|
||||
|
||||
if (entityItem->getType() == EntityTypes::Model) {
|
||||
const RenderableModelEntityItem* constModelEntityItem = dynamic_cast<const RenderableModelEntityItem*>(entityItem);
|
||||
if (constModelEntityItem->hasCompoundShapeURL()) {
|
||||
RenderableModelEntityItem* modelEntityItem = const_cast<RenderableModelEntityItem*>(constModelEntityItem);
|
||||
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
||||
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
||||
if (modelEntityItem->hasCompoundShapeURL()) {
|
||||
Model* model = modelEntityItem->getModel(this);
|
||||
if (model) {
|
||||
const QSharedPointer<NetworkGeometry> collisionNetworkGeometry = model->getCollisionGeometry();
|
||||
|
@ -615,7 +616,7 @@ void EntityTreeRenderer::renderElementProxy(EntityTreeElement* entityTreeElement
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* args) {
|
||||
void EntityTreeRenderer::renderProxies(EntityItemPointer entity, RenderArgs* args) {
|
||||
bool isShadowMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE;
|
||||
if (!isShadowMode && _displayModelBounds) {
|
||||
PerformanceTimer perfTimer("renderProxies");
|
||||
|
@ -674,7 +675,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
// we need to iterate the actual entityItems of the element
|
||||
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||
|
||||
QList<EntityItem*>& entityItems = entityTreeElement->getEntities();
|
||||
EntityItems& entityItems = entityTreeElement->getEntities();
|
||||
|
||||
|
||||
uint16_t numberOfEntities = entityItems.size();
|
||||
|
@ -686,7 +687,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
}
|
||||
|
||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
EntityItem* entityItem = entityItems[i];
|
||||
EntityItemPointer entityItem = entityItems[i];
|
||||
|
||||
if (entityItem->isVisible()) {
|
||||
|
||||
|
@ -696,17 +697,17 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
|||
float entityVolumeEstimate = entityItem->getVolumeEstimate();
|
||||
if (entityVolumeEstimate < _bestZoneVolume) {
|
||||
_bestZoneVolume = entityVolumeEstimate;
|
||||
_bestZone = dynamic_cast<const ZoneEntityItem*>(entityItem);
|
||||
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(entityItem);
|
||||
} else if (entityVolumeEstimate == _bestZoneVolume) {
|
||||
if (!_bestZone) {
|
||||
_bestZoneVolume = entityVolumeEstimate;
|
||||
_bestZone = dynamic_cast<const ZoneEntityItem*>(entityItem);
|
||||
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(entityItem);
|
||||
} else {
|
||||
// in the case of the volume being equal, we will use the
|
||||
// EntityItemID to deterministically pick one entity over the other
|
||||
if (entityItem->getEntityItemID() < _bestZone->getEntityItemID()) {
|
||||
_bestZoneVolume = entityVolumeEstimate;
|
||||
_bestZone = dynamic_cast<const ZoneEntityItem*>(entityItem);
|
||||
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(entityItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -835,7 +836,7 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
|
|||
EntityTree* entityTree = static_cast<EntityTree*>(_tree);
|
||||
|
||||
OctreeElement* element;
|
||||
EntityItem* intersectedEntity = NULL;
|
||||
EntityItemPointer intersectedEntity = NULL;
|
||||
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
|
||||
(void**)&intersectedEntity, lockType, &result.accurate,
|
||||
precisionPicking);
|
||||
|
@ -873,6 +874,7 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS
|
|||
|
||||
connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity);
|
||||
connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity);
|
||||
connect(this, &EntityTreeRenderer::collisionWithEntity, entityScriptingInterface, &EntityScriptingInterface::collisionWithEntity);
|
||||
}
|
||||
|
||||
QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) {
|
||||
|
@ -1101,20 +1103,23 @@ void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) {
|
|||
}
|
||||
|
||||
|
||||
void EntityTreeRenderer::changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID) {
|
||||
if (_entityScripts.contains(oldEntityID)) {
|
||||
EntityScriptDetails details = _entityScripts[oldEntityID];
|
||||
_entityScripts.remove(oldEntityID);
|
||||
_entityScripts[newEntityID] = details;
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision) {
|
||||
EntityItem* entity = entityTree->findEntityByEntityItemID(id);
|
||||
EntityItemPointer entity = entityTree->findEntityByEntityItemID(id);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
QUuid simulatorID = entity->getSimulatorID();
|
||||
if (simulatorID.isNull()) {
|
||||
// Can be null if it has never moved since being created or coming out of persistence.
|
||||
// However, for there to be a collission, one of the two objects must be moving.
|
||||
const EntityItemID& otherID = (id == collision.idA) ? collision.idB : collision.idA;
|
||||
EntityItemPointer otherEntity = entityTree->findEntityByEntityItemID(otherID);
|
||||
if (!otherEntity) {
|
||||
return;
|
||||
}
|
||||
simulatorID = otherEntity->getSimulatorID();
|
||||
}
|
||||
|
||||
if (simulatorID.isNull() || (simulatorID != myNodeID)) {
|
||||
return; // Only one injector per simulation, please.
|
||||
}
|
||||
|
@ -1128,7 +1133,7 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
|
|||
const float energy = mass * linearVelocity * linearVelocity / 2.0f;
|
||||
const glm::vec3 position = collision.contactPoint;
|
||||
const float COLLISION_ENERGY_AT_FULL_VOLUME = 0.5f;
|
||||
const float COLLISION_MINIMUM_VOLUME = 0.001f;
|
||||
const float COLLISION_MINIMUM_VOLUME = 0.005f;
|
||||
const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME);
|
||||
if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) {
|
||||
return;
|
||||
|
@ -1144,7 +1149,7 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
|
|||
}
|
||||
|
||||
// This is a hack. Quiet sound aren't really heard at all, so we compress everything to the range [1-c, 1], if we play it all.
|
||||
const float COLLISION_SOUND_COMPRESSION_RANGE = 0.7f;
|
||||
const float COLLISION_SOUND_COMPRESSION_RANGE = 1.0f; // This section could be removed when the value is 1, but let's see how it goes.
|
||||
float volume = energyFactorOfFull;
|
||||
volume = (volume * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE);
|
||||
|
||||
|
@ -1175,7 +1180,7 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
|
|||
return;
|
||||
}
|
||||
// Don't respond to small continuous contacts.
|
||||
const float COLLISION_MINUMUM_PENETRATION = 0.005f;
|
||||
const float COLLISION_MINUMUM_PENETRATION = 0.002f;
|
||||
if ((collision.type != CONTACT_EVENT_TYPE_START) && (glm::length(collision.penetration) < COLLISION_MINUMUM_PENETRATION)) {
|
||||
return;
|
||||
}
|
||||
|
@ -1187,6 +1192,7 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
|
|||
playEntityCollisionSound(myNodeID, entityTree, idB, collision);
|
||||
|
||||
// And now the entity scripts
|
||||
emit collisionWithEntity(idA, idB, collision);
|
||||
QScriptValue entityScriptA = loadEntityScript(idA);
|
||||
if (entityScriptA.property("collisionWithEntity").isValid()) {
|
||||
QScriptValueList args;
|
||||
|
@ -1196,6 +1202,7 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons
|
|||
entityScriptA.property("collisionWithEntity").call(entityScriptA, args);
|
||||
}
|
||||
|
||||
emit collisionWithEntity(idB, idA, collision);
|
||||
QScriptValue entityScriptB = loadEntityScript(idB);
|
||||
if (entityScriptB.property("collisionWithEntity").isValid()) {
|
||||
QScriptValueList args;
|
||||
|
|
|
@ -62,9 +62,9 @@ public:
|
|||
RenderArgs::RenderSide renderSide = RenderArgs::MONO,
|
||||
RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE);
|
||||
|
||||
virtual const FBXGeometry* getGeometryForEntity(const EntityItem* entityItem);
|
||||
virtual const Model* getModelForEntityItem(const EntityItem* entityItem);
|
||||
virtual const FBXGeometry* getCollisionGeometryForEntity(const EntityItem* entityItem);
|
||||
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem);
|
||||
virtual const Model* getModelForEntityItem(EntityItemPointer entityItem);
|
||||
virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem);
|
||||
|
||||
/// clears the tree
|
||||
virtual void clear();
|
||||
|
@ -107,11 +107,11 @@ signals:
|
|||
|
||||
void enterEntity(const EntityItemID& entityItemID);
|
||||
void leaveEntity(const EntityItemID& entityItemID);
|
||||
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
||||
public slots:
|
||||
void addingEntity(const EntityItemID& entityID);
|
||||
void deletingEntity(const EntityItemID& entityID);
|
||||
void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID);
|
||||
void entitySciptChanging(const EntityItemID& entityID);
|
||||
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
||||
|
@ -130,7 +130,7 @@ private:
|
|||
void checkAndCallUnload(const EntityItemID& entityID);
|
||||
|
||||
QList<Model*> _releasedModels;
|
||||
void renderProxies(const EntityItem* entity, RenderArgs* args);
|
||||
void renderProxies(EntityItemPointer entity, RenderArgs* args);
|
||||
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
|
||||
bool precisionPicking);
|
||||
|
||||
|
@ -147,7 +147,7 @@ private:
|
|||
ScriptEngine* _entitiesScriptEngine;
|
||||
ScriptEngine* _sandboxScriptEngine;
|
||||
|
||||
QScriptValue loadEntityScript(EntityItem* entity, bool isPreload = false);
|
||||
QScriptValue loadEntityScript(EntityItemPointer entity, bool isPreload = false);
|
||||
QScriptValue loadEntityScript(const EntityItemID& entityItemID, bool isPreload = false);
|
||||
QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID);
|
||||
QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& url);
|
||||
|
@ -173,7 +173,7 @@ private:
|
|||
QMultiMap<QUrl, EntityItemID> _waitingOnPreload;
|
||||
|
||||
bool _hasPreviousZone = false;
|
||||
const ZoneEntityItem* _bestZone;
|
||||
std::shared_ptr<ZoneEntityItem> _bestZone;
|
||||
float _bestZoneVolume;
|
||||
|
||||
glm::vec3 _previousKeyLightColor;
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
#include "RenderableBoxEntityItem.h"
|
||||
|
||||
EntityItem* RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableBoxEntityItem(entityID, properties);
|
||||
EntityItemPointer RenderableBoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new RenderableBoxEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
void RenderableBoxEntityItem::render(RenderArgs* args) {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
class RenderableBoxEntityItem : public BoxEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
RenderableBoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
BoxEntityItem(entityItemID, properties)
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
#include "RenderableLightEntityItem.h"
|
||||
|
||||
EntityItem* RenderableLightEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableLightEntityItem(entityID, properties);
|
||||
EntityItemPointer RenderableLightEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new RenderableLightEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
void RenderableLightEntityItem::render(RenderArgs* args) {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class RenderableLightEntityItem : public LightEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
RenderableLightEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
LightEntityItem(entityItemID, properties)
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
#include "RenderableLineEntityItem.h"
|
||||
|
||||
EntityItem* RenderableLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableLineEntityItem(entityID, properties);
|
||||
EntityItemPointer RenderableLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new RenderableLineEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
void RenderableLineEntityItem::render(RenderArgs* args) {
|
||||
|
@ -47,5 +47,6 @@ void RenderableLineEntityItem::render(RenderArgs* args) {
|
|||
}
|
||||
geometryCache->renderVertices(gpu::LINE_STRIP, _lineVerticesID);
|
||||
glPopMatrix();
|
||||
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
class RenderableLineEntityItem : public LineEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
RenderableLineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
LineEntityItem(entityItemID, properties),
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
#include "EntitiesRendererLogging.h"
|
||||
#include "RenderableModelEntityItem.h"
|
||||
|
||||
EntityItem* RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableModelEntityItem(entityID, properties);
|
||||
EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new RenderableModelEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
RenderableModelEntityItem::~RenderableModelEntityItem() {
|
||||
|
@ -112,8 +112,6 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
PerformanceTimer perfTimer("RMEIrender");
|
||||
assert(getType() == EntityTypes::Model);
|
||||
|
||||
bool drawAsModel = hasModel();
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
|
||||
|
@ -125,8 +123,7 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
highlightSimulationOwnership = (getSimulatorID() == myNodeID);
|
||||
}
|
||||
|
||||
bool didDraw = false;
|
||||
if (drawAsModel && !highlightSimulationOwnership) {
|
||||
if (hasModel()) {
|
||||
remapTextures();
|
||||
glPushMatrix();
|
||||
{
|
||||
|
@ -179,19 +176,20 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
if (args && (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE)) {
|
||||
if (movingOrAnimating) {
|
||||
_model->renderInScene(alpha, args);
|
||||
didDraw = true;
|
||||
}
|
||||
} else {
|
||||
_model->renderInScene(alpha, args);
|
||||
didDraw = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
if (!didDraw) {
|
||||
if (highlightSimulationOwnership) {
|
||||
glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor);
|
||||
}
|
||||
} else {
|
||||
glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
RenderableDebugableEntityItem::renderBoundingBox(this, args, 0.0f, greenColor);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class EntityTreeRenderer;
|
|||
|
||||
class RenderableModelEntityItem : public ModelEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
RenderableModelEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
ModelEntityItem(entityItemID, properties),
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
#include "RenderableParticleEffectEntityItem.h"
|
||||
|
||||
EntityItem* RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableParticleEffectEntityItem(entityID, properties);
|
||||
EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new RenderableParticleEffectEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
virtual void render(RenderArgs* args);
|
||||
|
||||
|
|
336
libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
Normal file
336
libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
Normal file
|
@ -0,0 +1,336 @@
|
|||
//
|
||||
// RenderablePolyVoxEntityItem.cpp
|
||||
// libraries/entities-renderer/src/
|
||||
//
|
||||
// Created by Seth Alves on 5/19/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
|
||||
//
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#include <gpu/GPUConfig.h>
|
||||
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <Model.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include <PolyVoxCore/CubicSurfaceExtractorWithNormals.h>
|
||||
#include <PolyVoxCore/MarchingCubesSurfaceExtractor.h>
|
||||
#include <PolyVoxCore/SurfaceMesh.h>
|
||||
#include <PolyVoxCore/SimpleVolume.h>
|
||||
#include <PolyVoxCore/Raycast.h>
|
||||
#include <PolyVoxCore/Material.h>
|
||||
|
||||
#include "model/Geometry.h"
|
||||
#include "gpu/GLBackend.h"
|
||||
#include "EntityTreeRenderer.h"
|
||||
#include "RenderablePolyVoxEntityItem.h"
|
||||
|
||||
|
||||
EntityItemPointer RenderablePolyVoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new RenderablePolyVoxEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() {
|
||||
delete _volData;
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) {
|
||||
|
||||
if (_volData && voxelVolumeSize == _voxelVolumeSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "resetting voxel-space size";
|
||||
|
||||
PolyVoxEntityItem::setVoxelVolumeSize(voxelVolumeSize);
|
||||
|
||||
if (_volData) {
|
||||
delete _volData;
|
||||
}
|
||||
|
||||
PolyVox::Vector3DInt32 lowCorner(0, 0, 0);
|
||||
PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize[0] - 1, // -1 because these corners are inclusive
|
||||
_voxelVolumeSize[1] - 1,
|
||||
_voxelVolumeSize[2] - 1);
|
||||
|
||||
_volData = new PolyVox::SimpleVolume<uint8_t>(PolyVox::Region(lowCorner, highCorner));
|
||||
}
|
||||
|
||||
|
||||
void RenderablePolyVoxEntityItem::setVoxelData(QByteArray voxelData) {
|
||||
if (voxelData == _voxelData) {
|
||||
return;
|
||||
}
|
||||
PolyVoxEntityItem::setVoxelData(voxelData);
|
||||
decompressVolumeData();
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 RenderablePolyVoxEntityItem::voxelToWorldMatrix() const {
|
||||
glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units
|
||||
glm::mat4 scaled = glm::scale(glm::mat4(), scale);
|
||||
glm::mat4 centerToCorner = glm::translate(scaled, _voxelVolumeSize / -2.0f);
|
||||
glm::mat4 rotation = glm::mat4_cast(_rotation);
|
||||
glm::mat4 translation = glm::translate(getCenter());
|
||||
return translation * rotation * centerToCorner;
|
||||
}
|
||||
|
||||
glm::mat4 RenderablePolyVoxEntityItem::worldToVoxelMatrix() const {
|
||||
glm::mat4 worldToModelMatrix = glm::inverse(voxelToWorldMatrix());
|
||||
return worldToModelMatrix;
|
||||
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) {
|
||||
// This three-level for loop iterates over every voxel in the volume
|
||||
for (int z = 0; z < _volData->getDepth(); z++) {
|
||||
for (int y = 0; y < _volData->getHeight(); y++) {
|
||||
for (int x = 0; x < _volData->getWidth(); x++) {
|
||||
// Store our current position as a vector...
|
||||
glm::vec3 pos(x, y, z);
|
||||
// And compute how far the current position is from the center of the volume
|
||||
float fDistToCenter = glm::distance(pos, center);
|
||||
// If the current voxel is less than 'radius' units from the center then we make it solid.
|
||||
if (fDistToCenter <= radius) {
|
||||
_volData->setVoxelAt(x, y, z, toValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
compressVolumeData();
|
||||
_needsModelReload = true;
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) {
|
||||
// glm::vec3 centerVoxelCoords = worldToVoxelCoordinates(centerWorldCoords);
|
||||
glm::vec4 centerVoxelCoords = worldToVoxelMatrix() * glm::vec4(centerWorldCoords, 1.0f);
|
||||
glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units
|
||||
float scaleY = scale[0];
|
||||
float radiusVoxelCoords = radiusWorldCoords / scaleY;
|
||||
setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue);
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::getModel() {
|
||||
if (!_volData) {
|
||||
// this will cause the allocation of _volData
|
||||
setVoxelVolumeSize(_voxelVolumeSize);
|
||||
}
|
||||
|
||||
// A mesh object to hold the result of surface extraction
|
||||
PolyVox::SurfaceMesh<PolyVox::PositionMaterialNormal> polyVoxMesh;
|
||||
|
||||
switch (_voxelSurfaceStyle) {
|
||||
case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: {
|
||||
PolyVox::MarchingCubesSurfaceExtractor<PolyVox::SimpleVolume<uint8_t>> surfaceExtractor
|
||||
(_volData, _volData->getEnclosingRegion(), &polyVoxMesh);
|
||||
surfaceExtractor.execute();
|
||||
break;
|
||||
}
|
||||
case PolyVoxEntityItem::SURFACE_CUBIC: {
|
||||
PolyVox::CubicSurfaceExtractorWithNormals<PolyVox::SimpleVolume<uint8_t>> surfaceExtractor
|
||||
(_volData, _volData->getEnclosingRegion(), &polyVoxMesh);
|
||||
surfaceExtractor.execute();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// convert PolyVox mesh to a Sam mesh
|
||||
model::Mesh* mesh = new model::Mesh();
|
||||
model::MeshPointer meshPtr(mesh);
|
||||
|
||||
const std::vector<uint32_t>& vecIndices = polyVoxMesh.getIndices();
|
||||
auto indexBuffer = new gpu::Buffer(vecIndices.size() * sizeof(uint32_t), (gpu::Byte*)vecIndices.data());
|
||||
auto indexBufferPtr = gpu::BufferPointer(indexBuffer);
|
||||
mesh->setIndexBuffer(gpu::BufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW)));
|
||||
|
||||
|
||||
const std::vector<PolyVox::PositionMaterialNormal>& vecVertices = polyVoxMesh.getVertices();
|
||||
auto vertexBuffer = new gpu::Buffer(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal),
|
||||
(gpu::Byte*)vecVertices.data());
|
||||
auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer);
|
||||
mesh->setVertexBuffer(gpu::BufferView(vertexBufferPtr,
|
||||
0,
|
||||
vertexBufferPtr->getSize() - sizeof(float) * 3,
|
||||
sizeof(PolyVox::PositionMaterialNormal),
|
||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)));
|
||||
mesh->addAttribute(gpu::Stream::NORMAL,
|
||||
gpu::BufferView(vertexBufferPtr,
|
||||
sizeof(float) * 3,
|
||||
vertexBufferPtr->getSize() - sizeof(float) * 3,
|
||||
sizeof(PolyVox::PositionMaterialNormal),
|
||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)));
|
||||
|
||||
// auto normalAttrib = mesh->getAttributeBuffer(gpu::Stream::NORMAL);
|
||||
// for (auto normal = normalAttrib.begin<glm::vec3>(); normal != normalAttrib.end<glm::vec3>(); normal++) {
|
||||
// (*normal) = -(*normal);
|
||||
// }
|
||||
|
||||
qDebug() << "---- vecIndices.size() =" << vecIndices.size();
|
||||
qDebug() << "---- vecVertices.size() =" << vecVertices.size();
|
||||
|
||||
_modelGeometry.setMesh(meshPtr);
|
||||
_needsModelReload = false;
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render");
|
||||
assert(getType() == EntityTypes::PolyVox);
|
||||
|
||||
if (_needsModelReload) {
|
||||
getModel();
|
||||
}
|
||||
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::vec3 scale = dimensions / _voxelVolumeSize;
|
||||
glm::vec3 center = getCenter();
|
||||
glm::quat rotation = getRotation();
|
||||
glPushMatrix();
|
||||
glTranslatef(position.x, position.y, position.z);
|
||||
glm::vec3 axis = glm::axis(rotation);
|
||||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
glm::vec3 positionToCenter = center - position;
|
||||
// make the rendered voxel volume be centered on the entity's position
|
||||
positionToCenter -= _dimensions * glm::vec3(0.5f,0.5f,0.5f);
|
||||
glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z);
|
||||
glScalef(scale.x, scale.y, scale.z);
|
||||
|
||||
auto mesh = _modelGeometry.getMesh();
|
||||
gpu::Batch batch;
|
||||
batch.setInputFormat(mesh->getVertexFormat());
|
||||
batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer());
|
||||
batch.setInputBuffer(gpu::Stream::NORMAL,
|
||||
mesh->getVertexBuffer()._buffer,
|
||||
sizeof(float) * 3,
|
||||
mesh->getVertexBuffer()._stride);
|
||||
batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0);
|
||||
batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0);
|
||||
gpu::GLBackend::renderBatch(batch);
|
||||
glPopMatrix();
|
||||
RenderableDebugableEntityItem::render(this, args);
|
||||
}
|
||||
|
||||
class RaycastFunctor
|
||||
{
|
||||
public:
|
||||
RaycastFunctor() : _result(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)) { }
|
||||
bool operator()(PolyVox::SimpleVolume<unsigned char>::Sampler& sampler)
|
||||
{
|
||||
if (sampler.getVoxel() == 0) {
|
||||
return true; // keep raycasting
|
||||
}
|
||||
PolyVox::Vector3DInt32 positionIndex = sampler.getPosition();
|
||||
_result = glm::vec4(positionIndex.getX(), positionIndex.getY(), positionIndex.getZ(), 1.0f);
|
||||
return false;
|
||||
}
|
||||
glm::vec4 _result;
|
||||
};
|
||||
|
||||
bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& origin,
|
||||
const glm::vec3& direction,
|
||||
bool& keepSearching,
|
||||
OctreeElement*& element,
|
||||
float& distance, BoxFace& face,
|
||||
void** intersectedObject,
|
||||
bool precisionPicking) const
|
||||
{
|
||||
if (_needsModelReload || !precisionPicking) {
|
||||
// just intersect with bounding box
|
||||
return true;
|
||||
}
|
||||
|
||||
glm::mat4 wtvMatrix = worldToVoxelMatrix();
|
||||
glm::vec3 farPoint = origin + direction;
|
||||
glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f);
|
||||
glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f);
|
||||
glm::vec4 directionInVoxel = farInVoxel - originInVoxel;
|
||||
|
||||
PolyVox::Vector3DFloat start(originInVoxel[0], originInVoxel[1], originInVoxel[2]);
|
||||
PolyVox::Vector3DFloat pvDirection(directionInVoxel[0], directionInVoxel[1], directionInVoxel[2]);
|
||||
pvDirection.normalise();
|
||||
|
||||
// the PolyVox ray intersection code requires a near and far point.
|
||||
glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units
|
||||
float distanceToEntity = glm::distance(origin, _position);
|
||||
float largestDimension = glm::max(_dimensions[0], _dimensions[1], _dimensions[2]);
|
||||
// set ray cast length to long enough to cover all of the voxel space
|
||||
pvDirection *= (distanceToEntity + largestDimension) / glm::min(scale[0], scale[1], scale[2]);
|
||||
|
||||
PolyVox::RaycastResult raycastResult;
|
||||
RaycastFunctor callback;
|
||||
raycastResult = PolyVox::raycastWithDirection(_volData, start, pvDirection, callback);
|
||||
|
||||
if (raycastResult == PolyVox::RaycastResults::Completed) {
|
||||
// the ray completed its path -- nothing was hit.
|
||||
return false;
|
||||
}
|
||||
|
||||
glm::vec4 intersectedWorldPosition = voxelToWorldMatrix() * callback._result;
|
||||
|
||||
distance = glm::distance(glm::vec3(intersectedWorldPosition), origin);
|
||||
|
||||
face = BoxFace::MIN_X_FACE; // XXX
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// compress the data in _volData and save the results. The compressed form is used during
|
||||
// saves to disk and for transmission over the wire
|
||||
void RenderablePolyVoxEntityItem::compressVolumeData() {
|
||||
int rawSize = _volData->getDepth() * _volData->getHeight() * _volData->getWidth();
|
||||
QByteArray uncompressedData = QByteArray(rawSize, '\0');
|
||||
|
||||
for (int z = 0; z < _volData->getDepth(); z++) {
|
||||
for (int y = 0; y < _volData->getHeight(); y++) {
|
||||
for (int x = 0; x < _volData->getWidth(); x++) {
|
||||
uint8_t uVoxelValue = _volData->getVoxelAt(x, y, z);
|
||||
int uncompressedIndex = z * _volData->getHeight() * _volData->getWidth() + y * _volData->getWidth() + x;
|
||||
uncompressedData[uncompressedIndex] = uVoxelValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray newVoxelData = qCompress(uncompressedData, 9);
|
||||
// HACK -- until we have a way to allow for properties larger than MTU, don't update.
|
||||
if (newVoxelData.length() < 1200) {
|
||||
_voxelData = newVoxelData;
|
||||
qDebug() << "-------------- voxel compresss --------------";
|
||||
qDebug() << "raw-size =" << rawSize << " compressed-size =" << newVoxelData.size();
|
||||
} else {
|
||||
qDebug() << "voxel data too large, reverting change.";
|
||||
// revert
|
||||
decompressVolumeData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// take compressed data and decompreess it into _volData.
|
||||
void RenderablePolyVoxEntityItem::decompressVolumeData() {
|
||||
int rawSize = _volData->getDepth() * _volData->getHeight() * _volData->getWidth();
|
||||
QByteArray uncompressedData = QByteArray(rawSize, '\0');
|
||||
|
||||
uncompressedData = qUncompress(_voxelData);
|
||||
|
||||
for (int z = 0; z < _volData->getDepth(); z++) {
|
||||
for (int y = 0; y < _volData->getHeight(); y++) {
|
||||
for (int x = 0; x < _volData->getWidth(); x++) {
|
||||
int uncompressedIndex = z * _volData->getHeight() * _volData->getWidth() + y * _volData->getWidth() + x;
|
||||
_volData->setVoxelAt(x, y, z, uncompressedData[uncompressedIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_needsModelReload = true;
|
||||
|
||||
qDebug() << "--------------- voxel decompress ---------------";
|
||||
qDebug() << "raw-size =" << rawSize << " compressed-size =" << _voxelData.size();
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// RenderablePolyVoxEntityItem.h
|
||||
// libraries/entities-renderer/src/
|
||||
//
|
||||
// Created by Seth Alves on 5/19/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_RenderablePolyVoxEntityItem_h
|
||||
#define hifi_RenderablePolyVoxEntityItem_h
|
||||
|
||||
#include <PolyVoxCore/SimpleVolume.h>
|
||||
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "RenderableDebugableEntityItem.h"
|
||||
|
||||
class RenderablePolyVoxEntityItem : public PolyVoxEntityItem {
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
RenderablePolyVoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
PolyVoxEntityItem(entityItemID, properties) { }
|
||||
|
||||
virtual ~RenderablePolyVoxEntityItem();
|
||||
|
||||
virtual void somethingChangedNotification() {
|
||||
// This gets called from EnityItem::readEntityDataFromBuffer every time a packet describing
|
||||
// this entity comes from the entity-server. It gets called even if nothing has actually changed
|
||||
// (see the comment in EntityItem.cpp). If that gets fixed, this could be used to know if we
|
||||
// need to redo the voxel data.
|
||||
// _needsModelReload = true;
|
||||
}
|
||||
|
||||
|
||||
void render(RenderArgs* args);
|
||||
virtual bool supportsDetailedRayIntersection() const { return true; }
|
||||
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
void** intersectedObject, bool precisionPicking) const;
|
||||
|
||||
void getModel();
|
||||
|
||||
virtual void setVoxelData(QByteArray voxelData);
|
||||
|
||||
virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize);
|
||||
glm::mat4 voxelToWorldMatrix() const;
|
||||
glm::mat4 worldToVoxelMatrix() const;
|
||||
|
||||
// coords are in voxel-volume space
|
||||
virtual void setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue);
|
||||
|
||||
// coords are in world-space
|
||||
virtual void setSphere(glm::vec3 center, float radius, uint8_t toValue);
|
||||
|
||||
private:
|
||||
void compressVolumeData();
|
||||
void decompressVolumeData();
|
||||
|
||||
PolyVox::SimpleVolume<uint8_t>* _volData = nullptr;
|
||||
model::Geometry _modelGeometry;
|
||||
bool _needsModelReload = true;
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_RenderablePolyVoxEntityItem_h
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
#include "RenderableSphereEntityItem.h"
|
||||
|
||||
EntityItem* RenderableSphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableSphereEntityItem(entityID, properties);
|
||||
EntityItemPointer RenderableSphereEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new RenderableSphereEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
void RenderableSphereEntityItem::render(RenderArgs* args) {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class RenderableSphereEntityItem : public SphereEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
RenderableSphereEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
SphereEntityItem(entityItemID, properties)
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
|
||||
const int FIXED_FONT_POINT_SIZE = 40;
|
||||
|
||||
EntityItem* RenderableTextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableTextEntityItem(entityID, properties);
|
||||
EntityItemPointer RenderableTextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new RenderableTextEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
void RenderableTextEntityItem::render(RenderArgs* args) {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class RenderableTextEntityItem : public TextEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
RenderableTextEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
TextEntityItem(entityItemID, properties)
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
const float DPI = 30.47f;
|
||||
const float METERS_TO_INCHES = 39.3701f;
|
||||
|
||||
EntityItem* RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableWebEntityItem(entityID, properties);
|
||||
EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new RenderableWebEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
RenderableWebEntityItem::RenderableWebEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
|
|
|
@ -17,7 +17,7 @@ class OffscreenQmlSurface;
|
|||
|
||||
class RenderableWebEntityItem : public WebEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
RenderableWebEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
~RenderableWebEntityItem();
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
#include <GeometryCache.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
EntityItem* RenderableZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new RenderableZoneEntityItem(entityID, properties);
|
||||
EntityItemPointer RenderableZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new RenderableZoneEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
template<typename Lambda>
|
||||
|
|
|
@ -19,7 +19,7 @@ class NetworkGeometry;
|
|||
|
||||
class RenderableZoneEntityItem : public ZoneEntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
RenderableZoneEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
ZoneEntityItem(entityItemID, properties),
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "AddEntityOperator.h"
|
||||
|
||||
AddEntityOperator::AddEntityOperator(EntityTree* tree,
|
||||
EntityItem* newEntity) :
|
||||
EntityItemPointer newEntity) :
|
||||
_tree(tree),
|
||||
_newEntity(newEntity),
|
||||
_foundNew(false),
|
||||
|
|
|
@ -14,14 +14,14 @@
|
|||
|
||||
class AddEntityOperator : public RecurseOctreeOperator {
|
||||
public:
|
||||
AddEntityOperator(EntityTree* tree, EntityItem* newEntity);
|
||||
AddEntityOperator(EntityTree* tree, EntityItemPointer newEntity);
|
||||
|
||||
virtual bool preRecursion(OctreeElement* element);
|
||||
virtual bool postRecursion(OctreeElement* element);
|
||||
virtual OctreeElement* possiblyCreateChildAt(OctreeElement* element, int childIndex);
|
||||
private:
|
||||
EntityTree* _tree;
|
||||
EntityItem* _newEntity;
|
||||
EntityItemPointer _newEntity;
|
||||
bool _foundNew;
|
||||
quint64 _changeTime;
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
#include "EntityTreeElement.h"
|
||||
|
||||
|
||||
EntityItem* BoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItem* result = new BoxEntityItem(entityID, properties);
|
||||
EntityItemPointer BoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer result { new BoxEntityItem(entityID, properties) };
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class BoxEntityItem : public EntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
BoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ bool DeleteEntityOperator::preRecursion(OctreeElement* element) {
|
|||
// If this is the element we're looking for, then ask it to remove the old entity
|
||||
// and we can stop searching.
|
||||
if (entityTreeElement == details.containingElement) {
|
||||
EntityItem* theEntity = details.entity;
|
||||
EntityItemPointer theEntity = details.entity;
|
||||
bool entityDeleted = entityTreeElement->removeEntityItem(theEntity); // remove it from the element
|
||||
assert(entityDeleted);
|
||||
_tree->setContainingElement(details.entity->getEntityItemID(), NULL); // update or id to element lookup
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
class EntityToDeleteDetails {
|
||||
public:
|
||||
EntityItem* entity;
|
||||
EntityItemPointer entity;
|
||||
AACube cube;
|
||||
EntityTreeElement* containingElement;
|
||||
};
|
||||
|
|
|
@ -183,9 +183,9 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
QByteArray encodedPropertyFlags;
|
||||
int propertyCount = 0;
|
||||
|
||||
successIDFits = packetData->appendValue(encodedID);
|
||||
successIDFits = packetData->appendRawData(encodedID);
|
||||
if (successIDFits) {
|
||||
successTypeFits = packetData->appendValue(encodedType);
|
||||
successTypeFits = packetData->appendRawData(encodedType);
|
||||
}
|
||||
if (successTypeFits) {
|
||||
successCreatedFits = packetData->appendValue(_created);
|
||||
|
@ -194,17 +194,17 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
successLastEditedFits = packetData->appendValue(lastEdited);
|
||||
}
|
||||
if (successLastEditedFits) {
|
||||
successLastUpdatedFits = packetData->appendValue(encodedUpdateDelta);
|
||||
successLastUpdatedFits = packetData->appendRawData(encodedUpdateDelta);
|
||||
}
|
||||
if (successLastUpdatedFits) {
|
||||
successLastSimulatedFits = packetData->appendValue(encodedSimulatedDelta);
|
||||
successLastSimulatedFits = packetData->appendRawData(encodedSimulatedDelta);
|
||||
}
|
||||
|
||||
if (successLastSimulatedFits) {
|
||||
propertyFlagsOffset = packetData->getUncompressedByteOffset();
|
||||
encodedPropertyFlags = propertyFlags;
|
||||
oldPropertyFlagsLength = encodedPropertyFlags.length();
|
||||
successPropertyFlagsFits = packetData->appendValue(encodedPropertyFlags);
|
||||
successPropertyFlagsFits = packetData->appendRawData(encodedPropertyFlags);
|
||||
}
|
||||
|
||||
bool headerFits = successIDFits && successTypeFits && successCreatedFits && successLastEditedFits
|
||||
|
@ -318,15 +318,6 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
return 0;
|
||||
}
|
||||
|
||||
// if this bitstream indicates that this node is the simulation owner, ignore any physics-related updates.
|
||||
glm::vec3 savePosition = _position;
|
||||
glm::quat saveRotation = _rotation;
|
||||
// glm::vec3 saveVelocity = _velocity;
|
||||
// glm::vec3 saveAngularVelocity = _angularVelocity;
|
||||
// glm::vec3 saveGravity = _gravity;
|
||||
// glm::vec3 saveAcceleration = _acceleration;
|
||||
|
||||
|
||||
// Header bytes
|
||||
// object ID [16 bytes]
|
||||
// ByteCountCoded(type code) [~1 byte]
|
||||
|
@ -337,299 +328,315 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
const int MINIMUM_HEADER_BYTES = 27;
|
||||
|
||||
int bytesRead = 0;
|
||||
if (bytesLeftToRead >= MINIMUM_HEADER_BYTES) {
|
||||
if (bytesLeftToRead < MINIMUM_HEADER_BYTES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int originalLength = bytesLeftToRead;
|
||||
QByteArray originalDataBuffer((const char*)data, originalLength);
|
||||
// if this bitstream indicates that this node is the simulation owner, ignore any physics-related updates.
|
||||
glm::vec3 savePosition = _position;
|
||||
glm::quat saveRotation = _rotation;
|
||||
glm::vec3 saveVelocity = _velocity;
|
||||
glm::vec3 saveAngularVelocity = _angularVelocity;
|
||||
|
||||
int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
|
||||
int originalLength = bytesLeftToRead;
|
||||
QByteArray originalDataBuffer((const char*)data, originalLength);
|
||||
|
||||
const unsigned char* dataAt = data;
|
||||
int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0;
|
||||
|
||||
// id
|
||||
QByteArray encodedID = originalDataBuffer.mid(bytesRead, NUM_BYTES_RFC4122_UUID); // maximum possible size
|
||||
_id = QUuid::fromRfc4122(encodedID);
|
||||
dataAt += encodedID.size();
|
||||
bytesRead += encodedID.size();
|
||||
|
||||
// type
|
||||
QByteArray encodedType = originalDataBuffer.mid(bytesRead); // maximum possible size
|
||||
ByteCountCoded<quint32> typeCoder = encodedType;
|
||||
encodedType = typeCoder; // determine true length
|
||||
dataAt += encodedType.size();
|
||||
bytesRead += encodedType.size();
|
||||
quint32 type = typeCoder;
|
||||
_type = (EntityTypes::EntityType)type;
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
bool overwriteLocalData = true; // assume the new content overwrites our local data
|
||||
// id
|
||||
QByteArray encodedID = originalDataBuffer.mid(bytesRead, NUM_BYTES_RFC4122_UUID); // maximum possible size
|
||||
_id = QUuid::fromRfc4122(encodedID);
|
||||
dataAt += encodedID.size();
|
||||
bytesRead += encodedID.size();
|
||||
|
||||
// type
|
||||
QByteArray encodedType = originalDataBuffer.mid(bytesRead); // maximum possible size
|
||||
ByteCountCoded<quint32> typeCoder = encodedType;
|
||||
encodedType = typeCoder; // determine true length
|
||||
dataAt += encodedType.size();
|
||||
bytesRead += encodedType.size();
|
||||
quint32 type = typeCoder;
|
||||
_type = (EntityTypes::EntityType)type;
|
||||
|
||||
// _created
|
||||
quint64 createdFromBuffer = 0;
|
||||
memcpy(&createdFromBuffer, dataAt, sizeof(createdFromBuffer));
|
||||
dataAt += sizeof(createdFromBuffer);
|
||||
bytesRead += sizeof(createdFromBuffer);
|
||||
bool overwriteLocalData = true; // assume the new content overwrites our local data
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
if (_created == UNKNOWN_CREATED_TIME) {
|
||||
// we don't yet have a _created timestamp, so we accept this one
|
||||
createdFromBuffer -= clockSkew;
|
||||
if (createdFromBuffer > now || createdFromBuffer == UNKNOWN_CREATED_TIME) {
|
||||
createdFromBuffer = now;
|
||||
}
|
||||
_created = createdFromBuffer;
|
||||
// _created
|
||||
quint64 createdFromBuffer = 0;
|
||||
memcpy(&createdFromBuffer, dataAt, sizeof(createdFromBuffer));
|
||||
dataAt += sizeof(createdFromBuffer);
|
||||
bytesRead += sizeof(createdFromBuffer);
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
if (_created == UNKNOWN_CREATED_TIME) {
|
||||
// we don't yet have a _created timestamp, so we accept this one
|
||||
createdFromBuffer -= clockSkew;
|
||||
if (createdFromBuffer > now || createdFromBuffer == UNKNOWN_CREATED_TIME) {
|
||||
createdFromBuffer = now;
|
||||
}
|
||||
_created = createdFromBuffer;
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
quint64 lastEdited = getLastEdited();
|
||||
float editedAgo = getEditedAgo();
|
||||
QString agoAsString = formatSecondsElapsed(editedAgo);
|
||||
QString ageAsString = formatSecondsElapsed(getAge());
|
||||
qCDebug(entities) << "------------------------------------------";
|
||||
qCDebug(entities) << "Loading entity " << getEntityItemID() << " from buffer...";
|
||||
qCDebug(entities) << "------------------------------------------";
|
||||
debugDump();
|
||||
qCDebug(entities) << "------------------------------------------";
|
||||
qCDebug(entities) << " _created =" << _created;
|
||||
qCDebug(entities) << " age=" << getAge() << "seconds - " << ageAsString;
|
||||
qCDebug(entities) << " lastEdited =" << lastEdited;
|
||||
qCDebug(entities) << " ago=" << editedAgo << "seconds - " << agoAsString;
|
||||
#endif
|
||||
|
||||
quint64 lastEditedFromBuffer = 0;
|
||||
quint64 lastEditedFromBufferAdjusted = 0;
|
||||
|
||||
// TODO: we could make this encoded as a delta from _created
|
||||
// _lastEdited
|
||||
memcpy(&lastEditedFromBuffer, dataAt, sizeof(lastEditedFromBuffer));
|
||||
dataAt += sizeof(lastEditedFromBuffer);
|
||||
bytesRead += sizeof(lastEditedFromBuffer);
|
||||
lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew;
|
||||
if (lastEditedFromBufferAdjusted > now) {
|
||||
lastEditedFromBufferAdjusted = now;
|
||||
}
|
||||
|
||||
bool fromSameServerEdit = (lastEditedFromBuffer == _lastEditedFromRemoteInRemoteTime);
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << "data from server **************** ";
|
||||
qCDebug(entities) << " entityItemID:" << getEntityItemID();
|
||||
qCDebug(entities) << " now:" << now;
|
||||
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
|
||||
qCDebug(entities) << " lastEditedFromBuffer:" << debugTime(lastEditedFromBuffer, now);
|
||||
qCDebug(entities) << " clockSkew:" << debugTimeOnly(clockSkew);
|
||||
qCDebug(entities) << " lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now);
|
||||
qCDebug(entities) << " _lastEditedFromRemote:" << debugTime(_lastEditedFromRemote, now);
|
||||
qCDebug(entities) << " _lastEditedFromRemoteInRemoteTime:" << debugTime(_lastEditedFromRemoteInRemoteTime, now);
|
||||
qCDebug(entities) << " fromSameServerEdit:" << fromSameServerEdit;
|
||||
#endif
|
||||
|
||||
bool ignoreServerPacket = false; // assume we'll use this server packet
|
||||
|
||||
// If this packet is from the same server edit as the last packet we accepted from the server
|
||||
// we probably want to use it.
|
||||
if (fromSameServerEdit) {
|
||||
// If this is from the same sever packet, then check against any local changes since we got
|
||||
// the most recent packet from this server time
|
||||
if (_lastEdited > _lastEditedFromRemote) {
|
||||
ignoreServerPacket = true;
|
||||
}
|
||||
} else {
|
||||
// If this isn't from the same sever packet, then honor our skew adjusted times...
|
||||
// If we've changed our local tree more recently than the new data from this packet
|
||||
// then we will not be changing our values, instead we just read and skip the data
|
||||
if (_lastEdited > lastEditedFromBufferAdjusted) {
|
||||
ignoreServerPacket = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoreServerPacket) {
|
||||
overwriteLocalData = false;
|
||||
#ifdef WANT_DEBUG
|
||||
quint64 lastEdited = getLastEdited();
|
||||
float editedAgo = getEditedAgo();
|
||||
QString agoAsString = formatSecondsElapsed(editedAgo);
|
||||
QString ageAsString = formatSecondsElapsed(getAge());
|
||||
qCDebug(entities) << "------------------------------------------";
|
||||
qCDebug(entities) << "Loading entity " << getEntityItemID() << " from buffer...";
|
||||
qCDebug(entities) << "------------------------------------------";
|
||||
qCDebug(entities) << "IGNORING old data from server!!! ****************";
|
||||
debugDump();
|
||||
qCDebug(entities) << "------------------------------------------";
|
||||
qCDebug(entities) << " _created =" << _created;
|
||||
qCDebug(entities) << " age=" << getAge() << "seconds - " << ageAsString;
|
||||
qCDebug(entities) << " lastEdited =" << lastEdited;
|
||||
qCDebug(entities) << " ago=" << editedAgo << "seconds - " << agoAsString;
|
||||
#endif
|
||||
|
||||
quint64 lastEditedFromBuffer = 0;
|
||||
quint64 lastEditedFromBufferAdjusted = 0;
|
||||
|
||||
// TODO: we could make this encoded as a delta from _created
|
||||
// _lastEdited
|
||||
memcpy(&lastEditedFromBuffer, dataAt, sizeof(lastEditedFromBuffer));
|
||||
dataAt += sizeof(lastEditedFromBuffer);
|
||||
bytesRead += sizeof(lastEditedFromBuffer);
|
||||
lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew;
|
||||
if (lastEditedFromBufferAdjusted > now) {
|
||||
lastEditedFromBufferAdjusted = now;
|
||||
}
|
||||
|
||||
bool fromSameServerEdit = (lastEditedFromBuffer == _lastEditedFromRemoteInRemoteTime);
|
||||
|
||||
} else {
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << "data from server **************** ";
|
||||
qCDebug(entities) << " entityItemID:" << getEntityItemID();
|
||||
qCDebug(entities) << " now:" << now;
|
||||
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
|
||||
qCDebug(entities) << " lastEditedFromBuffer:" << debugTime(lastEditedFromBuffer, now);
|
||||
qCDebug(entities) << " clockSkew:" << debugTimeOnly(clockSkew);
|
||||
qCDebug(entities) << " lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now);
|
||||
qCDebug(entities) << " _lastEditedFromRemote:" << debugTime(_lastEditedFromRemote, now);
|
||||
qCDebug(entities) << " _lastEditedFromRemoteInRemoteTime:" << debugTime(_lastEditedFromRemoteInRemoteTime, now);
|
||||
qCDebug(entities) << " fromSameServerEdit:" << fromSameServerEdit;
|
||||
qCDebug(entities) << "USING NEW data from server!!! ****************";
|
||||
debugDump();
|
||||
#endif
|
||||
|
||||
bool ignoreServerPacket = false; // assume we'll use this server packet
|
||||
|
||||
// If this packet is from the same server edit as the last packet we accepted from the server
|
||||
// we probably want to use it.
|
||||
if (fromSameServerEdit) {
|
||||
// If this is from the same sever packet, then check against any local changes since we got
|
||||
// the most recent packet from this server time
|
||||
if (_lastEdited > _lastEditedFromRemote) {
|
||||
ignoreServerPacket = true;
|
||||
}
|
||||
} else {
|
||||
// If this isn't from the same sever packet, then honor our skew adjusted times...
|
||||
// If we've changed our local tree more recently than the new data from this packet
|
||||
// then we will not be changing our values, instead we just read and skip the data
|
||||
if (_lastEdited > lastEditedFromBufferAdjusted) {
|
||||
ignoreServerPacket = true;
|
||||
}
|
||||
}
|
||||
// don't allow _lastEdited to be in the future
|
||||
_lastEdited = lastEditedFromBufferAdjusted;
|
||||
_lastEditedFromRemote = now;
|
||||
_lastEditedFromRemoteInRemoteTime = lastEditedFromBuffer;
|
||||
|
||||
if (ignoreServerPacket) {
|
||||
overwriteLocalData = false;
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << "IGNORING old data from server!!! ****************";
|
||||
debugDump();
|
||||
#endif
|
||||
} else {
|
||||
// TODO: only send this notification if something ACTUALLY changed (hint, we haven't yet parsed
|
||||
// the properties out of the bitstream (see below))
|
||||
somethingChangedNotification(); // notify derived classes that something has changed
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << "USING NEW data from server!!! ****************";
|
||||
debugDump();
|
||||
#endif
|
||||
|
||||
// don't allow _lastEdited to be in the future
|
||||
_lastEdited = lastEditedFromBufferAdjusted;
|
||||
_lastEditedFromRemote = now;
|
||||
_lastEditedFromRemoteInRemoteTime = lastEditedFromBuffer;
|
||||
|
||||
// TODO: only send this notification if something ACTUALLY changed (hint, we haven't yet parsed
|
||||
// the properties out of the bitstream (see below))
|
||||
somethingChangedNotification(); // notify derived classes that something has changed
|
||||
}
|
||||
|
||||
// last updated is stored as ByteCountCoded delta from lastEdited
|
||||
QByteArray encodedUpdateDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
|
||||
ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta;
|
||||
quint64 updateDelta = updateDeltaCoder;
|
||||
// last updated is stored as ByteCountCoded delta from lastEdited
|
||||
QByteArray encodedUpdateDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
|
||||
ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta;
|
||||
quint64 updateDelta = updateDeltaCoder;
|
||||
if (overwriteLocalData) {
|
||||
_lastUpdated = lastEditedFromBufferAdjusted + updateDelta; // don't adjust for clock skew since we already did that
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << " _lastUpdated:" << debugTime(_lastUpdated, now);
|
||||
qCDebug(entities) << " _lastEdited:" << debugTime(_lastEdited, now);
|
||||
qCDebug(entities) << " lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now);
|
||||
#endif
|
||||
}
|
||||
encodedUpdateDelta = updateDeltaCoder; // determine true length
|
||||
dataAt += encodedUpdateDelta.size();
|
||||
bytesRead += encodedUpdateDelta.size();
|
||||
|
||||
// Newer bitstreams will have a last simulated and a last updated value
|
||||
quint64 lastSimulatedFromBufferAdjusted = now;
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME) {
|
||||
// last simulated is stored as ByteCountCoded delta from lastEdited
|
||||
QByteArray encodedSimulatedDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
|
||||
ByteCountCoded<quint64> simulatedDeltaCoder = encodedSimulatedDelta;
|
||||
quint64 simulatedDelta = simulatedDeltaCoder;
|
||||
if (overwriteLocalData) {
|
||||
_lastUpdated = lastEditedFromBufferAdjusted + updateDelta; // don't adjust for clock skew since we already did that
|
||||
lastSimulatedFromBufferAdjusted = lastEditedFromBufferAdjusted + simulatedDelta; // don't adjust for clock skew since we already did that
|
||||
if (lastSimulatedFromBufferAdjusted > now) {
|
||||
lastSimulatedFromBufferAdjusted = now;
|
||||
}
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << " _lastUpdated:" << debugTime(_lastUpdated, now);
|
||||
qCDebug(entities) << " _lastEdited:" << debugTime(_lastEdited, now);
|
||||
qCDebug(entities) << " lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now);
|
||||
qCDebug(entities) << " lastSimulatedFromBufferAdjusted:" << debugTime(lastSimulatedFromBufferAdjusted, now);
|
||||
#endif
|
||||
}
|
||||
encodedUpdateDelta = updateDeltaCoder; // determine true length
|
||||
dataAt += encodedUpdateDelta.size();
|
||||
bytesRead += encodedUpdateDelta.size();
|
||||
|
||||
// Newer bitstreams will have a last simulated and a last updated value
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_LAST_SIMULATED_TIME) {
|
||||
// last simulated is stored as ByteCountCoded delta from lastEdited
|
||||
QByteArray encodedSimulatedDelta = originalDataBuffer.mid(bytesRead); // maximum possible size
|
||||
ByteCountCoded<quint64> simulatedDeltaCoder = encodedSimulatedDelta;
|
||||
quint64 simulatedDelta = simulatedDeltaCoder;
|
||||
encodedSimulatedDelta = simulatedDeltaCoder; // determine true length
|
||||
dataAt += encodedSimulatedDelta.size();
|
||||
bytesRead += encodedSimulatedDelta.size();
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
if (overwriteLocalData) {
|
||||
qCDebug(entities) << "EntityItem::readEntityDataFromBuffer()... changed entity:" << getEntityItemID();
|
||||
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
|
||||
qCDebug(entities) << " getLastSimulated:" << debugTime(getLastSimulated(), now);
|
||||
qCDebug(entities) << " getLastUpdated:" << debugTime(getLastUpdated(), now);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Property Flags
|
||||
QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size
|
||||
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
|
||||
dataAt += propertyFlags.getEncodedLength();
|
||||
bytesRead += propertyFlags.getEncodedLength();
|
||||
bool useMeters = (args.bitstreamVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS);
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePositionInDomainUnits);
|
||||
}
|
||||
|
||||
// Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) {
|
||||
if (propertyFlags.getHasProperty(PROP_RADIUS)) {
|
||||
float fromBuffer;
|
||||
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer));
|
||||
dataAt += sizeof(fromBuffer);
|
||||
bytesRead += sizeof(fromBuffer);
|
||||
if (overwriteLocalData) {
|
||||
_lastSimulated = lastEditedFromBufferAdjusted + simulatedDelta; // don't adjust for clock skew since we already did that
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << " _lastSimulated:" << debugTime(_lastSimulated, now);
|
||||
qCDebug(entities) << " _lastEdited:" << debugTime(_lastEdited, now);
|
||||
qCDebug(entities) << " lastEditedFromBufferAdjusted:" << debugTime(lastEditedFromBufferAdjusted, now);
|
||||
#endif
|
||||
setRadius(fromBuffer);
|
||||
}
|
||||
encodedSimulatedDelta = simulatedDeltaCoder; // determine true length
|
||||
dataAt += encodedSimulatedDelta.size();
|
||||
bytesRead += encodedSimulatedDelta.size();
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
if (overwriteLocalData) {
|
||||
qCDebug(entities) << "EntityItem::readEntityDataFromBuffer()... changed entity:" << getEntityItemID();
|
||||
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
|
||||
qCDebug(entities) << " getLastSimulated:" << debugTime(getLastSimulated(), now);
|
||||
qCDebug(entities) << " getLastUpdated:" << debugTime(getLastUpdated(), now);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// Property Flags
|
||||
QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size
|
||||
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
|
||||
dataAt += propertyFlags.getEncodedLength();
|
||||
bytesRead += propertyFlags.getEncodedLength();
|
||||
bool useMeters = (args.bitstreamVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS);
|
||||
} else {
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePosition);
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePositionInDomainUnits);
|
||||
}
|
||||
|
||||
// Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) {
|
||||
if (propertyFlags.getHasProperty(PROP_RADIUS)) {
|
||||
float fromBuffer;
|
||||
memcpy(&fromBuffer, dataAt, sizeof(fromBuffer));
|
||||
dataAt += sizeof(fromBuffer);
|
||||
bytesRead += sizeof(fromBuffer);
|
||||
if (overwriteLocalData) {
|
||||
setRadius(fromBuffer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensionsInDomainUnits);
|
||||
}
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
|
||||
READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity);
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocityInDomainUnits);
|
||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravityInDomainUnits);
|
||||
}
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) {
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
|
||||
READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityInDegrees);
|
||||
}
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible);
|
||||
READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions);
|
||||
READ_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, bool, updateCollisionsWillMove);
|
||||
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked);
|
||||
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
|
||||
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) {
|
||||
READ_ENTITY_PROPERTY(PROP_SIMULATOR_ID, QUuid, updateSimulatorID);
|
||||
}
|
||||
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
|
||||
READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_NAME, QString, setName);
|
||||
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
|
||||
|
||||
////////////////////////////////////
|
||||
// WARNING: Do not add stream content here after the subclass. Always add it before the subclass
|
||||
//
|
||||
// NOTE: we had a bad version of the stream that we added stream data after the subclass. We can attempt to recover
|
||||
// by doing this parsing here... but it's not likely going to fully recover the content.
|
||||
//
|
||||
// TODO: Remove this conde once we've sufficiently migrated content past this damaged version
|
||||
if (args.bitstreamVersion == VERSION_ENTITIES_HAS_MARKETPLACE_ID_DAMAGED) {
|
||||
READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
}
|
||||
|
||||
if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES))) {
|
||||
// NOTE: This code is attempting to "repair" the old data we just got from the server to make it more
|
||||
// closely match where the entities should be if they'd stepped forward in time to "now". The server
|
||||
// is sending us data with a known "last simulated" time. That time is likely in the past, and therefore
|
||||
// this "new" data is actually slightly out of date. We calculate the time we need to skip forward and
|
||||
// use our simulation helper routine to get a best estimate of where the entity should be.
|
||||
const float MIN_TIME_SKIP = 0.0f;
|
||||
const float MAX_TIME_SKIP = 1.0f; // in seconds
|
||||
float skipTimeForward = glm::clamp((float)(now - _lastSimulated) / (float)(USECS_PER_SECOND),
|
||||
MIN_TIME_SKIP, MAX_TIME_SKIP);
|
||||
if (skipTimeForward > 0.0f) {
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << "skipTimeForward:" << skipTimeForward;
|
||||
#endif
|
||||
|
||||
// we want to extrapolate the motion forward to compensate for packet travel time, but
|
||||
// we don't want the side effect of flag setting.
|
||||
simulateKinematicMotion(skipTimeForward, false);
|
||||
}
|
||||
_lastSimulated = now;
|
||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensionsInDomainUnits);
|
||||
}
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotation);
|
||||
READ_ENTITY_PROPERTY(PROP_DENSITY, float, updateDensity);
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocity);
|
||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravity);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocityInDomainUnits);
|
||||
READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, updateGravityInDomainUnits);
|
||||
}
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) {
|
||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_DAMPING, float, updateDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_RESTITUTION, float, updateRestitution);
|
||||
READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction);
|
||||
READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime);
|
||||
READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript);
|
||||
READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint);
|
||||
if (useMeters) {
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity);
|
||||
} else {
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityInDegrees);
|
||||
}
|
||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, updateAngularDamping);
|
||||
READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, setVisible);
|
||||
READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, updateIgnoreForCollisions);
|
||||
READ_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, bool, updateCollisionsWillMove);
|
||||
READ_ENTITY_PROPERTY(PROP_LOCKED, bool, setLocked);
|
||||
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
|
||||
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) {
|
||||
// we always accept the server's notion of simulatorID, so we fake overwriteLocalData as true
|
||||
// before we try to READ_ENTITY_PROPERTY it
|
||||
bool temp = overwriteLocalData;
|
||||
overwriteLocalData = true;
|
||||
READ_ENTITY_PROPERTY(PROP_SIMULATOR_ID, QUuid, updateSimulatorID);
|
||||
overwriteLocalData = temp;
|
||||
}
|
||||
|
||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
|
||||
READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_NAME, QString, setName);
|
||||
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData);
|
||||
|
||||
////////////////////////////////////
|
||||
// WARNING: Do not add stream content here after the subclass. Always add it before the subclass
|
||||
//
|
||||
// NOTE: we had a bad version of the stream that we added stream data after the subclass. We can attempt to recover
|
||||
// by doing this parsing here... but it's not likely going to fully recover the content.
|
||||
//
|
||||
// TODO: Remove this conde once we've sufficiently migrated content past this damaged version
|
||||
if (args.bitstreamVersion == VERSION_ENTITIES_HAS_MARKETPLACE_ID_DAMAGED) {
|
||||
READ_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
}
|
||||
|
||||
if (overwriteLocalData && (getDirtyFlags() & (EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES))) {
|
||||
// NOTE: This code is attempting to "repair" the old data we just got from the server to make it more
|
||||
// closely match where the entities should be if they'd stepped forward in time to "now". The server
|
||||
// is sending us data with a known "last simulated" time. That time is likely in the past, and therefore
|
||||
// this "new" data is actually slightly out of date. We calculate the time we need to skip forward and
|
||||
// use our simulation helper routine to get a best estimate of where the entity should be.
|
||||
const float MIN_TIME_SKIP = 0.0f;
|
||||
const float MAX_TIME_SKIP = 1.0f; // in seconds
|
||||
float skipTimeForward = glm::clamp((float)(now - lastSimulatedFromBufferAdjusted) / (float)(USECS_PER_SECOND),
|
||||
MIN_TIME_SKIP, MAX_TIME_SKIP);
|
||||
if (skipTimeForward > 0.0f) {
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(entities) << "skipTimeForward:" << skipTimeForward;
|
||||
#endif
|
||||
|
||||
// we want to extrapolate the motion forward to compensate for packet travel time, but
|
||||
// we don't want the side effect of flag setting.
|
||||
simulateKinematicMotion(skipTimeForward, false);
|
||||
}
|
||||
}
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid& myNodeID = nodeList->getSessionUUID();
|
||||
if (_simulatorID == myNodeID && !_simulatorID.isNull()) {
|
||||
// the packet that produced this bitstream originally came from physics simulations performed by
|
||||
// this node, so our version has to be newer than what the packet contained.
|
||||
_position = savePosition;
|
||||
_rotation = saveRotation;
|
||||
// _velocity = saveVelocity;
|
||||
// _angularVelocity = saveAngularVelocity;
|
||||
// _gravity = saveGravity;
|
||||
// _acceleration = saveAcceleration;
|
||||
if (overwriteLocalData) {
|
||||
if (_simulatorID == myNodeID && !_simulatorID.isNull()) {
|
||||
// we own the simulation, so we keep our transform+velocities and remove any related dirty flags
|
||||
// rather than accept the values in the packet
|
||||
_position = savePosition;
|
||||
_rotation = saveRotation;
|
||||
_velocity = saveVelocity;
|
||||
_angularVelocity = saveAngularVelocity;
|
||||
_dirtyFlags &= ~(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES);
|
||||
} else {
|
||||
_lastSimulated = now;
|
||||
}
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
|
@ -652,6 +659,7 @@ void EntityItem::adjustEditPacketForClockSkew(unsigned char* editPacketBuffer, s
|
|||
// lastEdited
|
||||
quint64 lastEditedInLocalTime;
|
||||
memcpy(&lastEditedInLocalTime, dataAt, sizeof(lastEditedInLocalTime));
|
||||
assert(lastEditedInLocalTime > 0);
|
||||
quint64 lastEditedInServerTime = lastEditedInLocalTime + clockSkew;
|
||||
memcpy(dataAt, &lastEditedInServerTime, sizeof(lastEditedInServerTime));
|
||||
#ifdef WANT_DEBUG
|
||||
|
@ -917,6 +925,21 @@ EntityItemProperties EntityItem::getProperties() const {
|
|||
return properties;
|
||||
}
|
||||
|
||||
void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) const {
|
||||
// a TerseUpdate includes the transform and its derivatives
|
||||
properties._position = _position;
|
||||
properties._velocity = _velocity;
|
||||
properties._rotation = _rotation;
|
||||
properties._angularVelocity = _angularVelocity;
|
||||
properties._acceleration = _acceleration;
|
||||
|
||||
properties._positionChanged = true;
|
||||
properties._velocityChanged = true;
|
||||
properties._rotationChanged = true;
|
||||
properties._angularVelocityChanged = true;
|
||||
properties._accelerationChanged = true;
|
||||
}
|
||||
|
||||
bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
|
@ -948,18 +971,18 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
||||
|
||||
if (somethingChanged) {
|
||||
somethingChangedNotification(); // notify derived classes that something has changed
|
||||
uint64_t now = usecTimestampNow();
|
||||
#ifdef WANT_DEBUG
|
||||
int elapsed = now - getLastEdited();
|
||||
qCDebug(entities) << "EntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
||||
#endif
|
||||
if (_created != UNKNOWN_CREATED_TIME) {
|
||||
setLastEdited(now);
|
||||
}
|
||||
setLastEdited(now);
|
||||
somethingChangedNotification(); // notify derived classes that something has changed
|
||||
if (getDirtyFlags() & (EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES)) {
|
||||
// TODO: Andrew & Brad to discuss. Is this correct? Maybe it is. Need to think through all cases.
|
||||
// anything that sets the transform or velocity must update _lastSimulated which is used
|
||||
// for kinematic extrapolation (e.g. we want to extrapolate forward from this moment
|
||||
// when position and/or velocity was changed).
|
||||
_lastSimulated = now;
|
||||
}
|
||||
}
|
||||
|
@ -972,14 +995,6 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
timestamp = now;
|
||||
}
|
||||
_created = timestamp;
|
||||
|
||||
timestamp = properties.getLastEdited();
|
||||
if (timestamp > now) {
|
||||
timestamp = now;
|
||||
} else if (timestamp < _created) {
|
||||
timestamp = _created;
|
||||
}
|
||||
_lastEdited = timestamp;
|
||||
}
|
||||
|
||||
return somethingChanged;
|
||||
|
|
|
@ -347,6 +347,8 @@ public:
|
|||
|
||||
quint64 getLastEditedFromRemote() { return _lastEditedFromRemote; }
|
||||
|
||||
void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
|
||||
|
||||
protected:
|
||||
|
||||
static bool _sendPhysicsUpdates;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "ParticleEffectEntityItem.h"
|
||||
#include "TextEntityItem.h"
|
||||
#include "ZoneEntityItem.h"
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "LineEntityItem.h"
|
||||
|
||||
AtmospherePropertyGroup EntityItemProperties::_staticAtmosphere;
|
||||
|
@ -88,12 +89,16 @@ CONSTRUCT_PROPERTY(keyLightColor, ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR),
|
|||
CONSTRUCT_PROPERTY(keyLightIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY),
|
||||
CONSTRUCT_PROPERTY(keyLightAmbientIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY),
|
||||
CONSTRUCT_PROPERTY(keyLightDirection, ZoneEntityItem::DEFAULT_KEYLIGHT_DIRECTION),
|
||||
CONSTRUCT_PROPERTY(voxelVolumeSize, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE),
|
||||
CONSTRUCT_PROPERTY(voxelData, PolyVoxEntityItem::DEFAULT_VOXEL_DATA),
|
||||
CONSTRUCT_PROPERTY(voxelSurfaceStyle, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE),
|
||||
CONSTRUCT_PROPERTY(name, ENTITY_ITEM_DEFAULT_NAME),
|
||||
CONSTRUCT_PROPERTY(backgroundMode, BACKGROUND_MODE_INHERIT),
|
||||
CONSTRUCT_PROPERTY(sourceUrl, ""),
|
||||
CONSTRUCT_PROPERTY(lineWidth, LineEntityItem::DEFAULT_LINE_WIDTH),
|
||||
CONSTRUCT_PROPERTY(linePoints, 0),
|
||||
|
||||
|
||||
_id(UNKNOWN_ENTITY_ID),
|
||||
_idSet(false),
|
||||
_lastEdited(0),
|
||||
|
@ -339,9 +344,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, keyLightDirection);
|
||||
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode);
|
||||
CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl);
|
||||
CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize);
|
||||
CHECK_PROPERTY_CHANGE(PROP_VOXEL_DATA, voxelData);
|
||||
CHECK_PROPERTY_CHANGE(PROP_VOXEL_SURFACE_STYLE, voxelSurfaceStyle);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LINE_WIDTH, lineWidth);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints);
|
||||
|
||||
changedProperties += _stage.getChangedProperties();
|
||||
changedProperties += _atmosphere.getChangedProperties();
|
||||
changedProperties += _skybox.getChangedProperties();
|
||||
|
@ -423,6 +430,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightDirection);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(backgroundMode, getBackgroundModeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(sourceUrl);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(voxelVolumeSize);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(voxelData);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(voxelSurfaceStyle);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(lineWidth);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(linePoints);
|
||||
|
||||
|
@ -530,11 +540,12 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(keyLightDirection, glmVec3, setKeyLightDirection);
|
||||
COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(backgroundMode, BackgroundMode);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelData, QByteArray, setVoxelData);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelSurfaceStyle, uint16_t, setVoxelSurfaceStyle);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(lineWidth, float, setLineWidth);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints);
|
||||
|
||||
|
||||
|
||||
_stage.copyFromScriptValue(object, _defaultSettings);
|
||||
_atmosphere.copyFromScriptValue(object, _defaultSettings);
|
||||
_skybox.copyFromScriptValue(object, _defaultSettings);
|
||||
|
@ -630,23 +641,23 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
quint64 lastEdited = properties.getLastEdited();
|
||||
bool successLastEditedFits = packetData->appendValue(lastEdited);
|
||||
|
||||
bool successIDFits = packetData->appendValue(encodedID);
|
||||
bool successIDFits = packetData->appendRawData(encodedID);
|
||||
if (successIDFits) {
|
||||
successIDFits = packetData->appendValue(encodedToken);
|
||||
successIDFits = packetData->appendRawData(encodedToken);
|
||||
}
|
||||
bool successTypeFits = packetData->appendValue(encodedType);
|
||||
bool successTypeFits = packetData->appendRawData(encodedType);
|
||||
|
||||
// NOTE: We intentionally do not send "created" times in edit messages. This is because:
|
||||
// 1) if the edit is to an existing entity, the created time can not be changed
|
||||
// 2) if the edit is to a new entity, the created time is the last edited time
|
||||
|
||||
// TODO: Should we get rid of this in this in edit packets, since this has to always be 0?
|
||||
bool successLastUpdatedFits = packetData->appendValue(encodedUpdateDelta);
|
||||
bool successLastUpdatedFits = packetData->appendRawData(encodedUpdateDelta);
|
||||
|
||||
int propertyFlagsOffset = packetData->getUncompressedByteOffset();
|
||||
QByteArray encodedPropertyFlags = propertyFlags;
|
||||
int oldPropertyFlagsLength = encodedPropertyFlags.length();
|
||||
bool successPropertyFlagsFits = packetData->appendValue(encodedPropertyFlags);
|
||||
bool successPropertyFlagsFits = packetData->appendRawData(encodedPropertyFlags);
|
||||
int propertyCount = 0;
|
||||
|
||||
bool headerFits = successIDFits && successTypeFits && successLastEditedFits
|
||||
|
@ -751,6 +762,12 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
_staticSkybox.setProperties(properties);
|
||||
_staticSkybox.appentToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState );
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::PolyVox) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_VOXEL_VOLUME_SIZE, properties.getVoxelVolumeSize());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VOXEL_DATA, properties.getVoxelData());
|
||||
APPEND_ENTITY_PROPERTY(PROP_VOXEL_SURFACE_STYLE, properties.getVoxelSurfaceStyle());
|
||||
}
|
||||
|
||||
if(properties.getType() == EntityTypes::Line){
|
||||
APPEND_ENTITY_PROPERTY(PROP_LINE_WIDTH, properties.getLineWidth());
|
||||
|
@ -990,6 +1007,12 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
properties.getAtmosphere().decodeFromEditPacket(propertyFlags, dataAt , processedBytes);
|
||||
properties.getSkybox().decodeFromEditPacket(propertyFlags, dataAt , processedBytes);
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::PolyVox) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_VOLUME_SIZE, glm::vec3, setVoxelVolumeSize);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_DATA, QByteArray, setVoxelData);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_SURFACE_STYLE, uint16_t, setVoxelSurfaceStyle);
|
||||
}
|
||||
|
||||
if(properties.getType() == EntityTypes::Line) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_WIDTH, float, setLineWidth);
|
||||
|
@ -999,7 +1022,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MARKETPLACE_ID, QString, setMarketplaceID);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NAME, QString, setName);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
@ -1100,9 +1123,13 @@ void EntityItemProperties::markAllChanged() {
|
|||
_skybox.markAllChanged();
|
||||
|
||||
_sourceUrlChanged = true;
|
||||
_voxelVolumeSizeChanged = true;
|
||||
_voxelDataChanged = true;
|
||||
_voxelSurfaceStyleChanged = true;
|
||||
|
||||
_lineWidthChanged = true;
|
||||
_linePointsChanged = true;
|
||||
|
||||
}
|
||||
|
||||
/// The maximum bounding cube for the entity, independent of it's rotation.
|
||||
|
@ -1148,4 +1175,7 @@ AABox EntityItemProperties::getAABox() const {
|
|||
return AABox(rotatedExtentsRelativeToRegistrationPoint);
|
||||
}
|
||||
|
||||
|
||||
bool EntityItemProperties::hasTerseUpdateChanges() const {
|
||||
// a TerseUpdate includes the transform and its derivatives
|
||||
return _positionChanged || _velocityChanged || _rotationChanged || _angularVelocityChanged || _accelerationChanged;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ class EntityItemProperties {
|
|||
friend class ZoneEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
friend class WebEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
friend class LineEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
friend class PolyVoxEntityItem; // TODO: consider removing this friend relationship and use public methods
|
||||
public:
|
||||
EntityItemProperties();
|
||||
virtual ~EntityItemProperties();
|
||||
|
@ -135,6 +136,9 @@ public:
|
|||
DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float);
|
||||
DEFINE_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float);
|
||||
DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3);
|
||||
DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3);
|
||||
DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray);
|
||||
DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t);
|
||||
DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_BACKGROUND_MODE, BackgroundMode, backgroundMode, BackgroundMode);
|
||||
DEFINE_PROPERTY_GROUP(Stage, stage, StagePropertyGroup);
|
||||
|
@ -191,6 +195,10 @@ public:
|
|||
|
||||
QString getSimulatorIDAsString() const { return _simulatorID.toString().mid(1,36).toUpper(); }
|
||||
|
||||
void setVoxelDataDirty() { _voxelDataChanged = true; }
|
||||
|
||||
bool hasTerseUpdateChanges() const;
|
||||
|
||||
private:
|
||||
QUuid _id;
|
||||
bool _idSet;
|
||||
|
@ -211,6 +219,7 @@ private:
|
|||
QStringList _textureNames;
|
||||
glm::vec3 _naturalDimensions;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(EntityItemProperties);
|
||||
QScriptValue EntityItemPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties);
|
||||
QScriptValue EntityItemNonDefaultPropertiesToScriptValue(QScriptEngine* engine, const EntityItemProperties& properties);
|
||||
|
@ -221,7 +230,6 @@ void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemP
|
|||
inline void EntityItemProperties::setPosition(const glm::vec3& value)
|
||||
{ _position = glm::clamp(value, 0.0f, (float)TREE_SCALE); _positionChanged = true; }
|
||||
|
||||
|
||||
inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
||||
debug << "EntityItemProperties[" << "\n";
|
||||
|
||||
|
@ -283,6 +291,9 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
|||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParticleRadius, particleRadius, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, MarketplaceID, marketplaceID, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, "");
|
||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, "");
|
||||
|
||||
properties.getStage().debugDump();
|
||||
properties.getAtmosphere().debugDump();
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#define READ_ENTITY_PROPERTY(P,T,S) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
T fromBuffer; \
|
||||
int bytes = OctreePacketData::uppackDataFromBytes(dataAt, fromBuffer); \
|
||||
int bytes = OctreePacketData::unpackDataFromBytes(dataAt, fromBuffer); \
|
||||
dataAt += bytes; \
|
||||
bytesRead += bytes; \
|
||||
if (overwriteLocalData) { \
|
||||
|
@ -49,7 +49,7 @@
|
|||
#define READ_ENTITY_PROPERTY_TO_PROPERTIES(P,T,O) \
|
||||
if (propertyFlags.getHasProperty(P)) { \
|
||||
T fromBuffer; \
|
||||
int bytes = OctreePacketData::uppackDataFromBytes(dataAt, fromBuffer); \
|
||||
int bytes = OctreePacketData::unpackDataFromBytes(dataAt, fromBuffer); \
|
||||
dataAt += bytes; \
|
||||
processedBytes += bytes; \
|
||||
properties.O(fromBuffer); \
|
||||
|
@ -91,11 +91,19 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, float v) { return QScri
|
|||
inline QScriptValue convertScriptValue(QScriptEngine* e, int v) { return QScriptValue(v); }
|
||||
inline QScriptValue convertScriptValue(QScriptEngine* e, quint32 v) { return QScriptValue(v); }
|
||||
inline QScriptValue convertScriptValue(QScriptEngine* e, const QString& v) { return QScriptValue(v); }
|
||||
|
||||
inline QScriptValue convertScriptValue(QScriptEngine* e, const xColor& v) { return xColorToScriptValue(e, v); }
|
||||
inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::quat& v) { return quatToScriptValue(e, v); }
|
||||
inline QScriptValue convertScriptValue(QScriptEngine* e, const QScriptValue& v) { return v; }
|
||||
inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector<glm::vec3>& v) {return qVectorVec3ToScriptValue(e, v); }
|
||||
|
||||
inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) {
|
||||
QByteArray b64 = v.toBase64();
|
||||
return QScriptValue(QString(b64));
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(G,g,P,p) \
|
||||
if (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P()) { \
|
||||
QScriptValue groupProperties = properties.property(#g); \
|
||||
|
@ -131,6 +139,16 @@ inline int int_convertFromScriptValue(const QScriptValue& v, bool& isValid) { re
|
|||
inline bool bool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toBool(); }
|
||||
inline QString QString_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toString().trimmed(); }
|
||||
inline QUuid QUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); }
|
||||
|
||||
|
||||
inline QByteArray QByteArray_convertFromScriptValue(const QScriptValue& v, bool& isValid) {
|
||||
isValid = true;
|
||||
QString b64 = v.toVariant().toString().trimmed();
|
||||
return QByteArray::fromBase64(b64.toUtf8());
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline glmVec3 glmVec3_convertFromScriptValue(const QScriptValue& v, bool& isValid) {
|
||||
isValid = false; /// assume it can't be converted
|
||||
QScriptValue x = v.property("x");
|
||||
|
|
|
@ -109,10 +109,18 @@ enum EntityPropertyList {
|
|||
PROP_COLLISION_SOUND_URL,
|
||||
PROP_RESTITUTION,
|
||||
PROP_FRICTION,
|
||||
<<<<<<< HEAD
|
||||
|
||||
//for lines
|
||||
PROP_LINE_WIDTH,
|
||||
PROP_LINE_POINTS,
|
||||
=======
|
||||
|
||||
PROP_VOXEL_VOLUME_SIZE,
|
||||
PROP_VOXEL_DATA,
|
||||
PROP_VOXEL_SURFACE_STYLE,
|
||||
|
||||
>>>>>>> master
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ATTENTION: add new properties ABOVE this line
|
||||
PROP_AFTER_LAST_ITEM,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "ModelEntityItem.h"
|
||||
#include "ZoneEntityItem.h"
|
||||
#include "EntitiesLogging.h"
|
||||
#include "PolyVoxEntityItem.h"
|
||||
|
||||
|
||||
EntityScriptingInterface::EntityScriptingInterface() :
|
||||
|
@ -78,11 +79,12 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
|
|||
bool success = true;
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForWrite();
|
||||
EntityItem* entity = _entityTree->addEntity(id, propertiesWithSimID);
|
||||
EntityItemPointer entity = _entityTree->addEntity(id, propertiesWithSimID);
|
||||
if (entity) {
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
// This Node is creating a new object. If it's in motion, set this Node as the simulator.
|
||||
bidForSimulationOwnership(propertiesWithSimID);
|
||||
entity->setSimulatorID(propertiesWithSimID.getSimulatorID()); // and make note of it now, so we can act on it right away.
|
||||
} else {
|
||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||
success = false;
|
||||
|
@ -102,7 +104,8 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
|
|||
EntityItemProperties results;
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(EntityItemID(identity)));
|
||||
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));
|
||||
|
||||
if (entity) {
|
||||
results = entity->getProperties();
|
||||
|
@ -137,13 +140,23 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
|||
|
||||
// make sure the properties has a type, so that the encode can know which properties to include
|
||||
if (properties.getType() == EntityTypes::Unknown) {
|
||||
EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
// we need to change the outgoing properties, so we make a copy, modify, and send.
|
||||
EntityItemProperties modifiedProperties = properties;
|
||||
entity->setLastBroadcast(usecTimestampNow());
|
||||
modifiedProperties.setType(entity->getType());
|
||||
bidForSimulationOwnership(modifiedProperties);
|
||||
if (modifiedProperties.hasTerseUpdateChanges()) {
|
||||
// we make a bid for (or assert) our simulation ownership
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||
modifiedProperties.setSimulatorID(myNodeID);
|
||||
|
||||
if (entity->getSimulatorID() == myNodeID) {
|
||||
// we think we already own simulation, so make sure we send ALL TerseUpdate properties
|
||||
entity->getAllTerseUpdateProperties(modifiedProperties);
|
||||
}
|
||||
}
|
||||
queueEntityMessage(PacketTypeEntityEdit, entityID, modifiedProperties);
|
||||
return id;
|
||||
}
|
||||
|
@ -161,7 +174,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
|
|||
if (_entityTree) {
|
||||
_entityTree->lockForWrite();
|
||||
|
||||
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(entityID));
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||
if (entity) {
|
||||
if (entity->getLocked()) {
|
||||
shouldDelete = false;
|
||||
|
@ -183,7 +196,7 @@ QUuid EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float
|
|||
EntityItemID result;
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
const EntityItem* closestEntity = _entityTree->findClosestEntity(center, radius);
|
||||
EntityItemPointer closestEntity = _entityTree->findClosestEntity(center, radius);
|
||||
_entityTree->unlock();
|
||||
if (closestEntity) {
|
||||
result = closestEntity->getEntityItemID();
|
||||
|
@ -205,11 +218,11 @@ QVector<QUuid> EntityScriptingInterface::findEntities(const glm::vec3& center, f
|
|||
QVector<QUuid> result;
|
||||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
QVector<const EntityItem*> entities;
|
||||
QVector<EntityItemPointer> entities;
|
||||
_entityTree->findEntities(center, radius, entities);
|
||||
_entityTree->unlock();
|
||||
|
||||
foreach (const EntityItem* entity, entities) {
|
||||
foreach (EntityItemPointer entity, entities) {
|
||||
result << entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
|
@ -221,11 +234,11 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn
|
|||
if (_entityTree) {
|
||||
_entityTree->lockForRead();
|
||||
AABox box(corner, dimensions);
|
||||
QVector<EntityItem*> entities;
|
||||
QVector<EntityItemPointer> entities;
|
||||
_entityTree->findEntities(box, entities);
|
||||
_entityTree->unlock();
|
||||
|
||||
foreach (const EntityItem* entity, entities) {
|
||||
foreach (EntityItemPointer entity, entities) {
|
||||
result << entity->getEntityItemID();
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +261,7 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke
|
|||
RayToEntityIntersectionResult result;
|
||||
if (_entityTree) {
|
||||
OctreeElement* element;
|
||||
EntityItem* intersectedEntity = NULL;
|
||||
EntityItemPointer intersectedEntity = NULL;
|
||||
result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
|
||||
(void**)&intersectedEntity, lockType, &result.accurate,
|
||||
precisionPicking);
|
||||
|
@ -380,3 +393,40 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
vec3FromScriptValue(intersection, value.intersection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) {
|
||||
if (!_entityTree) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EntityItemPointer entity = static_cast<EntityItemPointer>(_entityTree->findEntityByEntityItemID(entityID));
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "EntityScriptingInterface::setVoxelSphere no entity with ID" << entityID;
|
||||
return false;
|
||||
}
|
||||
|
||||
EntityTypes::EntityType entityType = entity->getType();
|
||||
if (entityType != EntityTypes::PolyVox) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto now = usecTimestampNow();
|
||||
|
||||
PolyVoxEntityItem* polyVoxEntity = static_cast<PolyVoxEntityItem*>(entity.get());
|
||||
_entityTree->lockForWrite();
|
||||
polyVoxEntity->setSphere(center, radius, value);
|
||||
entity->setLastEdited(now);
|
||||
entity->setLastBroadcast(now);
|
||||
_entityTree->unlock();
|
||||
|
||||
_entityTree->lockForRead();
|
||||
EntityItemProperties properties = entity->getProperties();
|
||||
_entityTree->unlock();
|
||||
|
||||
properties.setVoxelDataDirty();
|
||||
properties.setLastEdited(now);
|
||||
|
||||
queueEntityMessage(PacketTypeEntityEdit, entityID, properties);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ public:
|
|||
float distance;
|
||||
BoxFace face;
|
||||
glm::vec3 intersection;
|
||||
EntityItem* entity;
|
||||
EntityItemPointer entity;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(RayToEntityIntersectionResult)
|
||||
|
@ -117,10 +117,13 @@ public slots:
|
|||
Q_INVOKABLE void setSendPhysicsUpdates(bool value);
|
||||
Q_INVOKABLE bool getSendPhysicsUpdates() const;
|
||||
|
||||
Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value);
|
||||
|
||||
Q_INVOKABLE void dumpTree() const;
|
||||
|
||||
signals:
|
||||
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
|
||||
void canAdjustLocksChanged(bool canAdjustLocks);
|
||||
void canRezChanged(bool canRez);
|
||||
|
|
|
@ -38,22 +38,21 @@ void EntitySimulation::updateEntities() {
|
|||
}
|
||||
|
||||
void EntitySimulation::getEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||
for (auto entityItr : _entitiesToDelete) {
|
||||
EntityItem* entity = &(*entityItr);
|
||||
|
||||
for (auto entity : _entitiesToDelete) {
|
||||
// this entity is still in its tree, so we insert into the external list
|
||||
entitiesToDelete.push_back(entity);
|
||||
++entityItr;
|
||||
}
|
||||
_entitiesToDelete.clear();
|
||||
}
|
||||
|
||||
void EntitySimulation::addEntityInternal(EntityItem* entity) {
|
||||
void EntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||
if (entity->isMoving() && !entity->getPhysicsInfo()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void EntitySimulation::changeEntityInternal(EntityItem* entity) {
|
||||
void EntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||
if (entity->isMoving() && !entity->getPhysicsInfo()) {
|
||||
_simpleKinematicEntities.insert(entity);
|
||||
} else {
|
||||
|
@ -68,7 +67,7 @@ void EntitySimulation::expireMortalEntities(const quint64& now) {
|
|||
_nextExpiry = quint64(-1);
|
||||
SetOfEntities::iterator itemItr = _mortalEntities.begin();
|
||||
while (itemItr != _mortalEntities.end()) {
|
||||
EntityItem* entity = *itemItr;
|
||||
EntityItemPointer entity = *itemItr;
|
||||
quint64 expiry = entity->getExpiry();
|
||||
if (expiry < now) {
|
||||
_entitiesToDelete.insert(entity);
|
||||
|
@ -96,7 +95,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
|||
PerformanceTimer perfTimer("updatingEntities");
|
||||
SetOfEntities::iterator itemItr = _entitiesToUpdate.begin();
|
||||
while (itemItr != _entitiesToUpdate.end()) {
|
||||
EntityItem* entity = *itemItr;
|
||||
EntityItemPointer entity = *itemItr;
|
||||
// TODO: catch transition from needing update to not as a "change"
|
||||
// so we don't have to scan for it here.
|
||||
if (!entity->needsToCallUpdate()) {
|
||||
|
@ -117,7 +116,7 @@ void EntitySimulation::sortEntitiesThatMoved() {
|
|||
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), (float)TREE_SCALE);
|
||||
SetOfEntities::iterator itemItr = _entitiesToSort.begin();
|
||||
while (itemItr != _entitiesToSort.end()) {
|
||||
EntityItem* entity = *itemItr;
|
||||
EntityItemPointer entity = *itemItr;
|
||||
// check to see if this movement has sent the entity outside of the domain.
|
||||
AACube newCube = entity->getMaximumAACube();
|
||||
if (!domainBounds.touches(newCube)) {
|
||||
|
@ -145,7 +144,7 @@ void EntitySimulation::sortEntitiesThatMoved() {
|
|||
_entitiesToSort.clear();
|
||||
}
|
||||
|
||||
void EntitySimulation::addEntity(EntityItem* entity) {
|
||||
void EntitySimulation::addEntity(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
if (entity->isMortal()) {
|
||||
_mortalEntities.insert(entity);
|
||||
|
@ -167,7 +166,7 @@ void EntitySimulation::addEntity(EntityItem* entity) {
|
|||
entity->clearDirtyFlags();
|
||||
}
|
||||
|
||||
void EntitySimulation::removeEntity(EntityItem* entity) {
|
||||
void EntitySimulation::removeEntity(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
_entitiesToUpdate.remove(entity);
|
||||
_mortalEntities.remove(entity);
|
||||
|
@ -180,7 +179,7 @@ void EntitySimulation::removeEntity(EntityItem* entity) {
|
|||
entity->_simulated = false;
|
||||
}
|
||||
|
||||
void EntitySimulation::changeEntity(EntityItem* entity) {
|
||||
void EntitySimulation::changeEntity(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
if (!entity->_simulated) {
|
||||
// This entity was either never added to the simulation or has been removed
|
||||
|
@ -250,7 +249,7 @@ void EntitySimulation::clearEntities() {
|
|||
void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
||||
SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin();
|
||||
while (itemItr != _simpleKinematicEntities.end()) {
|
||||
EntityItem* entity = *itemItr;
|
||||
EntityItemPointer entity = *itemItr;
|
||||
if (entity->isMoving() && !entity->getPhysicsInfo()) {
|
||||
entity->simulate(now);
|
||||
_entitiesToSort.insert(entity);
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
#include "EntityItem.h"
|
||||
#include "EntityTree.h"
|
||||
|
||||
typedef QSet<EntityItem*> SetOfEntities;
|
||||
typedef QVector<EntityItem*> VectorOfEntities;
|
||||
typedef QSet<EntityItemPointer> SetOfEntities;
|
||||
typedef QVector<EntityItemPointer> VectorOfEntities;
|
||||
|
||||
// the EntitySimulation needs to know when these things change on an entity,
|
||||
// so it can sort EntityItem or relay its state to the PhysicsEngine.
|
||||
|
@ -59,16 +59,16 @@ public:
|
|||
protected: // these only called by the EntityTree?
|
||||
/// \param entity pointer to EntityItem to be added
|
||||
/// \sideeffect sets relevant backpointers in entity, but maybe later when appropriate data structures are locked
|
||||
void addEntity(EntityItem* entity);
|
||||
void addEntity(EntityItemPointer entity);
|
||||
|
||||
/// \param entity pointer to EntityItem to be removed
|
||||
/// \brief the actual removal may happen later when appropriate data structures are locked
|
||||
/// \sideeffect nulls relevant backpointers in entity
|
||||
void removeEntity(EntityItem* entity);
|
||||
void removeEntity(EntityItemPointer entity);
|
||||
|
||||
/// \param entity pointer to EntityItem to that may have changed in a way that would affect its simulation
|
||||
/// call this whenever an entity was changed from some EXTERNAL event (NOT by the EntitySimulation itself)
|
||||
void changeEntity(EntityItem* entity);
|
||||
void changeEntity(EntityItemPointer entity);
|
||||
|
||||
void clearEntities();
|
||||
|
||||
|
@ -88,9 +88,9 @@ protected:
|
|||
// These pure virtual methods are protected because they are not to be called will-nilly. The base class
|
||||
// calls them in the right places.
|
||||
virtual void updateEntitiesInternal(const quint64& now) = 0;
|
||||
virtual void addEntityInternal(EntityItem* entity);
|
||||
virtual void removeEntityInternal(EntityItem* entity) = 0;
|
||||
virtual void changeEntityInternal(EntityItem* entity);
|
||||
virtual void addEntityInternal(EntityItemPointer entity);
|
||||
virtual void removeEntityInternal(EntityItemPointer entity) = 0;
|
||||
virtual void changeEntityInternal(EntityItemPointer entity);
|
||||
virtual void clearEntitiesInternal() = 0;
|
||||
|
||||
void expireMortalEntities(const quint64& now);
|
||||
|
|
|
@ -75,7 +75,7 @@ bool EntityTree::handlesEditPacketType(PacketType packetType) const {
|
|||
}
|
||||
|
||||
/// Adds a new entity item to the tree
|
||||
void EntityTree::postAddEntity(EntityItem* entity) {
|
||||
void EntityTree::postAddEntity(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
// check to see if we need to simulate this entity..
|
||||
if (_simulation) {
|
||||
|
@ -84,6 +84,7 @@ void EntityTree::postAddEntity(EntityItem* entity) {
|
|||
_simulation->unlock();
|
||||
}
|
||||
_isDirty = true;
|
||||
maybeNotifyNewCollisionSoundURL("", entity->getCollisionSoundURL());
|
||||
emit addingEntity(entity->getEntityItemID());
|
||||
}
|
||||
|
||||
|
@ -94,7 +95,7 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
|
|||
return false;
|
||||
}
|
||||
|
||||
EntityItem* existingEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
if (!existingEntity) {
|
||||
qCDebug(entities) << "UNEXPECTED!!!! don't call updateEntity() on entity items that don't exist. entityID=" << entityID;
|
||||
return false;
|
||||
|
@ -103,7 +104,7 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
|
|||
return updateEntityWithElement(existingEntity, properties, containingElement, senderNode);
|
||||
}
|
||||
|
||||
bool EntityTree::updateEntity(EntityItem* entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
||||
bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
||||
EntityTreeElement* containingElement = getContainingElement(entity->getEntityItemID());
|
||||
if (!containingElement) {
|
||||
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entity-->element lookup failed!!! entityID="
|
||||
|
@ -113,7 +114,7 @@ bool EntityTree::updateEntity(EntityItem* entity, const EntityItemProperties& pr
|
|||
return updateEntityWithElement(entity, properties, containingElement, senderNode);
|
||||
}
|
||||
|
||||
bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemProperties& origProperties,
|
||||
bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& origProperties,
|
||||
EntityTreeElement* containingElement, const SharedNodePointer& senderNode) {
|
||||
EntityItemProperties properties = origProperties;
|
||||
|
||||
|
@ -184,6 +185,7 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
|
|||
// else client accepts what the server says
|
||||
|
||||
QString entityScriptBefore = entity->getScript();
|
||||
QString collisionSoundURLBefore = entity->getCollisionSoundURL();
|
||||
uint32_t preFlags = entity->getDirtyFlags();
|
||||
UpdateEntityOperator theOperator(this, containingElement, entity, properties);
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
|
@ -206,8 +208,9 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
|
|||
QString entityScriptAfter = entity->getScript();
|
||||
if (entityScriptBefore != entityScriptAfter) {
|
||||
emitEntityScriptChanging(entity->getEntityItemID()); // the entity script has changed
|
||||
}
|
||||
}
|
||||
}
|
||||
maybeNotifyNewCollisionSoundURL(collisionSoundURLBefore, entity->getCollisionSoundURL());
|
||||
}
|
||||
|
||||
// TODO: this final containingElement check should eventually be removed (or wrapped in an #ifdef DEBUG).
|
||||
containingElement = getContainingElement(entity->getEntityItemID());
|
||||
|
@ -220,8 +223,8 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
|
|||
return true;
|
||||
}
|
||||
|
||||
EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItem* result = NULL;
|
||||
EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer result = NULL;
|
||||
|
||||
if (getIsClient()) {
|
||||
// if our Node isn't allowed to create entities in this domain, don't try.
|
||||
|
@ -266,6 +269,11 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem
|
|||
void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) {
|
||||
emit entityScriptChanging(entityItemID);
|
||||
}
|
||||
void EntityTree::maybeNotifyNewCollisionSoundURL(const QString& previousCollisionSoundURL, const QString& nextCollisionSoundURL) {
|
||||
if (!nextCollisionSoundURL.isEmpty() && (nextCollisionSoundURL != previousCollisionSoundURL)) {
|
||||
emit newCollisionSoundURL(QUrl(nextCollisionSoundURL));
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::setSimulation(EntitySimulation* simulation) {
|
||||
if (simulation) {
|
||||
|
@ -291,7 +299,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ign
|
|||
return;
|
||||
}
|
||||
|
||||
EntityItem* existingEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
if (!existingEntity) {
|
||||
if (!ignoreWarnings) {
|
||||
qCDebug(entities) << "UNEXPECTED!!!! don't call EntityTree::deleteEntity() on entity items that don't exist. "
|
||||
|
@ -328,7 +336,7 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs, bool force, bool i
|
|||
continue;
|
||||
}
|
||||
|
||||
EntityItem* existingEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
EntityItemPointer existingEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
if (!existingEntity) {
|
||||
if (!ignoreWarnings) {
|
||||
qCDebug(entities) << "UNEXPECTED!!!! don't call EntityTree::deleteEntities() on entity items that don't exist. "
|
||||
|
@ -362,7 +370,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
_simulation->lock();
|
||||
}
|
||||
foreach(const EntityToDeleteDetails& details, entities) {
|
||||
EntityItem* theEntity = details.entity;
|
||||
EntityItemPointer theEntity = details.entity;
|
||||
|
||||
if (getIsServer()) {
|
||||
// set up the deleted entities ID
|
||||
|
@ -374,8 +382,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
|||
|
||||
if (_simulation) {
|
||||
_simulation->removeEntity(theEntity);
|
||||
}
|
||||
delete theEntity; // we can delete the entity immediately
|
||||
}
|
||||
}
|
||||
if (_simulation) {
|
||||
_simulation->unlock();
|
||||
|
@ -388,7 +395,7 @@ public:
|
|||
glm::vec3 position;
|
||||
float targetRadius;
|
||||
bool found;
|
||||
const EntityItem* closestEntity;
|
||||
EntityItemPointer closestEntity;
|
||||
float closestEntityDistance;
|
||||
};
|
||||
|
||||
|
@ -402,7 +409,7 @@ bool EntityTree::findNearPointOperation(OctreeElement* element, void* extraData)
|
|||
|
||||
// If this entityTreeElement contains the point, then search it...
|
||||
if (sphereIntersection) {
|
||||
const EntityItem* thisClosestEntity = entityTreeElement->getClosestEntity(args->position);
|
||||
EntityItemPointer thisClosestEntity = entityTreeElement->getClosestEntity(args->position);
|
||||
|
||||
// we may have gotten NULL back, meaning no entity was available
|
||||
if (thisClosestEntity) {
|
||||
|
@ -428,7 +435,7 @@ bool EntityTree::findNearPointOperation(OctreeElement* element, void* extraData)
|
|||
return false;
|
||||
}
|
||||
|
||||
const EntityItem* EntityTree::findClosestEntity(glm::vec3 position, float targetRadius) {
|
||||
EntityItemPointer EntityTree::findClosestEntity(glm::vec3 position, float targetRadius) {
|
||||
FindNearPointArgs args = { position, targetRadius, false, NULL, FLT_MAX };
|
||||
lockForRead();
|
||||
// NOTE: This should use recursion, since this is a spatial operation
|
||||
|
@ -441,7 +448,7 @@ class FindAllNearPointArgs {
|
|||
public:
|
||||
glm::vec3 position;
|
||||
float targetRadius;
|
||||
QVector<const EntityItem*> entities;
|
||||
QVector<EntityItemPointer> entities;
|
||||
};
|
||||
|
||||
|
||||
|
@ -462,8 +469,8 @@ bool EntityTree::findInSphereOperation(OctreeElement* element, void* extraData)
|
|||
}
|
||||
|
||||
// NOTE: assumes caller has handled locking
|
||||
void EntityTree::findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntities) {
|
||||
FindAllNearPointArgs args = { center, radius, QVector<const EntityItem*>() };
|
||||
void EntityTree::findEntities(const glm::vec3& center, float radius, QVector<EntityItemPointer>& foundEntities) {
|
||||
FindAllNearPointArgs args = { center, radius, QVector<EntityItemPointer>() };
|
||||
// NOTE: This should use recursion, since this is a spatial operation
|
||||
recurseTreeWithOperation(findInSphereOperation, &args);
|
||||
|
||||
|
@ -478,7 +485,7 @@ public:
|
|||
}
|
||||
|
||||
AACube _cube;
|
||||
QVector<EntityItem*> _foundEntities;
|
||||
QVector<EntityItemPointer> _foundEntities;
|
||||
};
|
||||
|
||||
bool EntityTree::findInCubeOperation(OctreeElement* element, void* extraData) {
|
||||
|
@ -492,7 +499,7 @@ bool EntityTree::findInCubeOperation(OctreeElement* element, void* extraData) {
|
|||
}
|
||||
|
||||
// NOTE: assumes caller has handled locking
|
||||
void EntityTree::findEntities(const AACube& cube, QVector<EntityItem*>& foundEntities) {
|
||||
void EntityTree::findEntities(const AACube& cube, QVector<EntityItemPointer>& foundEntities) {
|
||||
FindEntitiesInCubeArgs args(cube);
|
||||
// NOTE: This should use recursion, since this is a spatial operation
|
||||
recurseTreeWithOperation(findInCubeOperation, &args);
|
||||
|
@ -507,7 +514,7 @@ public:
|
|||
}
|
||||
|
||||
AABox _box;
|
||||
QVector<EntityItem*> _foundEntities;
|
||||
QVector<EntityItemPointer> _foundEntities;
|
||||
};
|
||||
|
||||
bool EntityTree::findInBoxOperation(OctreeElement* element, void* extraData) {
|
||||
|
@ -521,7 +528,7 @@ bool EntityTree::findInBoxOperation(OctreeElement* element, void* extraData) {
|
|||
}
|
||||
|
||||
// NOTE: assumes caller has handled locking
|
||||
void EntityTree::findEntities(const AABox& box, QVector<EntityItem*>& foundEntities) {
|
||||
void EntityTree::findEntities(const AABox& box, QVector<EntityItemPointer>& foundEntities) {
|
||||
FindEntitiesInBoxArgs args(box);
|
||||
// NOTE: This should use recursion, since this is a spatial operation
|
||||
recurseTreeWithOperation(findInBoxOperation, &args);
|
||||
|
@ -529,13 +536,13 @@ void EntityTree::findEntities(const AABox& box, QVector<EntityItem*>& foundEntit
|
|||
foundEntities.swap(args._foundEntities);
|
||||
}
|
||||
|
||||
EntityItem* EntityTree::findEntityByID(const QUuid& id) {
|
||||
EntityItemPointer EntityTree::findEntityByID(const QUuid& id) {
|
||||
EntityItemID entityID(id);
|
||||
return findEntityByEntityItemID(entityID);
|
||||
}
|
||||
|
||||
EntityItem* EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) /*const*/ {
|
||||
EntityItem* foundEntity = NULL;
|
||||
EntityItemPointer EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) /*const*/ {
|
||||
EntityItemPointer foundEntity = NULL;
|
||||
EntityTreeElement* containingElement = getContainingElement(entityID);
|
||||
if (containingElement) {
|
||||
foundEntity = containingElement->getEntityWithEntityItemID(entityID);
|
||||
|
@ -571,7 +578,7 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
|||
// an existing entity... handle appropriately
|
||||
if (validEditPacket) {
|
||||
// search for the entity by EntityItemID
|
||||
EntityItem* existingEntity = findEntityByEntityItemID(entityItemID);
|
||||
EntityItemPointer existingEntity = findEntityByEntityItemID(entityItemID);
|
||||
if (existingEntity && packetType == PacketTypeEntityEdit) {
|
||||
// if the EntityItem exists, then update it
|
||||
if (wantEditLogging()) {
|
||||
|
@ -588,7 +595,7 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
|||
qCDebug(entities) << " properties:" << properties;
|
||||
}
|
||||
properties.setCreated(properties.getLastEdited());
|
||||
EntityItem* newEntity = addEntity(entityItemID, properties);
|
||||
EntityItemPointer newEntity = addEntity(entityItemID, properties);
|
||||
if (newEntity) {
|
||||
newEntity->markAsChangedOnServer();
|
||||
notifyNewlyCreatedEntity(*newEntity, senderNode);
|
||||
|
@ -604,7 +611,7 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
|||
<< "] attempted to add an entity.";
|
||||
}
|
||||
} else {
|
||||
qCDebug(entities) << "Add or Edit failed." << packetType << existingEntity;
|
||||
qCDebug(entities) << "Add or Edit failed." << packetType << existingEntity.get();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -652,7 +659,7 @@ void EntityTree::releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncod
|
|||
extraEncodeData->clear();
|
||||
}
|
||||
|
||||
void EntityTree::entityChanged(EntityItem* entity) {
|
||||
void EntityTree::entityChanged(EntityItemPointer entity) {
|
||||
if (_simulation) {
|
||||
_simulation->lock();
|
||||
_simulation->changeEntity(entity);
|
||||
|
@ -672,11 +679,12 @@ void EntityTree::update() {
|
|||
if (pendingDeletes.size() > 0) {
|
||||
// translate into list of ID's
|
||||
QSet<EntityItemID> idsToDelete;
|
||||
for (auto entityItr : pendingDeletes) {
|
||||
EntityItem* entity = &(*entityItr);
|
||||
|
||||
for (auto entity : pendingDeletes) {
|
||||
assert(!entity->getPhysicsInfo()); // TODO: Andrew to remove this after testing
|
||||
idsToDelete.insert(entity->getEntityItemID());
|
||||
}
|
||||
|
||||
// delete these things the roundabout way
|
||||
deleteEntities(idsToDelete, true);
|
||||
}
|
||||
|
@ -1004,7 +1012,7 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData)
|
|||
SendEntitiesOperationArgs* args = static_cast<SendEntitiesOperationArgs*>(extraData);
|
||||
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||
|
||||
const QList<EntityItem*>& entities = entityTreeElement->getEntities();
|
||||
const EntityItems& entities = entityTreeElement->getEntities();
|
||||
for (int i = 0; i < entities.size(); i++) {
|
||||
EntityItemID newID(QUuid::createUuid());
|
||||
args->newEntityIDs->append(newID);
|
||||
|
@ -1056,7 +1064,7 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
entityItemID = EntityItemID(QUuid::createUuid());
|
||||
}
|
||||
|
||||
EntityItem* entity = addEntity(entityItemID, properties);
|
||||
EntityItemPointer entity = addEntity(entityItemID, properties);
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType();
|
||||
}
|
||||
|
|
|
@ -30,9 +30,9 @@ public:
|
|||
|
||||
class EntityItemFBXService {
|
||||
public:
|
||||
virtual const FBXGeometry* getGeometryForEntity(const EntityItem* entityItem) = 0;
|
||||
virtual const Model* getModelForEntityItem(const EntityItem* entityItem) = 0;
|
||||
virtual const FBXGeometry* getCollisionGeometryForEntity(const EntityItem* entityItem) = 0;
|
||||
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) = 0;
|
||||
virtual const Model* getModelForEntityItem(EntityItemPointer entityItem) = 0;
|
||||
virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
@ -83,24 +83,24 @@ public:
|
|||
virtual void update();
|
||||
|
||||
// The newer API...
|
||||
void postAddEntity(EntityItem* entityItem);
|
||||
void postAddEntity(EntityItemPointer entityItem);
|
||||
|
||||
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
EntityItemPointer addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
// use this method if you only know the entityID
|
||||
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||
|
||||
// use this method if you have a pointer to the entity (avoid an extra entity lookup)
|
||||
bool updateEntity(EntityItem* entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||
bool updateEntity(EntityItemPointer entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||
|
||||
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = false);
|
||||
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = false);
|
||||
|
||||
/// \param position point of query in world-frame (meters)
|
||||
/// \param targetRadius radius of query (meters)
|
||||
const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius);
|
||||
EntityItem* findEntityByID(const QUuid& id);
|
||||
EntityItem* findEntityByEntityItemID(const EntityItemID& entityID);
|
||||
EntityItemPointer findClosestEntity(glm::vec3 position, float targetRadius);
|
||||
EntityItemPointer findEntityByID(const QUuid& id);
|
||||
EntityItemPointer findEntityByEntityItemID(const EntityItemID& entityID);
|
||||
|
||||
EntityItemID assignEntityID(const EntityItemID& entityItemID); /// Assigns a known ID for a creator token ID
|
||||
|
||||
|
@ -108,21 +108,21 @@ public:
|
|||
/// finds all entities that touch a sphere
|
||||
/// \param center the center of the sphere in world-frame (meters)
|
||||
/// \param radius the radius of the sphere in world-frame (meters)
|
||||
/// \param foundEntities[out] vector of const EntityItem*
|
||||
/// \param foundEntities[out] vector of EntityItemPointer
|
||||
/// \remark Side effect: any initial contents in foundEntities will be lost
|
||||
void findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntities);
|
||||
void findEntities(const glm::vec3& center, float radius, QVector<EntityItemPointer>& foundEntities);
|
||||
|
||||
/// finds all entities that touch a cube
|
||||
/// \param cube the query cube in world-frame (meters)
|
||||
/// \param foundEntities[out] vector of non-const EntityItem*
|
||||
/// \param foundEntities[out] vector of non-EntityItemPointer
|
||||
/// \remark Side effect: any initial contents in entities will be lost
|
||||
void findEntities(const AACube& cube, QVector<EntityItem*>& foundEntities);
|
||||
void findEntities(const AACube& cube, QVector<EntityItemPointer>& foundEntities);
|
||||
|
||||
/// finds all entities that touch a box
|
||||
/// \param box the query box in world-frame (meters)
|
||||
/// \param foundEntities[out] vector of non-const EntityItem*
|
||||
/// \param foundEntities[out] vector of non-EntityItemPointer
|
||||
/// \remark Side effect: any initial contents in entities will be lost
|
||||
void findEntities(const AABox& box, QVector<EntityItem*>& foundEntities);
|
||||
void findEntities(const AABox& box, QVector<EntityItemPointer>& foundEntities);
|
||||
|
||||
void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);
|
||||
void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);
|
||||
|
@ -138,10 +138,10 @@ public:
|
|||
|
||||
EntityItemFBXService* getFBXService() const { return _fbxService; }
|
||||
void setFBXService(EntityItemFBXService* service) { _fbxService = service; }
|
||||
const FBXGeometry* getGeometryForEntity(const EntityItem* entityItem) {
|
||||
const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) {
|
||||
return _fbxService ? _fbxService->getGeometryForEntity(entityItem) : NULL;
|
||||
}
|
||||
const Model* getModelForEntityItem(const EntityItem* entityItem) {
|
||||
const Model* getModelForEntityItem(EntityItemPointer entityItem) {
|
||||
return _fbxService ? _fbxService->getModelForEntityItem(entityItem) : NULL;
|
||||
}
|
||||
|
||||
|
@ -153,7 +153,7 @@ public:
|
|||
|
||||
QVector<EntityItemID> sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z);
|
||||
|
||||
void entityChanged(EntityItem* entity);
|
||||
void entityChanged(EntityItemPointer entity);
|
||||
|
||||
void emitEntityScriptChanging(const EntityItemID& entityItemID);
|
||||
|
||||
|
@ -171,13 +171,13 @@ signals:
|
|||
void deletingEntity(const EntityItemID& entityID);
|
||||
void addingEntity(const EntityItemID& entityID);
|
||||
void entityScriptChanging(const EntityItemID& entityItemID);
|
||||
void changingEntityID(const EntityItemID& oldEntityID, const EntityItemID& newEntityID);
|
||||
void newCollisionSoundURL(const QUrl& url);
|
||||
void clearingEntities();
|
||||
|
||||
private:
|
||||
|
||||
void processRemovedEntities(const DeleteEntityOperator& theOperator);
|
||||
bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties,
|
||||
bool updateEntityWithElement(EntityItemPointer entity, const EntityItemProperties& properties,
|
||||
EntityTreeElement* containingElement,
|
||||
const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||
|
@ -200,6 +200,7 @@ private:
|
|||
EntitySimulation* _simulation;
|
||||
|
||||
bool _wantEditLogging = false;
|
||||
void maybeNotifyNewCollisionSoundURL(const QString& oldCollisionSoundURL, const QString& newCollisionSoundURL);
|
||||
};
|
||||
|
||||
#endif // hifi_EntityTree_h
|
||||
|
|
|
@ -39,7 +39,7 @@ OctreeElement* EntityTreeElement::createNewElement(unsigned char* octalCode) {
|
|||
|
||||
void EntityTreeElement::init(unsigned char* octalCode) {
|
||||
OctreeElement::init(octalCode);
|
||||
_entityItems = new QList<EntityItem*>;
|
||||
_entityItems = new EntityItems;
|
||||
_octreeMemoryUsage += sizeof(EntityTreeElement);
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params)
|
|||
}
|
||||
}
|
||||
for (uint16_t i = 0; i < _entityItems->size(); i++) {
|
||||
EntityItem* entity = (*_entityItems)[i];
|
||||
EntityItemPointer entity = (*_entityItems)[i];
|
||||
entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
|
||||
}
|
||||
|
||||
|
@ -263,7 +263,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
|||
}
|
||||
}
|
||||
for (uint16_t i = 0; i < _entityItems->size(); i++) {
|
||||
EntityItem* entity = (*_entityItems)[i];
|
||||
EntityItemPointer entity = (*_entityItems)[i];
|
||||
entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
|
||||
}
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
|||
// need to handle the case where our sibling elements need encoding but we don't.
|
||||
if (!entityTreeElementExtraEncodeData->elementCompleted) {
|
||||
for (uint16_t i = 0; i < _entityItems->size(); i++) {
|
||||
EntityItem* entity = (*_entityItems)[i];
|
||||
EntityItemPointer entity = (*_entityItems)[i];
|
||||
bool includeThisEntity = true;
|
||||
|
||||
if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastViewFrustumSent) {
|
||||
|
@ -320,7 +320,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
|||
|
||||
if (successAppendEntityCount) {
|
||||
foreach (uint16_t i, indexesOfEntitiesToInclude) {
|
||||
EntityItem* entity = (*_entityItems)[i];
|
||||
EntityItemPointer entity = (*_entityItems)[i];
|
||||
LevelDetails entityLevel = packetData->startLevel();
|
||||
OctreeElement::AppendState appendEntityState = entity->appendEntityData(packetData,
|
||||
params, entityTreeElementExtraEncodeData);
|
||||
|
@ -408,11 +408,11 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
|||
return appendElementState;
|
||||
}
|
||||
|
||||
bool EntityTreeElement::containsEntityBounds(const EntityItem* entity) const {
|
||||
bool EntityTreeElement::containsEntityBounds(EntityItemPointer entity) const {
|
||||
return containsBounds(entity->getMaximumAACube());
|
||||
}
|
||||
|
||||
bool EntityTreeElement::bestFitEntityBounds(const EntityItem* entity) const {
|
||||
bool EntityTreeElement::bestFitEntityBounds(EntityItemPointer entity) const {
|
||||
return bestFitBounds(entity->getMaximumAACube());
|
||||
}
|
||||
|
||||
|
@ -476,14 +476,14 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
|||
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
|
||||
int entityNumber = 0;
|
||||
|
||||
QList<EntityItem*>::iterator entityItr = _entityItems->begin();
|
||||
QList<EntityItem*>::const_iterator entityEnd = _entityItems->end();
|
||||
EntityItems::iterator entityItr = _entityItems->begin();
|
||||
EntityItems::const_iterator entityEnd = _entityItems->end();
|
||||
bool somethingIntersected = false;
|
||||
|
||||
//float bestEntityDistance = distance;
|
||||
|
||||
while(entityItr != entityEnd) {
|
||||
EntityItem* entity = (*entityItr);
|
||||
EntityItemPointer entity = (*entityItr);
|
||||
|
||||
AABox entityBox = entity->getAABox();
|
||||
float localDistance;
|
||||
|
@ -519,7 +519,7 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
|||
if (localDistance < distance) {
|
||||
distance = localDistance;
|
||||
face = localFace;
|
||||
*intersectedObject = (void*)entity;
|
||||
*intersectedObject = (void*)entity.get();
|
||||
somethingIntersected = true;
|
||||
}
|
||||
}
|
||||
|
@ -528,7 +528,7 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
|||
if (localDistance < distance) {
|
||||
distance = localDistance;
|
||||
face = localFace;
|
||||
*intersectedObject = (void*)entity;
|
||||
*intersectedObject = (void*)entity.get();
|
||||
somethingIntersected = true;
|
||||
}
|
||||
}
|
||||
|
@ -545,10 +545,10 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
|||
// TODO: change this to use better bounding shape for entity than sphere
|
||||
bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const {
|
||||
QList<EntityItem*>::iterator entityItr = _entityItems->begin();
|
||||
QList<EntityItem*>::const_iterator entityEnd = _entityItems->end();
|
||||
EntityItems::iterator entityItr = _entityItems->begin();
|
||||
EntityItems::const_iterator entityEnd = _entityItems->end();
|
||||
while(entityItr != entityEnd) {
|
||||
EntityItem* entity = (*entityItr);
|
||||
EntityItemPointer entity = (*entityItr);
|
||||
glm::vec3 entityCenter = entity->getPosition();
|
||||
float entityRadius = entity->getRadius();
|
||||
|
||||
|
@ -559,7 +559,9 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad
|
|||
|
||||
if (findSphereSpherePenetration(center, radius, entityCenter, entityRadius, penetration)) {
|
||||
// return true on first valid entity penetration
|
||||
*penetratedObject = (void*)(entity);
|
||||
|
||||
*penetratedObject = (void*)(entity.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
++entityItr;
|
||||
|
@ -567,8 +569,8 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad
|
|||
return false;
|
||||
}
|
||||
|
||||
const EntityItem* EntityTreeElement::getClosestEntity(glm::vec3 position) const {
|
||||
const EntityItem* closestEntity = NULL;
|
||||
EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const {
|
||||
EntityItemPointer closestEntity = NULL;
|
||||
float closestEntityDistance = FLT_MAX;
|
||||
uint16_t numberOfEntities = _entityItems->size();
|
||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
|
@ -581,10 +583,10 @@ const EntityItem* EntityTreeElement::getClosestEntity(glm::vec3 position) const
|
|||
}
|
||||
|
||||
// TODO: change this to use better bounding shape for entity than sphere
|
||||
void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector<const EntityItem*>& foundEntities) const {
|
||||
void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector<EntityItemPointer>& foundEntities) const {
|
||||
uint16_t numberOfEntities = _entityItems->size();
|
||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
const EntityItem* entity = (*_entityItems)[i];
|
||||
EntityItemPointer entity = (*_entityItems)[i];
|
||||
float distance = glm::length(entity->getPosition() - searchPosition);
|
||||
if (distance < searchRadius + entity->getRadius()) {
|
||||
foundEntities.push_back(entity);
|
||||
|
@ -593,12 +595,12 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc
|
|||
}
|
||||
|
||||
// TODO: change this to use better bounding shape for entity than sphere
|
||||
void EntityTreeElement::getEntities(const AACube& box, QVector<EntityItem*>& foundEntities) {
|
||||
QList<EntityItem*>::iterator entityItr = _entityItems->begin();
|
||||
QList<EntityItem*>::iterator entityEnd = _entityItems->end();
|
||||
void EntityTreeElement::getEntities(const AACube& box, QVector<EntityItemPointer>& foundEntities) {
|
||||
EntityItems::iterator entityItr = _entityItems->begin();
|
||||
EntityItems::iterator entityEnd = _entityItems->end();
|
||||
AACube entityCube;
|
||||
while(entityItr != entityEnd) {
|
||||
EntityItem* entity = (*entityItr);
|
||||
EntityItemPointer entity = (*entityItr);
|
||||
float radius = entity->getRadius();
|
||||
// NOTE: we actually do cube-cube collision queries here, which is sloppy but good enough for now
|
||||
// TODO: decide whether to replace entityCube-cube query with sphere-cube (requires a square root
|
||||
|
@ -611,8 +613,8 @@ void EntityTreeElement::getEntities(const AACube& box, QVector<EntityItem*>& fou
|
|||
}
|
||||
}
|
||||
|
||||
const EntityItem* EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id) const {
|
||||
const EntityItem* foundEntity = NULL;
|
||||
EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id) const {
|
||||
EntityItemPointer foundEntity = NULL;
|
||||
uint16_t numberOfEntities = _entityItems->size();
|
||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
if ((*_entityItems)[i]->getEntityItemID() == id) {
|
||||
|
@ -623,8 +625,8 @@ const EntityItem* EntityTreeElement::getEntityWithEntityItemID(const EntityItemI
|
|||
return foundEntity;
|
||||
}
|
||||
|
||||
EntityItem* EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id) {
|
||||
EntityItem* foundEntity = NULL;
|
||||
EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id) {
|
||||
EntityItemPointer foundEntity = NULL;
|
||||
uint16_t numberOfEntities = _entityItems->size();
|
||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
if ((*_entityItems)[i]->getEntityItemID() == id) {
|
||||
|
@ -638,9 +640,13 @@ EntityItem* EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id)
|
|||
void EntityTreeElement::cleanupEntities() {
|
||||
uint16_t numberOfEntities = _entityItems->size();
|
||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
EntityItem* entity = (*_entityItems)[i];
|
||||
EntityItemPointer entity = (*_entityItems)[i];
|
||||
entity->_element = NULL;
|
||||
delete entity;
|
||||
|
||||
// NOTE: We explicitly don't delete the EntityItem here because since we only
|
||||
// access it by smart pointers, when we remove it from the _entityItems
|
||||
// we know that it will be deleted.
|
||||
//delete entity;
|
||||
}
|
||||
_entityItems->clear();
|
||||
}
|
||||
|
@ -659,7 +665,7 @@ bool EntityTreeElement::removeEntityWithEntityItemID(const EntityItemID& id) {
|
|||
return foundEntity;
|
||||
}
|
||||
|
||||
bool EntityTreeElement::removeEntityItem(EntityItem* entity) {
|
||||
bool EntityTreeElement::removeEntityItem(EntityItemPointer entity) {
|
||||
int numEntries = _entityItems->removeAll(entity);
|
||||
if (numEntries > 0) {
|
||||
assert(entity->_element == this);
|
||||
|
@ -706,7 +712,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
|||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||
int bytesForThisEntity = 0;
|
||||
EntityItemID entityItemID;
|
||||
EntityItem* entityItem = NULL;
|
||||
EntityItemPointer entityItem = NULL;
|
||||
|
||||
// Old model files don't have UUIDs in them. So we don't want to try to read those IDs from the stream.
|
||||
// Since this can only happen on loading an old file, we can safely treat these as new entity cases,
|
||||
|
@ -771,7 +777,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
|||
return bytesRead;
|
||||
}
|
||||
|
||||
void EntityTreeElement::addEntityItem(EntityItem* entity) {
|
||||
void EntityTreeElement::addEntityItem(EntityItemPointer entity) {
|
||||
assert(entity);
|
||||
assert(entity->_element == NULL);
|
||||
_entityItems->push_back(entity);
|
||||
|
@ -809,7 +815,7 @@ bool EntityTreeElement::pruneChildren() {
|
|||
void EntityTreeElement::expandExtentsToContents(Extents& extents) {
|
||||
if (_entityItems->size()) {
|
||||
for (uint16_t i = 0; i < _entityItems->size(); i++) {
|
||||
EntityItem* entity = (*_entityItems)[i];
|
||||
EntityItemPointer entity = (*_entityItems)[i];
|
||||
extents.add(entity->getAABox());
|
||||
}
|
||||
}
|
||||
|
@ -825,7 +831,7 @@ void EntityTreeElement::debugDump() {
|
|||
qCDebug(entities) << " has entities:" << _entityItems->size();
|
||||
qCDebug(entities) << "--------------------------------------------------";
|
||||
for (uint16_t i = 0; i < _entityItems->size(); i++) {
|
||||
EntityItem* entity = (*_entityItems)[i];
|
||||
EntityItemPointer entity = (*_entityItems)[i];
|
||||
entity->debugDump();
|
||||
}
|
||||
qCDebug(entities) << "--------------------------------------------------";
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#ifndef hifi_EntityTreeElement_h
|
||||
#define hifi_EntityTreeElement_h
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <OctreeElement.h>
|
||||
#include <QList>
|
||||
|
||||
|
@ -19,6 +21,8 @@
|
|||
#include "EntityItem.h"
|
||||
#include "EntityTree.h"
|
||||
|
||||
typedef QVector<EntityItemPointer> EntityItems;
|
||||
|
||||
class EntityTree;
|
||||
class EntityTreeElement;
|
||||
|
||||
|
@ -30,7 +34,7 @@ public:
|
|||
_movingItems(0)
|
||||
{ }
|
||||
|
||||
QList<EntityItem*> _movingEntities;
|
||||
QList<EntityItemPointer> _movingEntities;
|
||||
int _totalElements;
|
||||
int _totalItems;
|
||||
int _movingItems;
|
||||
|
@ -142,40 +146,41 @@ public:
|
|||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||
glm::vec3& penetration, void** penetratedObject) const;
|
||||
|
||||
const QList<EntityItem*>& getEntities() const { return *_entityItems; }
|
||||
QList<EntityItem*>& getEntities() { return *_entityItems; }
|
||||
const EntityItems& getEntities() const { return *_entityItems; }
|
||||
EntityItems& getEntities() { return *_entityItems; }
|
||||
|
||||
bool hasEntities() const { return _entityItems ? _entityItems->size() > 0 : false; }
|
||||
|
||||
void setTree(EntityTree* tree) { _myTree = tree; }
|
||||
|
||||
bool updateEntity(const EntityItem& entity);
|
||||
void addEntityItem(EntityItem* entity);
|
||||
void addEntityItem(EntityItemPointer entity);
|
||||
|
||||
const EntityItem* getClosestEntity(glm::vec3 position) const;
|
||||
EntityItemPointer getClosestEntity(glm::vec3 position) const;
|
||||
|
||||
/// finds all entities that touch a sphere
|
||||
/// \param position the center of the query sphere
|
||||
/// \param radius the radius of the query sphere
|
||||
/// \param entities[out] vector of const EntityItem*
|
||||
void getEntities(const glm::vec3& position, float radius, QVector<const EntityItem*>& foundEntities) const;
|
||||
/// \param entities[out] vector of const EntityItemPointer
|
||||
void getEntities(const glm::vec3& position, float radius, QVector<EntityItemPointer>& foundEntities) const;
|
||||
|
||||
/// finds all entities that touch a box
|
||||
/// \param box the query box
|
||||
/// \param entities[out] vector of non-const EntityItem*
|
||||
void getEntities(const AACube& box, QVector<EntityItem*>& foundEntities);
|
||||
/// \param entities[out] vector of non-const EntityItemPointer
|
||||
void getEntities(const AACube& box, QVector<EntityItemPointer>& foundEntities);
|
||||
|
||||
const EntityItem* getEntityWithID(uint32_t id) const;
|
||||
const EntityItem* getEntityWithEntityItemID(const EntityItemID& id) const;
|
||||
void getEntitiesInside(const AACube& box, QVector<EntityItem*>& foundEntities);
|
||||
EntityItemPointer getEntityWithID(uint32_t id) const;
|
||||
EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const;
|
||||
void getEntitiesInside(const AACube& box, QVector<EntityItemPointer>& foundEntities);
|
||||
|
||||
EntityItem* getEntityWithEntityItemID(const EntityItemID& id);
|
||||
EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id);
|
||||
|
||||
void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities
|
||||
bool removeEntityWithEntityItemID(const EntityItemID& id);
|
||||
bool removeEntityItem(EntityItem* entity);
|
||||
bool removeEntityItem(EntityItemPointer entity);
|
||||
|
||||
bool containsEntityBounds(const EntityItem* entity) const;
|
||||
bool bestFitEntityBounds(const EntityItem* entity) const;
|
||||
bool containsEntityBounds(EntityItemPointer entity) const;
|
||||
bool bestFitEntityBounds(EntityItemPointer entity) const;
|
||||
|
||||
bool containsBounds(const EntityItemProperties& properties) const; // NOTE: property units in meters
|
||||
bool bestFitBounds(const EntityItemProperties& properties) const; // NOTE: property units in meters
|
||||
|
@ -198,7 +203,7 @@ public:
|
|||
protected:
|
||||
virtual void init(unsigned char * octalCode);
|
||||
EntityTree* _myTree;
|
||||
QList<EntityItem*>* _entityItems;
|
||||
EntityItems* _entityItems;
|
||||
};
|
||||
|
||||
#endif // hifi_EntityTreeElement_h
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "WebEntityItem.h"
|
||||
#include "ZoneEntityItem.h"
|
||||
#include "LineEntityItem.h"
|
||||
#include "PolyVoxEntityItem.h"
|
||||
|
||||
QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
|
||||
QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
|
||||
|
@ -45,6 +46,7 @@ REGISTER_ENTITY_TYPE(Text)
|
|||
REGISTER_ENTITY_TYPE(ParticleEffect)
|
||||
REGISTER_ENTITY_TYPE(Zone)
|
||||
REGISTER_ENTITY_TYPE(Line)
|
||||
REGISTER_ENTITY_TYPE(PolyVox)
|
||||
|
||||
const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
|
||||
QMap<EntityType, QString>::iterator matchedTypeName = _typeToNameMap.find(entityType);
|
||||
|
@ -76,9 +78,9 @@ bool EntityTypes::registerEntityType(EntityType entityType, const char* name, En
|
|||
return false;
|
||||
}
|
||||
|
||||
EntityItem* EntityTypes::constructEntityItem(EntityType entityType, const EntityItemID& entityID,
|
||||
EntityItemPointer EntityTypes::constructEntityItem(EntityType entityType, const EntityItemID& entityID,
|
||||
const EntityItemProperties& properties) {
|
||||
EntityItem* newEntityItem = NULL;
|
||||
EntityItemPointer newEntityItem = NULL;
|
||||
EntityTypeFactory factory = NULL;
|
||||
if (entityType >= 0 && entityType <= LAST) {
|
||||
factory = _factories[entityType];
|
||||
|
@ -91,7 +93,7 @@ EntityItem* EntityTypes::constructEntityItem(EntityType entityType, const Entity
|
|||
return newEntityItem;
|
||||
}
|
||||
|
||||
EntityItem* EntityTypes::constructEntityItem(const unsigned char* data, int bytesToRead,
|
||||
EntityItemPointer EntityTypes::constructEntityItem(const unsigned char* data, int bytesToRead,
|
||||
ReadBitstreamToTreeParams& args) {
|
||||
|
||||
if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) {
|
||||
|
|
|
@ -20,11 +20,17 @@
|
|||
#include <OctreeRenderer.h> // for RenderArgs
|
||||
|
||||
class EntityItem;
|
||||
typedef std::shared_ptr<EntityItem> EntityItemPointer;
|
||||
|
||||
inline uint qHash(const EntityItemPointer& a, uint seed) {
|
||||
return qHash(a.get(), seed);
|
||||
}
|
||||
|
||||
class EntityItemID;
|
||||
class EntityItemProperties;
|
||||
class ReadBitstreamToTreeParams;
|
||||
|
||||
typedef EntityItem* (*EntityTypeFactory)(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
typedef EntityItemPointer (*EntityTypeFactory)(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
class EntityTypes {
|
||||
public:
|
||||
|
@ -39,14 +45,15 @@ public:
|
|||
Zone,
|
||||
Web,
|
||||
Line,
|
||||
LAST = Line
|
||||
PolyVox,
|
||||
LAST = PolyVox
|
||||
} EntityType;
|
||||
|
||||
static const QString& getEntityTypeName(EntityType entityType);
|
||||
static EntityTypes::EntityType getEntityTypeFromName(const QString& name);
|
||||
static bool registerEntityType(EntityType entityType, const char* name, EntityTypeFactory factoryMethod);
|
||||
static EntityItem* constructEntityItem(EntityType entityType, const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItem* constructEntityItem(const unsigned char* data, int bytesToRead, ReadBitstreamToTreeParams& args);
|
||||
static EntityItemPointer constructEntityItem(EntityType entityType, const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer constructEntityItem(const unsigned char* data, int bytesToRead, ReadBitstreamToTreeParams& args);
|
||||
|
||||
private:
|
||||
static QMap<EntityType, QString> _typeToNameMap;
|
||||
|
@ -59,7 +66,7 @@ private:
|
|||
/// Macro for registering entity types. Make sure to add an element to the EntityType enum with your name, and your class should be
|
||||
/// named NameEntityItem and must of a static method called factory that takes an EnityItemID, and EntityItemProperties and return a newly
|
||||
/// constructed (heap allocated) instance of your type. e.g. The following prototype:
|
||||
// static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
// static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
#define REGISTER_ENTITY_TYPE(x) static bool x##Registration = \
|
||||
EntityTypes::registerEntityType(EntityTypes::x, #x, x##EntityItem::factory);
|
||||
|
||||
|
@ -67,7 +74,7 @@ private:
|
|||
/// an element to the EntityType enum with your name. But unlike REGISTER_ENTITY_TYPE, your class can be named anything
|
||||
/// so long as you provide a static method passed to the macro, that takes an EnityItemID, and EntityItemProperties and
|
||||
/// returns a newly constructed (heap allocated) instance of your type. e.g. The following prototype:
|
||||
// static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
// static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
#define REGISTER_ENTITY_TYPE_WITH_FACTORY(x,y) static bool x##Registration = \
|
||||
EntityTypes::registerEntityType(EntityTypes::x, #x, y); \
|
||||
if (!x##Registration) { \
|
||||
|
|
|
@ -22,8 +22,9 @@
|
|||
|
||||
bool LightEntityItem::_lightsArePickable = false;
|
||||
|
||||
EntityItem* LightEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new LightEntityItem(entityID, properties);
|
||||
EntityItemPointer LightEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer result { new LightEntityItem(entityID, properties) };
|
||||
return result;
|
||||
}
|
||||
|
||||
// our non-pure virtual subclass for now...
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class LightEntityItem : public EntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
LightEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
|
||||
|
|
|
@ -20,20 +20,20 @@
|
|||
#include "EntityTreeElement.h"
|
||||
|
||||
|
||||
|
||||
const float LineEntityItem::DEFAULT_LINE_WIDTH = 2.0f;
|
||||
|
||||
|
||||
|
||||
EntityItem* LineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItem* result = new LineEntityItem(entityID, properties);
|
||||
EntityItemPointer LineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer result { new LineEntityItem(entityID, properties) };
|
||||
return result;
|
||||
}
|
||||
|
||||
LineEntityItem::LineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
EntityItem(entityItemID) ,
|
||||
_lineWidth(DEFAULT_LINE_WIDTH),
|
||||
_points(QVector<glm::vec3>(0)),
|
||||
_pointsChanged(true)
|
||||
_pointsChanged(true),
|
||||
_points(QVector<glm::vec3>(0))
|
||||
{
|
||||
_type = EntityTypes::Line;
|
||||
_created = properties.getCreated();
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
class LineEntityItem : public EntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
LineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ const bool ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false;
|
|||
const float ModelEntityItem::DEFAULT_ANIMATION_FPS = 30.0f;
|
||||
|
||||
|
||||
EntityItem* ModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return new ModelEntityItem(entityID, properties);
|
||||
EntityItemPointer ModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return EntityItemPointer(new ModelEntityItem(entityID, properties));
|
||||
}
|
||||
|
||||
ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
class ModelEntityItem : public EntityItem {
|
||||
public:
|
||||
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
ModelEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ MovingEntitiesOperator::~MovingEntitiesOperator() {
|
|||
}
|
||||
|
||||
|
||||
void MovingEntitiesOperator::addEntityToMoveList(EntityItem* entity, const AACube& newCube) {
|
||||
void MovingEntitiesOperator::addEntityToMoveList(EntityItemPointer entity, const AACube& newCube) {
|
||||
EntityTreeElement* oldContainingElement = _tree->getContainingElement(entity->getEntityItemID());
|
||||
AABox newCubeClamped = newCube.clamp(0.0f, (float)TREE_SCALE);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
class EntityToMoveDetails {
|
||||
public:
|
||||
EntityItem* entity;
|
||||
EntityItemPointer entity;
|
||||
AACube oldCube; // meters
|
||||
AACube newCube; // meters
|
||||
AABox newCubeClamped; // meters
|
||||
|
@ -37,7 +37,7 @@ public:
|
|||
MovingEntitiesOperator(EntityTree* tree);
|
||||
~MovingEntitiesOperator();
|
||||
|
||||
void addEntityToMoveList(EntityItem* entity, const AACube& newCube);
|
||||
void addEntityToMoveList(EntityItemPointer entity, const AACube& newCube);
|
||||
virtual bool preRecursion(OctreeElement* element);
|
||||
virtual bool postRecursion(OctreeElement* element);
|
||||
virtual OctreeElement* possiblyCreateChildAt(OctreeElement* element, int childIndex);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue