mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 04:57:58 +02:00
resolve conflicts on merge with upstream master
This commit is contained in:
commit
cb708859fb
52 changed files with 864 additions and 492 deletions
|
@ -27,10 +27,14 @@
|
||||||
#include <SoundCache.h>
|
#include <SoundCache.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
|
|
||||||
|
#include <recording/Deck.h>
|
||||||
|
#include <recording/Recorder.h>
|
||||||
|
|
||||||
#include <WebSocketServerClass.h>
|
#include <WebSocketServerClass.h>
|
||||||
#include <EntityScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
#include <EntityScriptingInterface.h> // TODO: consider moving to scriptengine.h
|
||||||
|
|
||||||
#include "avatars/ScriptableAvatar.h"
|
#include "avatars/ScriptableAvatar.h"
|
||||||
|
#include "RecordingScriptingInterface.h"
|
||||||
|
|
||||||
#include "Agent.h"
|
#include "Agent.h"
|
||||||
|
|
||||||
|
@ -56,8 +60,10 @@ Agent::Agent(NLPacket& packet) :
|
||||||
|
|
||||||
DependencyManager::set<ResourceCacheSharedItems>();
|
DependencyManager::set<ResourceCacheSharedItems>();
|
||||||
DependencyManager::set<SoundCache>();
|
DependencyManager::set<SoundCache>();
|
||||||
|
|
||||||
DependencyManager::set<AudioInjectorManager>();
|
DependencyManager::set<AudioInjectorManager>();
|
||||||
|
DependencyManager::set<recording::Deck>();
|
||||||
|
DependencyManager::set<recording::Recorder>();
|
||||||
|
DependencyManager::set<RecordingScriptingInterface>();
|
||||||
|
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
|
|
||||||
|
@ -117,7 +123,6 @@ void Agent::handleAudioPacket(QSharedPointer<NLPacket> packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString AGENT_LOGGING_NAME = "agent";
|
const QString AGENT_LOGGING_NAME = "agent";
|
||||||
const int PING_INTERVAL = 1000;
|
|
||||||
|
|
||||||
void Agent::run() {
|
void Agent::run() {
|
||||||
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
|
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
|
||||||
|
@ -139,10 +144,6 @@ void Agent::run() {
|
||||||
NodeType::MessagesMixer
|
NodeType::MessagesMixer
|
||||||
});
|
});
|
||||||
|
|
||||||
_pingTimer = new QTimer(this);
|
|
||||||
connect(_pingTimer, SIGNAL(timeout()), SLOT(sendPingRequests()));
|
|
||||||
_pingTimer->start(PING_INTERVAL);
|
|
||||||
|
|
||||||
// figure out the URL for the script for this agent assignment
|
// figure out the URL for the script for this agent assignment
|
||||||
QUrl scriptURL;
|
QUrl scriptURL;
|
||||||
if (_payload.isEmpty()) {
|
if (_payload.isEmpty()) {
|
||||||
|
@ -249,6 +250,8 @@ void Agent::setIsAvatar(bool isAvatar) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_isAvatar) {
|
if (!_isAvatar) {
|
||||||
|
DependencyManager::get<RecordingScriptingInterface>()->setControlledAvatar(nullptr);
|
||||||
|
|
||||||
if (_avatarIdentityTimer) {
|
if (_avatarIdentityTimer) {
|
||||||
_avatarIdentityTimer->stop();
|
_avatarIdentityTimer->stop();
|
||||||
delete _avatarIdentityTimer;
|
delete _avatarIdentityTimer;
|
||||||
|
@ -266,6 +269,7 @@ void Agent::setIsAvatar(bool isAvatar) {
|
||||||
void Agent::setAvatarData(AvatarData* avatarData, const QString& objectName) {
|
void Agent::setAvatarData(AvatarData* avatarData, const QString& objectName) {
|
||||||
_avatarData = avatarData;
|
_avatarData = avatarData;
|
||||||
_scriptEngine->registerGlobalObject(objectName, avatarData);
|
_scriptEngine->registerGlobalObject(objectName, avatarData);
|
||||||
|
DependencyManager::get<RecordingScriptingInterface>()->setControlledAvatar(avatarData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Agent::sendAvatarIdentityPacket() {
|
void Agent::sendAvatarIdentityPacket() {
|
||||||
|
@ -396,11 +400,6 @@ void Agent::aboutToFinish() {
|
||||||
_scriptEngine->stop();
|
_scriptEngine->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pingTimer) {
|
|
||||||
_pingTimer->stop();
|
|
||||||
delete _pingTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(NULL);
|
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(NULL);
|
||||||
|
|
||||||
|
@ -413,21 +412,3 @@ void Agent::aboutToFinish() {
|
||||||
// cleanup the AudioInjectorManager (and any still running injectors)
|
// cleanup the AudioInjectorManager (and any still running injectors)
|
||||||
DependencyManager::set<AudioInjectorManager>();
|
DependencyManager::set<AudioInjectorManager>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Agent::sendPingRequests() {
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
|
||||||
|
|
||||||
nodeList->eachMatchingNode([](const SharedNodePointer& node)->bool {
|
|
||||||
switch (node->getType()) {
|
|
||||||
case NodeType::AvatarMixer:
|
|
||||||
case NodeType::AudioMixer:
|
|
||||||
case NodeType::EntityServer:
|
|
||||||
case NodeType::AssetServer:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, [nodeList](const SharedNodePointer& node) {
|
|
||||||
nodeList->sendPacket(nodeList->constructPingPacket(), *node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -58,14 +58,12 @@ private slots:
|
||||||
void handleAudioPacket(QSharedPointer<NLPacket> packet);
|
void handleAudioPacket(QSharedPointer<NLPacket> packet);
|
||||||
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||||
void handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
void handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||||
void sendPingRequests();
|
|
||||||
void processAgentAvatarAndAudio(float deltaTime);
|
void processAgentAvatarAndAudio(float deltaTime);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ScriptEngine> _scriptEngine;
|
std::unique_ptr<ScriptEngine> _scriptEngine;
|
||||||
EntityEditPacketSender _entityEditSender;
|
EntityEditPacketSender _entityEditSender;
|
||||||
EntityTreeHeadlessViewer _entityViewer;
|
EntityTreeHeadlessViewer _entityViewer;
|
||||||
QTimer* _pingTimer;
|
|
||||||
|
|
||||||
MixedAudioStream _receivedAudioStream;
|
MixedAudioStream _receivedAudioStream;
|
||||||
float _lastReceivedAudioLoudness;
|
float _lastReceivedAudioLoudness;
|
||||||
|
|
|
@ -198,7 +198,7 @@ void AssignmentClient::sendStatusPacketToACM() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssignmentClient::sendAssignmentRequest() {
|
void AssignmentClient::sendAssignmentRequest() {
|
||||||
if (!_currentAssignment) {
|
if (!_currentAssignment && !_isAssigned) {
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
@ -229,8 +229,9 @@ void AssignmentClient::handleCreateAssignmentPacket(QSharedPointer<NLPacket> pac
|
||||||
// construct the deployed assignment from the packet data
|
// construct the deployed assignment from the packet data
|
||||||
_currentAssignment = AssignmentFactory::unpackAssignment(*packet);
|
_currentAssignment = AssignmentFactory::unpackAssignment(*packet);
|
||||||
|
|
||||||
if (_currentAssignment) {
|
if (_currentAssignment && !_isAssigned) {
|
||||||
qDebug() << "Received an assignment -" << *_currentAssignment;
|
qDebug() << "Received an assignment -" << *_currentAssignment;
|
||||||
|
_isAssigned = true;
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
|
@ -309,12 +310,11 @@ void AssignmentClient::handleAuthenticationRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssignmentClient::assignmentCompleted() {
|
void AssignmentClient::assignmentCompleted() {
|
||||||
|
|
||||||
// we expect that to be here the previous assignment has completely cleaned up
|
// we expect that to be here the previous assignment has completely cleaned up
|
||||||
assert(_currentAssignment.isNull());
|
assert(_currentAssignment.isNull());
|
||||||
|
|
||||||
// reset our current assignment pointer to NULL now that it has been deleted
|
// reset our current assignment pointer to null now that it has been deleted
|
||||||
_currentAssignment = NULL;
|
_currentAssignment = nullptr;
|
||||||
|
|
||||||
// reset the logging target to the the CHILD_TARGET_NAME
|
// reset the logging target to the the CHILD_TARGET_NAME
|
||||||
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME);
|
||||||
|
@ -330,4 +330,6 @@ void AssignmentClient::assignmentCompleted() {
|
||||||
nodeList->setOwnerType(NodeType::Unassigned);
|
nodeList->setOwnerType(NodeType::Unassigned);
|
||||||
nodeList->reset();
|
nodeList->reset();
|
||||||
nodeList->resetNodeInterestSet();
|
nodeList->resetNodeInterestSet();
|
||||||
|
|
||||||
|
_isAssigned = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ private:
|
||||||
|
|
||||||
Assignment _requestAssignment;
|
Assignment _requestAssignment;
|
||||||
QPointer<ThreadedAssignment> _currentAssignment;
|
QPointer<ThreadedAssignment> _currentAssignment;
|
||||||
|
bool _isAssigned { false };
|
||||||
QString _assignmentServerHostname;
|
QString _assignmentServerHostname;
|
||||||
HifiSockAddr _assignmentServerSocket;
|
HifiSockAddr _assignmentServerSocket;
|
||||||
QTimer _requestTimer; // timer for requesting and assignment
|
QTimer _requestTimer; // timer for requesting and assignment
|
||||||
|
|
|
@ -128,30 +128,7 @@ void MessagesMixer::run() {
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||||
|
|
||||||
// wait until we have the domain-server settings, otherwise we bail
|
// The messages-mixer currently does currently have any domain settings. If it did, they would be
|
||||||
DomainHandler& domainHandler = nodeList->getDomainHandler();
|
// synchronously grabbed here.
|
||||||
|
|
||||||
qDebug() << "Waiting for domain settings from domain-server.";
|
|
||||||
|
|
||||||
// block until we get the settingsRequestComplete signal
|
|
||||||
QEventLoop loop;
|
|
||||||
connect(&domainHandler, &DomainHandler::settingsReceived, &loop, &QEventLoop::quit);
|
|
||||||
connect(&domainHandler, &DomainHandler::settingsReceiveFail, &loop, &QEventLoop::quit);
|
|
||||||
domainHandler.requestDomainSettings();
|
|
||||||
loop.exec();
|
|
||||||
|
|
||||||
if (domainHandler.getSettingsObject().isEmpty()) {
|
|
||||||
qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment.";
|
|
||||||
setFinished(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse the settings to pull out the values we need
|
|
||||||
parseDomainServerSettings(domainHandler.getSettingsObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
void MessagesMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
|
||||||
// TODO - if we want options, parse them here...
|
|
||||||
const QString MESSAGES_MIXER_SETTINGS_KEY = "messages_mixer";
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,6 @@ private slots:
|
||||||
void handleMessagesUnsubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
void handleMessagesUnsubscribe(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void parseDomainServerSettings(const QJsonObject& domainSettings);
|
|
||||||
|
|
||||||
QHash<QString,QSet<QUuid>> _channelSubscribers;
|
QHash<QString,QSet<QUuid>> _channelSubscribers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -953,7 +953,6 @@ bool OctreeServer::readConfiguration() {
|
||||||
|
|
||||||
if (domainHandler.getSettingsObject().isEmpty()) {
|
if (domainHandler.getSettingsObject().isEmpty()) {
|
||||||
qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment.";
|
qDebug() << "Failed to retreive settings object from domain-server. Bailing on assignment.";
|
||||||
setFinished(true);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1086,12 +1085,16 @@ void OctreeServer::run() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
nodeList->setOwnerType(getMyNodeType());
|
nodeList->setOwnerType(getMyNodeType());
|
||||||
|
|
||||||
|
|
||||||
// use common init to setup common timers and logging
|
// use common init to setup common timers and logging
|
||||||
commonInit(getMyLoggingServerTargetName(), getMyNodeType());
|
commonInit(getMyLoggingServerTargetName(), getMyNodeType());
|
||||||
|
|
||||||
|
// we need to ask the DS about agents so we can ping/reply with them
|
||||||
|
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
||||||
|
|
||||||
// read the configuration from either the payload or the domain server configuration
|
// read the configuration from either the payload or the domain server configuration
|
||||||
if (!readConfiguration()) {
|
if (!readConfiguration()) {
|
||||||
|
qDebug() << "OctreeServer bailing on run since readConfiguration has failed.";
|
||||||
|
setFinished(true);
|
||||||
return; // bailing on run, because readConfiguration failed
|
return; // bailing on run, because readConfiguration failed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1100,10 +1103,6 @@ void OctreeServer::run() {
|
||||||
connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer)));
|
connect(nodeList.data(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer)));
|
||||||
connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
|
connect(nodeList.data(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(nodeKilled(SharedNodePointer)));
|
||||||
|
|
||||||
|
|
||||||
// we need to ask the DS about agents so we can ping/reply with them
|
|
||||||
nodeList->addNodeTypeToInterestSet(NodeType::Agent);
|
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
setvbuf(stdout, NULL, _IOLBF, 0);
|
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
11
cmake/externals/openvr/CMakeLists.txt
vendored
11
cmake/externals/openvr/CMakeLists.txt
vendored
|
@ -25,9 +25,14 @@ set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR}/headers CACHE TYPE INTERNA
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
|
||||||
# FIXME need to account for different architectures
|
# FIXME need to account for different architectures
|
||||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win32/openvr_api.lib CACHE TYPE INTERNAL)
|
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||||
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32)
|
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win64/openvr_api.lib CACHE TYPE INTERNAL)
|
||||||
|
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win64)
|
||||||
|
else()
|
||||||
|
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/win32/openvr_api.lib CACHE TYPE INTERNAL)
|
||||||
|
add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32)
|
||||||
|
endif()
|
||||||
|
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
|
|
||||||
|
|
11
cmake/externals/sdl2/CMakeLists.txt
vendored
11
cmake/externals/sdl2/CMakeLists.txt
vendored
|
@ -66,8 +66,15 @@ if (APPLE)
|
||||||
elseif (WIN32)
|
elseif (WIN32)
|
||||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory")
|
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory")
|
||||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library")
|
|
||||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL")
|
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||||
|
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x64/SDL2.lib CACHE FILEPATH "Path to SDL2 library")
|
||||||
|
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x64 CACHE PATH "Location of SDL2 DLL")
|
||||||
|
else()
|
||||||
|
set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library")
|
||||||
|
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL")
|
||||||
|
endif()
|
||||||
|
|
||||||
else ()
|
else ()
|
||||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory")
|
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory")
|
||||||
|
|
|
@ -34,17 +34,26 @@ if (UNIX)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
# http://www.slproweb.com/products/Win32OpenSSL.html
|
|
||||||
set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl
|
|
||||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]"
|
|
||||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]"
|
|
||||||
)
|
|
||||||
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles)
|
||||||
set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "${_programfiles}/OpenSSL-Win64"
|
|
||||||
"C:/OpenSSL/" "C:/OpenSSL-Win32/" "C:/OpenSSL-Win64/"
|
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||||
)
|
# http://www.slproweb.com/products/Win32OpenSSL.html
|
||||||
|
set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl
|
||||||
|
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]"
|
||||||
|
)
|
||||||
|
set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win64" "C:/OpenSSL/" "C:/OpenSSL-Win64/")
|
||||||
|
else()
|
||||||
|
# http://www.slproweb.com/products/Win32OpenSSL.html
|
||||||
|
set(_OPENSSL_ROOT_HINTS ${OPENSSL_ROOT_DIR} $ENV{OPENSSL_ROOT_DIR} $ENV{HIFI_LIB_DIR}/openssl
|
||||||
|
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]"
|
||||||
|
)
|
||||||
|
set(_OPENSSL_ROOT_PATHS "${_programfiles}/OpenSSL" "${_programfiles}/OpenSSL-Win32" "C:/OpenSSL/" "C:/OpenSSL-Win32/")
|
||||||
|
endif()
|
||||||
|
|
||||||
unset(_programfiles)
|
unset(_programfiles)
|
||||||
set(_OPENSSL_ROOT_HINTS_AND_PATHS HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS})
|
set(_OPENSSL_ROOT_HINTS_AND_PATHS HINTS ${_OPENSSL_ROOT_HINTS} PATHS ${_OPENSSL_ROOT_PATHS})
|
||||||
|
|
||||||
else ()
|
else ()
|
||||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||||
hifi_library_search_hints("openssl")
|
hifi_library_search_hints("openssl")
|
||||||
|
|
|
@ -28,6 +28,8 @@ var TRIGGER_SMOOTH_RATIO = 0.1; // 0.0 disables smoothing of trigger value
|
||||||
var TRIGGER_ON_VALUE = 0.4;
|
var TRIGGER_ON_VALUE = 0.4;
|
||||||
var TRIGGER_OFF_VALUE = 0.15;
|
var TRIGGER_OFF_VALUE = 0.15;
|
||||||
|
|
||||||
|
var BUMPER_ON_VALUE = 0.5;
|
||||||
|
|
||||||
//
|
//
|
||||||
// distant manipulation
|
// distant manipulation
|
||||||
//
|
//
|
||||||
|
@ -45,7 +47,7 @@ var PICK_MAX_DISTANCE = 500; // max length of pick-ray
|
||||||
// near grabbing
|
// near grabbing
|
||||||
//
|
//
|
||||||
|
|
||||||
var GRAB_RADIUS = 0.3; // if the ray misses but an object is this close, it will still be selected
|
var GRAB_RADIUS = 0.03; // if the ray misses but an object is this close, it will still be selected
|
||||||
var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position
|
var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position
|
||||||
var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable.
|
var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable.
|
||||||
var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected
|
var NEAR_PICK_MAX_DISTANCE = 0.3; // max length of pick-ray for close grabbing to be selected
|
||||||
|
@ -53,6 +55,13 @@ var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things
|
||||||
var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object
|
var PICK_BACKOFF_DISTANCE = 0.2; // helps when hand is intersecting the grabble object
|
||||||
var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed
|
var NEAR_GRABBING_KINEMATIC = true; // force objects to be kinematic when near-grabbed
|
||||||
|
|
||||||
|
//
|
||||||
|
// equip
|
||||||
|
//
|
||||||
|
|
||||||
|
var EQUIP_SPRING_SHUTOFF_DISTANCE = 0.05;
|
||||||
|
var EQUIP_SPRING_TIMEFRAME = 0.4; // how quickly objects move to their new position
|
||||||
|
|
||||||
//
|
//
|
||||||
// other constants
|
// other constants
|
||||||
//
|
//
|
||||||
|
@ -68,7 +77,7 @@ var ZERO_VEC = {
|
||||||
var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}";
|
var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}";
|
||||||
var MSEC_PER_SEC = 1000.0;
|
var MSEC_PER_SEC = 1000.0;
|
||||||
|
|
||||||
// these control how long an abandoned pointer line will hang around
|
// these control how long an abandoned pointer line or action will hang around
|
||||||
var LIFETIME = 10;
|
var LIFETIME = 10;
|
||||||
var ACTION_TTL = 15; // seconds
|
var ACTION_TTL = 15; // seconds
|
||||||
var ACTION_TTL_REFRESH = 5;
|
var ACTION_TTL_REFRESH = 5;
|
||||||
|
@ -106,6 +115,12 @@ var STATE_CONTINUE_NEAR_TRIGGER = 7;
|
||||||
var STATE_FAR_TRIGGER = 8;
|
var STATE_FAR_TRIGGER = 8;
|
||||||
var STATE_CONTINUE_FAR_TRIGGER = 9;
|
var STATE_CONTINUE_FAR_TRIGGER = 9;
|
||||||
var STATE_RELEASE = 10;
|
var STATE_RELEASE = 10;
|
||||||
|
var STATE_EQUIP_SEARCHING = 11;
|
||||||
|
var STATE_EQUIP = 12
|
||||||
|
var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down
|
||||||
|
var STATE_CONTINUE_EQUIP = 14;
|
||||||
|
var STATE_WAITING_FOR_BUMPER_RELEASE = 15;
|
||||||
|
var STATE_EQUIP_SPRING = 16;
|
||||||
|
|
||||||
|
|
||||||
function stateToName(state) {
|
function stateToName(state) {
|
||||||
|
@ -132,6 +147,18 @@ function stateToName(state) {
|
||||||
return "continue_far_trigger";
|
return "continue_far_trigger";
|
||||||
case STATE_RELEASE:
|
case STATE_RELEASE:
|
||||||
return "release";
|
return "release";
|
||||||
|
case STATE_EQUIP_SEARCHING:
|
||||||
|
return "equip_searching";
|
||||||
|
case STATE_EQUIP:
|
||||||
|
return "equip";
|
||||||
|
case STATE_CONTINUE_EQUIP_BD:
|
||||||
|
return "continue_equip_bd";
|
||||||
|
case STATE_CONTINUE_EQUIP:
|
||||||
|
return "continue_equip";
|
||||||
|
case STATE_WAITING_FOR_BUMPER_RELEASE:
|
||||||
|
return "waiting_for_bumper_release";
|
||||||
|
case STATE_EQUIP_SPRING:
|
||||||
|
return "state_equip_spring";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "unknown";
|
return "unknown";
|
||||||
|
@ -182,6 +209,7 @@ function MyController(hand) {
|
||||||
this.pointer = null; // entity-id of line object
|
this.pointer = null; // entity-id of line object
|
||||||
this.triggerValue = 0; // rolling average of trigger value
|
this.triggerValue = 0; // rolling average of trigger value
|
||||||
this.rawTriggerValue = 0;
|
this.rawTriggerValue = 0;
|
||||||
|
this.rawBumperValue = 0;
|
||||||
|
|
||||||
this.offsetPosition = { x: 0.0, y: 0.0, z: 0.0 };
|
this.offsetPosition = { x: 0.0, y: 0.0, z: 0.0 };
|
||||||
this.offsetRotation = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 };
|
this.offsetRotation = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 };
|
||||||
|
@ -200,6 +228,9 @@ function MyController(hand) {
|
||||||
case STATE_SEARCHING:
|
case STATE_SEARCHING:
|
||||||
this.search();
|
this.search();
|
||||||
break;
|
break;
|
||||||
|
case STATE_EQUIP_SEARCHING:
|
||||||
|
this.search();
|
||||||
|
break;
|
||||||
case STATE_DISTANCE_HOLDING:
|
case STATE_DISTANCE_HOLDING:
|
||||||
this.distanceHolding();
|
this.distanceHolding();
|
||||||
break;
|
break;
|
||||||
|
@ -207,9 +238,18 @@ function MyController(hand) {
|
||||||
this.continueDistanceHolding();
|
this.continueDistanceHolding();
|
||||||
break;
|
break;
|
||||||
case STATE_NEAR_GRABBING:
|
case STATE_NEAR_GRABBING:
|
||||||
|
case STATE_EQUIP:
|
||||||
this.nearGrabbing();
|
this.nearGrabbing();
|
||||||
break;
|
break;
|
||||||
|
case STATE_WAITING_FOR_BUMPER_RELEASE:
|
||||||
|
this.waitingForBumperRelease();
|
||||||
|
break;
|
||||||
|
case STATE_EQUIP_SPRING:
|
||||||
|
this.pullTowardEquipPosition()
|
||||||
|
break;
|
||||||
case STATE_CONTINUE_NEAR_GRABBING:
|
case STATE_CONTINUE_NEAR_GRABBING:
|
||||||
|
case STATE_CONTINUE_EQUIP_BD:
|
||||||
|
case STATE_CONTINUE_EQUIP:
|
||||||
this.continueNearGrabbing();
|
this.continueNearGrabbing();
|
||||||
break;
|
break;
|
||||||
case STATE_NEAR_TRIGGER:
|
case STATE_NEAR_TRIGGER:
|
||||||
|
@ -281,10 +321,15 @@ function MyController(hand) {
|
||||||
this.pointer = null;
|
this.pointer = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.eitherTrigger = function (value) {
|
this.triggerPress = function (value) {
|
||||||
_this.rawTriggerValue = value;
|
_this.rawTriggerValue = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.bumperPress = function (value) {
|
||||||
|
_this.rawBumperValue = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
this.updateSmoothedTrigger = function () {
|
this.updateSmoothedTrigger = function () {
|
||||||
var triggerValue = this.rawTriggerValue;
|
var triggerValue = this.rawTriggerValue;
|
||||||
// smooth out trigger value
|
// smooth out trigger value
|
||||||
|
@ -305,23 +350,37 @@ function MyController(hand) {
|
||||||
return triggerValue > TRIGGER_ON_VALUE;
|
return triggerValue > TRIGGER_ON_VALUE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.bumperSqueezed = function() {
|
||||||
|
return _this.rawBumperValue > BUMPER_ON_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bumperReleased = function() {
|
||||||
|
return _this.rawBumperValue < BUMPER_ON_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.off = function() {
|
this.off = function() {
|
||||||
if (this.triggerSmoothedSqueezed()) {
|
if (this.triggerSmoothedSqueezed()) {
|
||||||
this.lastPickTime = 0;
|
this.lastPickTime = 0;
|
||||||
this.setState(STATE_SEARCHING);
|
this.setState(STATE_SEARCHING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.bumperSqueezed()) {
|
||||||
|
this.lastPickTime = 0;
|
||||||
|
this.setState(STATE_EQUIP_SEARCHING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.search = function() {
|
this.search = function() {
|
||||||
this.grabbedEntity = null;
|
this.grabbedEntity = null;
|
||||||
|
|
||||||
//if this hand is the one that's disabled, we don't want to search for anything at all
|
// if this hand is the one that's disabled, we don't want to search for anything at all
|
||||||
if (this.hand === disabledHand) {
|
if (this.hand === disabledHand) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) {
|
||||||
this.setState(STATE_RELEASE);
|
this.setState(STATE_RELEASE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -334,8 +393,6 @@ function MyController(hand) {
|
||||||
length: PICK_MAX_DISTANCE
|
length: PICK_MAX_DISTANCE
|
||||||
};
|
};
|
||||||
|
|
||||||
this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
|
||||||
|
|
||||||
// don't pick 60x per second.
|
// don't pick 60x per second.
|
||||||
var pickRays = [];
|
var pickRays = [];
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
@ -398,7 +455,15 @@ function MyController(hand) {
|
||||||
return;
|
return;
|
||||||
} else if (!intersection.properties.locked) {
|
} else if (!intersection.properties.locked) {
|
||||||
this.grabbedEntity = intersection.entityID;
|
this.grabbedEntity = intersection.entityID;
|
||||||
this.setState(STATE_NEAR_GRABBING);
|
if (this.state == STATE_SEARCHING) {
|
||||||
|
this.setState(STATE_NEAR_GRABBING);
|
||||||
|
} else { // equipping
|
||||||
|
if (typeof grabbableData.spatialKey !== 'undefined') {
|
||||||
|
this.setState(STATE_EQUIP_SPRING);
|
||||||
|
} else {
|
||||||
|
this.setState(STATE_EQUIP);
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (! entityIsGrabbedByOther(intersection.entityID)) {
|
} else if (! entityIsGrabbedByOther(intersection.entityID)) {
|
||||||
|
@ -407,8 +472,14 @@ function MyController(hand) {
|
||||||
&& !intersection.properties.locked) {
|
&& !intersection.properties.locked) {
|
||||||
// the hand is far from the intersected object. go into distance-holding mode
|
// the hand is far from the intersected object. go into distance-holding mode
|
||||||
this.grabbedEntity = intersection.entityID;
|
this.grabbedEntity = intersection.entityID;
|
||||||
this.setState(STATE_DISTANCE_HOLDING);
|
if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) {
|
||||||
return;
|
// if a distance pick in equip mode hits something with a spatialKey, equip it
|
||||||
|
this.setState(STATE_EQUIP_SPRING);
|
||||||
|
return;
|
||||||
|
} else if (this.state == STATE_SEARCHING) {
|
||||||
|
this.setState(STATE_DISTANCE_HOLDING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else if (grabbableData.wantsTrigger) {
|
} else if (grabbableData.wantsTrigger) {
|
||||||
this.grabbedEntity = intersection.entityID;
|
this.grabbedEntity = intersection.entityID;
|
||||||
this.setState(STATE_FAR_TRIGGER);
|
this.setState(STATE_FAR_TRIGGER);
|
||||||
|
@ -434,6 +505,7 @@ function MyController(hand) {
|
||||||
var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS);
|
var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS);
|
||||||
var minDistance = PICK_MAX_DISTANCE;
|
var minDistance = PICK_MAX_DISTANCE;
|
||||||
var i, props, distance, grabbableData;
|
var i, props, distance, grabbableData;
|
||||||
|
this.grabbedEntity = null;
|
||||||
for (i = 0; i < nearbyEntities.length; i++) {
|
for (i = 0; i < nearbyEntities.length; i++) {
|
||||||
var grabbableDataForCandidate =
|
var grabbableDataForCandidate =
|
||||||
getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA);
|
getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA);
|
||||||
|
@ -483,16 +555,17 @@ function MyController(hand) {
|
||||||
grabbableData = grabbableDataForCandidate;
|
grabbableData = grabbableDataForCandidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.grabbedEntity === null) {
|
if (this.grabbedEntity !== null) {
|
||||||
return;
|
if (grabbableData.wantsTrigger) {
|
||||||
}
|
this.setState(STATE_NEAR_TRIGGER);
|
||||||
if (grabbableData.wantsTrigger) {
|
return;
|
||||||
this.setState(STATE_NEAR_TRIGGER);
|
} else if (!props.locked && props.collisionsWillMove) {
|
||||||
return;
|
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP)
|
||||||
} else if (!props.locked && props.collisionsWillMove) {
|
return;
|
||||||
this.setState(STATE_NEAR_GRABBING);
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.distanceHolding = function() {
|
this.distanceHolding = function() {
|
||||||
|
@ -551,6 +624,16 @@ function MyController(hand) {
|
||||||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||||
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
|
var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
|
||||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||||
|
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||||
|
|
||||||
|
if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() &&
|
||||||
|
typeof grabbableData.spatialKey !== 'undefined') {
|
||||||
|
var saveGrabbedID = this.grabbedEntity;
|
||||||
|
this.release();
|
||||||
|
this.setState(STATE_EQUIP);
|
||||||
|
this.grabbedEntity = saveGrabbedID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
|
this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
|
||||||
|
|
||||||
|
@ -634,13 +717,12 @@ function MyController(hand) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) {
|
||||||
this.setState(STATE_RELEASE);
|
this.setState(STATE_RELEASE);
|
||||||
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
|
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.lineOff();
|
this.lineOff();
|
||||||
|
|
||||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||||
|
@ -656,7 +738,8 @@ function MyController(hand) {
|
||||||
|
|
||||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||||
|
|
||||||
if (grabbableData.spatialKey) {
|
if (this.state != STATE_NEAR_GRABBING && grabbableData.spatialKey) {
|
||||||
|
// if an object is "equipped" and has a spatialKey, use it.
|
||||||
if (grabbableData.spatialKey.relativePosition) {
|
if (grabbableData.spatialKey.relativePosition) {
|
||||||
this.offsetPosition = grabbableData.spatialKey.relativePosition;
|
this.offsetPosition = grabbableData.spatialKey.relativePosition;
|
||||||
}
|
}
|
||||||
|
@ -686,7 +769,13 @@ function MyController(hand) {
|
||||||
this.actionID = null;
|
this.actionID = null;
|
||||||
} else {
|
} else {
|
||||||
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC);
|
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC);
|
||||||
this.setState(STATE_CONTINUE_NEAR_GRABBING);
|
if (this.state == STATE_NEAR_GRABBING) {
|
||||||
|
this.setState(STATE_CONTINUE_NEAR_GRABBING);
|
||||||
|
} else {
|
||||||
|
// equipping
|
||||||
|
this.setState(STATE_CONTINUE_EQUIP_BD);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.hand === RIGHT_HAND) {
|
if (this.hand === RIGHT_HAND) {
|
||||||
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
|
Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
|
||||||
} else {
|
} else {
|
||||||
|
@ -696,17 +785,30 @@ function MyController(hand) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;;
|
this.currentHandControllerTipPosition =
|
||||||
|
(this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;
|
||||||
|
|
||||||
this.currentObjectTime = Date.now();
|
this.currentObjectTime = Date.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.continueNearGrabbing = function() {
|
this.continueNearGrabbing = function() {
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.triggerSmoothedReleased()) {
|
||||||
this.setState(STATE_RELEASE);
|
this.setState(STATE_RELEASE);
|
||||||
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
|
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) {
|
||||||
|
this.setState(STATE_CONTINUE_EQUIP);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) {
|
||||||
|
this.setState(STATE_WAITING_FOR_BUMPER_RELEASE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) {
|
||||||
|
this.setState(STATE_CONTINUE_EQUIP_BD);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Keep track of the fingertip velocity to impart when we release the object.
|
// Keep track of the fingertip velocity to impart when we release the object.
|
||||||
// Note that the idea of using a constant 'tip' velocity regardless of the
|
// Note that the idea of using a constant 'tip' velocity regardless of the
|
||||||
|
@ -740,6 +842,66 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.waitingForBumperRelease = function() {
|
||||||
|
if (this.bumperReleased()) {
|
||||||
|
this.setState(STATE_RELEASE);
|
||||||
|
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.pullTowardEquipPosition = function() {
|
||||||
|
this.lineOff();
|
||||||
|
|
||||||
|
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||||
|
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||||
|
|
||||||
|
// use a spring to pull the object to where it will be when equipped
|
||||||
|
var relativeRotation = { x: 0.0, y: 0.0, z: 0.0, w: 1.0 };
|
||||||
|
var relativePosition = { x: 0.0, y: 0.0, z: 0.0 };
|
||||||
|
if (grabbableData.spatialKey.relativePosition) {
|
||||||
|
relativePosition = grabbableData.spatialKey.relativePosition;
|
||||||
|
}
|
||||||
|
if (grabbableData.spatialKey.relativeRotation) {
|
||||||
|
relativeRotation = grabbableData.spatialKey.relativeRotation;
|
||||||
|
}
|
||||||
|
var handRotation = this.getHandRotation();
|
||||||
|
var handPosition = this.getHandPosition();
|
||||||
|
var targetRotation = Quat.multiply(handRotation, relativeRotation);
|
||||||
|
var offset = Vec3.multiplyQbyV(targetRotation, relativePosition);
|
||||||
|
var targetPosition = Vec3.sum(handPosition, offset);
|
||||||
|
|
||||||
|
if (typeof this.equipSpringID === 'undefined' ||
|
||||||
|
this.equipSpringID === null ||
|
||||||
|
this.equipSpringID === NULL_ACTION_ID) {
|
||||||
|
this.equipSpringID = Entities.addAction("spring", this.grabbedEntity, {
|
||||||
|
targetPosition: targetPosition,
|
||||||
|
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||||
|
targetRotation: targetRotation,
|
||||||
|
angularTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||||
|
ttl: ACTION_TTL
|
||||||
|
});
|
||||||
|
if (this.equipSpringID === NULL_ACTION_ID) {
|
||||||
|
this.equipSpringID = null;
|
||||||
|
this.setState(STATE_OFF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Entities.updateAction(this.grabbedEntity, this.equipSpringID, {
|
||||||
|
targetPosition: targetPosition,
|
||||||
|
linearTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||||
|
targetRotation: targetRotation,
|
||||||
|
angularTimeScale: EQUIP_SPRING_TIMEFRAME,
|
||||||
|
ttl: ACTION_TTL
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Vec3.distance(grabbedProperties.position, targetPosition) < EQUIP_SPRING_SHUTOFF_DISTANCE) {
|
||||||
|
Entities.deleteAction(this.grabbedEntity, this.equipSpringID);
|
||||||
|
this.equipSpringID = null;
|
||||||
|
this.setState(STATE_EQUIP);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.nearTrigger = function() {
|
this.nearTrigger = function() {
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.triggerSmoothedReleased()) {
|
||||||
this.setState(STATE_RELEASE);
|
this.setState(STATE_RELEASE);
|
||||||
|
@ -919,6 +1081,7 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
Entities.editEntity(entityID, whileHeldProperties);
|
Entities.editEntity(entityID, whileHeldProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
|
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
@ -948,8 +1111,12 @@ var leftController = new MyController(LEFT_HAND);
|
||||||
var MAPPING_NAME = "com.highfidelity.handControllerGrab";
|
var MAPPING_NAME = "com.highfidelity.handControllerGrab";
|
||||||
|
|
||||||
var mapping = Controller.newMapping(MAPPING_NAME);
|
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||||
mapping.from([Controller.Standard.RB, Controller.Standard.RT]).peek().to(rightController.eitherTrigger);
|
mapping.from([Controller.Standard.RT]).peek().to(rightController.triggerPress);
|
||||||
mapping.from([Controller.Standard.LB, Controller.Standard.LT]).peek().to(leftController.eitherTrigger);
|
mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress);
|
||||||
|
|
||||||
|
mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress);
|
||||||
|
mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress);
|
||||||
|
|
||||||
Controller.enableMapping(MAPPING_NAME);
|
Controller.enableMapping(MAPPING_NAME);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1617,6 +1617,11 @@ PropertiesTool = function(opts) {
|
||||||
pushCommandForSelections();
|
pushCommandForSelections();
|
||||||
selectionManager._update();
|
selectionManager._update();
|
||||||
}
|
}
|
||||||
|
} else if (data.action == "previewCamera") {
|
||||||
|
if (selectionManager.hasSelection()) {
|
||||||
|
Camera.mode = "entity";
|
||||||
|
Camera.cameraEntity = selectionManager.selections[0];
|
||||||
|
}
|
||||||
} else if (data.action == "rescaleDimensions") {
|
} else if (data.action == "rescaleDimensions") {
|
||||||
var multiplier = data.percentage / 100;
|
var multiplier = data.percentage / 100;
|
||||||
if (selectionManager.hasSelection()) {
|
if (selectionManager.hasSelection()) {
|
||||||
|
|
21
examples/entityScripts/createRecorder.js
Normal file
21
examples/entityScripts/createRecorder.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
var rotation = Quat.safeEulerAngles(Camera.getOrientation());
|
||||||
|
rotation = Quat.fromPitchYawRollDegrees(0, rotation.y, 0);
|
||||||
|
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(6, Quat.getFront(rotation)));
|
||||||
|
|
||||||
|
var recordAreaEntity = Entities.addEntity({
|
||||||
|
name: 'recorderEntity',
|
||||||
|
dimensions: {
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
z: 10
|
||||||
|
},
|
||||||
|
type: 'Box',
|
||||||
|
position: center,
|
||||||
|
color: {
|
||||||
|
red: 255,
|
||||||
|
green: 255,
|
||||||
|
blue: 255
|
||||||
|
},
|
||||||
|
visible: true,
|
||||||
|
script: "https://hifi-public.s3.amazonaws.com/sam/record/recordingEntityScript.js",
|
||||||
|
});
|
|
@ -13,9 +13,11 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||||
|
Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/utils.js");
|
||||||
|
|
||||||
(function() {
|
|
||||||
var insideRecorderArea = false;
|
var insideRecorderArea = false;
|
||||||
var enteredInTime = false;
|
var enteredInTime = false;
|
||||||
var isAvatarRecording = false;
|
var isAvatarRecording = false;
|
||||||
|
@ -25,51 +27,63 @@
|
||||||
_this = this;
|
_this = this;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
var isRecordingStarted = getEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false }).isRecordingStarted;
|
||||||
|
if (isRecordingStarted && !isAvatarRecording) {
|
||||||
|
_this.startRecording();
|
||||||
|
} else if ((!isRecordingStarted && isAvatarRecording) || (isAvatarRecording && !insideRecorderArea)) {
|
||||||
|
_this.stopRecording();
|
||||||
|
} else if (!isRecordingStarted && insideRecorderArea && !enteredInTime) {
|
||||||
|
//if an avatar enters the zone while a recording is started he will be able to participate to the next group recording
|
||||||
|
enteredInTime = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
recordingEntity.prototype = {
|
recordingEntity.prototype = {
|
||||||
update: function(){
|
|
||||||
var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData);
|
preload: function (entityID) {
|
||||||
var isRecordingStarted = userData.recordingKey.isRecordingStarted;
|
print("RECORDING ENTITY PRELOAD");
|
||||||
if(isRecordingStarted && !isAvatarRecording){
|
|
||||||
_this.startRecording();
|
|
||||||
}else if((!isRecordingStarted && isAvatarRecording) || (isAvatarRecording && !insideRecorderArea)){
|
|
||||||
_this.stopRecording();
|
|
||||||
}else if(!isRecordingStarted && insideRecorderArea && !enteredInTime){
|
|
||||||
//if an avatar enters the zone while a recording is started he will be able to participate to the next group recording
|
|
||||||
enteredInTime = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
preload: function(entityID) {
|
|
||||||
this.entityID = entityID;
|
this.entityID = entityID;
|
||||||
Script.update.connect(_this.update);
|
|
||||||
|
var entityProperties = Entities.getEntityProperties(_this.entityID);
|
||||||
|
if (!entityProperties.ignoreForCollisions) {
|
||||||
|
Entities.editEntity(_this.entityID, { ignoreForCollisions: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
//print(JSON.stringify(entityProperties));
|
||||||
|
var recordingKey = getEntityCustomData("recordingKey", _this.entityID, undefined);
|
||||||
|
if (recordingKey === undefined) {
|
||||||
|
setEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.update.connect(update);
|
||||||
},
|
},
|
||||||
enterEntity: function(entityID) {
|
enterEntity: function (entityID) {
|
||||||
print("entering in the recording area");
|
print("entering in the recording area");
|
||||||
insideRecorderArea = true;
|
insideRecorderArea = true;
|
||||||
var userData = JSON.parse(Entities.getEntityProperties(_this.entityID, ["userData"]).userData);
|
var isRecordingStarted = getEntityCustomData("recordingKey", _this.entityID, { isRecordingStarted: false }).isRecordingStarted;
|
||||||
var isRecordingStarted = userData.recordingKey.isRecordingStarted;
|
if (!isRecordingStarted) {
|
||||||
if(!isRecordingStarted){
|
|
||||||
//i'm in the recording area in time (before the event starts)
|
//i'm in the recording area in time (before the event starts)
|
||||||
enteredInTime = true;
|
enteredInTime = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leaveEntity: function(entityID) {
|
leaveEntity: function (entityID) {
|
||||||
print("leaving the recording area");
|
print("leaving the recording area");
|
||||||
insideRecorderArea = false;
|
insideRecorderArea = false;
|
||||||
enteredInTime = false;
|
enteredInTime = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
startRecording: function(entityID){
|
startRecording: function (entityID) {
|
||||||
if(enteredInTime && !isAvatarRecording){
|
if (enteredInTime && !isAvatarRecording) {
|
||||||
print("RECORDING STARTED");
|
print("RECORDING STARTED");
|
||||||
Recording.startRecording();
|
Recording.startRecording();
|
||||||
isAvatarRecording = true;
|
isAvatarRecording = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
stopRecording: function(entityID){
|
stopRecording: function (entityID) {
|
||||||
if(isAvatarRecording){
|
if (isAvatarRecording) {
|
||||||
print("RECORDING ENDED");
|
print("RECORDING ENDED");
|
||||||
Recording.stopRecording();
|
Recording.stopRecording();
|
||||||
Recording.loadLastRecording();
|
Recording.loadLastRecording();
|
||||||
|
@ -80,12 +94,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clean: function(entityID) {
|
unload: function (entityID) {
|
||||||
Script.update.disconnect(_this.update);
|
print("RECORDING ENTITY UNLOAD");
|
||||||
|
Script.update.disconnect(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return new recordingEntity();
|
return new recordingEntity();
|
||||||
});
|
});
|
|
@ -5,16 +5,15 @@
|
||||||
// Created by Alessandro Signa on 11/12/15.
|
// Created by Alessandro Signa on 11/12/15.
|
||||||
// Copyright 2015 High Fidelity, Inc.
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
// Run this script to spawn a box (recorder) and drive the start/end of the recording for anyone who is inside the box
|
// Run this script to find the recorder (created by crateRecorder.js) and drive the start/end of the recording for anyone who is inside the box
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
|
||||||
var PARAMS_SCRIPT_URL = Script.resolvePath('recordingEntityScript.js');
|
|
||||||
|
|
||||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||||
Script.include("../libraries/toolBars.js");
|
Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/toolBars.js");
|
||||||
Script.include("../libraries/utils.js");
|
Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/utils.js");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,35 +29,25 @@ var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 };
|
||||||
var toolBar = null;
|
var toolBar = null;
|
||||||
var recordIcon;
|
var recordIcon;
|
||||||
|
|
||||||
|
var isRecordingEntityFound = false;
|
||||||
|
|
||||||
var isRecording = false;
|
var isRecording = false;
|
||||||
|
|
||||||
var recordAreaEntity = Entities.addEntity({
|
var recordAreaEntity = null;
|
||||||
name: 'recorderEntity',
|
findRecorder();
|
||||||
dimensions: {
|
|
||||||
x: 2,
|
|
||||||
y: 1,
|
|
||||||
z: 2
|
|
||||||
},
|
|
||||||
type: 'Box',
|
|
||||||
position: center,
|
|
||||||
color: {
|
|
||||||
red: 255,
|
|
||||||
green: 255,
|
|
||||||
blue: 255
|
|
||||||
},
|
|
||||||
visible: true,
|
|
||||||
ignoreForCollisions: true,
|
|
||||||
script: PARAMS_SCRIPT_URL,
|
|
||||||
|
|
||||||
userData: JSON.stringify({
|
|
||||||
recordingKey: {
|
|
||||||
isRecordingStarted: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
|
function findRecorder() {
|
||||||
|
foundEntities = Entities.findEntities(MyAvatar.position, 50);
|
||||||
|
for (var i = 0; i < foundEntities.length; i++) {
|
||||||
|
var name = Entities.getEntityProperties(foundEntities[i], "name").name;
|
||||||
|
if (name === "recorderEntity") {
|
||||||
|
recordAreaEntity = foundEntities[i];
|
||||||
|
isRecordingEntityFound = true;
|
||||||
|
print("Found recorder Entity!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setupToolBar();
|
setupToolBar();
|
||||||
|
|
||||||
|
@ -70,7 +59,7 @@ function setupToolBar() {
|
||||||
Tool.IMAGE_HEIGHT /= 2;
|
Tool.IMAGE_HEIGHT /= 2;
|
||||||
Tool.IMAGE_WIDTH /= 2;
|
Tool.IMAGE_WIDTH /= 2;
|
||||||
|
|
||||||
toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL); //put the button in the up-left corner
|
toolBar = new ToolBar(0, 100, ToolBar.HORIZONTAL); //put the button in the up-left corner
|
||||||
|
|
||||||
toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF);
|
toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF);
|
||||||
|
|
||||||
|
@ -81,9 +70,8 @@ function setupToolBar() {
|
||||||
width: Tool.IMAGE_WIDTH,
|
width: Tool.IMAGE_WIDTH,
|
||||||
height: Tool.IMAGE_HEIGHT,
|
height: Tool.IMAGE_HEIGHT,
|
||||||
alpha: Recording.isPlaying() ? ALPHA_OFF : ALPHA_ON,
|
alpha: Recording.isPlaying() ? ALPHA_OFF : ALPHA_ON,
|
||||||
visible: true
|
visible: isRecordingEntityFound,
|
||||||
}, true, isRecording);
|
}, true, isRecording);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mousePressEvent(event) {
|
function mousePressEvent(event) {
|
||||||
|
@ -106,8 +94,6 @@ function mousePressEvent(event) {
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
toolBar.cleanup();
|
toolBar.cleanup();
|
||||||
Entities.callEntityMethod(recordAreaEntity, 'clean'); //have to call this before deleting to avoid the JSON warnings
|
|
||||||
Entities.deleteEntity(recordAreaEntity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
57
examples/example/securityCamera.js
Normal file
57
examples/example/securityCamera.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
//
|
||||||
|
// securityCamera.js
|
||||||
|
// examples/example
|
||||||
|
//
|
||||||
|
// Created by Thijs Wenker on November 4, 2015
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
const CAMERA_OFFSET = {x: 0, y: 4, z: -14};
|
||||||
|
const LOOKAT_START_OFFSET = {x: -10, y: 0, z: 14};
|
||||||
|
const LOOKAT_END_OFFSET = {x: 10, y: 0, z: 14};
|
||||||
|
const TINY_VALUE = 0.001;
|
||||||
|
|
||||||
|
var lookatTargets = [Vec3.sum(MyAvatar.position, LOOKAT_START_OFFSET), Vec3.sum(MyAvatar.position, LOOKAT_END_OFFSET)];
|
||||||
|
var currentTarget = 0;
|
||||||
|
|
||||||
|
var forward = true;
|
||||||
|
|
||||||
|
var oldCameraMode = Camera.mode;
|
||||||
|
|
||||||
|
var cameraLookAt = function(cameraPos, lookAtPos) {
|
||||||
|
var lookAtRaw = Quat.lookAt(cameraPos, lookAtPos, Vec3.UP);
|
||||||
|
lookAtRaw.w = -lookAtRaw.w;
|
||||||
|
return lookAtRaw;
|
||||||
|
};
|
||||||
|
|
||||||
|
cameraEntity = Entities.addEntity({
|
||||||
|
type: "Box",
|
||||||
|
visible: false,
|
||||||
|
position: Vec3.sum(MyAvatar.position, CAMERA_OFFSET)
|
||||||
|
});
|
||||||
|
|
||||||
|
Camera.mode = "entity";
|
||||||
|
Camera.cameraEntity = cameraEntity;
|
||||||
|
|
||||||
|
Script.update.connect(function(deltaTime) {
|
||||||
|
var cameraProperties = Entities.getEntityProperties(cameraEntity, ["position", "rotation"]);
|
||||||
|
var targetOrientation = cameraLookAt(cameraProperties.position, lookatTargets[currentTarget]);
|
||||||
|
if (Math.abs(targetOrientation.x - cameraProperties.rotation.x) < TINY_VALUE &&
|
||||||
|
Math.abs(targetOrientation.y - cameraProperties.rotation.y) < TINY_VALUE &&
|
||||||
|
Math.abs(targetOrientation.z - cameraProperties.rotation.z) < TINY_VALUE &&
|
||||||
|
Math.abs(targetOrientation.w - cameraProperties.rotation.w) < TINY_VALUE) {
|
||||||
|
|
||||||
|
currentTarget = (currentTarget + 1) % lookatTargets.length;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Entities.editEntity(cameraEntity, {rotation: Quat.mix(cameraProperties.rotation, targetOrientation, deltaTime / 3)});
|
||||||
|
});
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function() {
|
||||||
|
Entities.deleteEntity(cameraEntity);
|
||||||
|
Camera.mode = oldCameraMode;
|
||||||
|
Camera.cameraEntity = null;
|
||||||
|
});
|
|
@ -382,7 +382,7 @@
|
||||||
var elHyperlinkHref = document.getElementById("property-hyperlink-href");
|
var elHyperlinkHref = document.getElementById("property-hyperlink-href");
|
||||||
var elHyperlinkDescription = document.getElementById("property-hyperlink-description");
|
var elHyperlinkDescription = document.getElementById("property-hyperlink-description");
|
||||||
|
|
||||||
|
var elPreviewCameraButton = document.getElementById("preview-camera-button");
|
||||||
|
|
||||||
if (window.EventBridge !== undefined) {
|
if (window.EventBridge !== undefined) {
|
||||||
EventBridge.scriptEventReceived.connect(function(data) {
|
EventBridge.scriptEventReceived.connect(function(data) {
|
||||||
|
@ -931,6 +931,12 @@
|
||||||
action: "centerAtmosphereToZone",
|
action: "centerAtmosphereToZone",
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
elPreviewCameraButton.addEventListener("click", function() {
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
|
type: "action",
|
||||||
|
action: "previewCamera"
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
window.onblur = function() {
|
window.onblur = function() {
|
||||||
// Fake a change event
|
// Fake a change event
|
||||||
|
@ -1032,7 +1038,7 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<label>Spacial Properites</label>
|
<label>Spacial Properties</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="property">
|
<div class="property">
|
||||||
|
@ -1044,6 +1050,7 @@
|
||||||
<div>
|
<div>
|
||||||
<input type="button" id="move-selection-to-grid" value="Selection to Grid">
|
<input type="button" id="move-selection-to-grid" value="Selection to Grid">
|
||||||
<input type="button" id="move-all-to-grid" value="All to Grid">
|
<input type="button" id="move-all-to-grid" value="All to Grid">
|
||||||
|
<input type="button" id="preview-camera-button" value="Preview Camera">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -204,7 +204,7 @@ EntityPropertyDialogBox = (function () {
|
||||||
array.push({ label: "Collisions Will Move:", type: "checkbox", value: properties.collisionsWillMove });
|
array.push({ label: "Collisions Will Move:", type: "checkbox", value: properties.collisionsWillMove });
|
||||||
index++;
|
index++;
|
||||||
array.push({ label: "Collision Sound URL:", value: properties.collisionSoundURL });
|
array.push({ label: "Collision Sound URL:", value: properties.collisionSoundURL });
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) });
|
array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) });
|
||||||
index++;
|
index++;
|
||||||
|
@ -260,6 +260,10 @@ EntityPropertyDialogBox = (function () {
|
||||||
array.push({ label: "Cutoff (in degrees):", value: properties.cutoff });
|
array.push({ label: "Cutoff (in degrees):", value: properties.cutoff });
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
array.push({ label: "", type: "inlineButton", buttonLabel: "Preview Camera", name: "previewCamera" });
|
||||||
|
index++;
|
||||||
|
|
||||||
array.push({ button: "Cancel" });
|
array.push({ button: "Cancel" });
|
||||||
index++;
|
index++;
|
||||||
|
|
||||||
|
@ -268,6 +272,11 @@ EntityPropertyDialogBox = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
Window.inlineButtonClicked.connect(function (name) {
|
Window.inlineButtonClicked.connect(function (name) {
|
||||||
|
if (name == "previewCamera") {
|
||||||
|
Camera.mode = "entity";
|
||||||
|
Camera.cameraEntity = propertiesForEditedEntity.id;
|
||||||
|
}
|
||||||
|
|
||||||
if (name == "resetDimensions") {
|
if (name == "resetDimensions") {
|
||||||
Window.reloadNonBlockingForm([
|
Window.reloadNonBlockingForm([
|
||||||
{ value: propertiesForEditedEntity.naturalDimensions.x.toFixed(decimals), oldIndex: dimensionX },
|
{ value: propertiesForEditedEntity.naturalDimensions.x.toFixed(decimals), oldIndex: dimensionX },
|
||||||
|
|
|
@ -11,6 +11,12 @@ vec3toStr = function(v, digits) {
|
||||||
return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }";
|
return "{ " + v.x.toFixed(digits) + ", " + v.y.toFixed(digits) + ", " + v.z.toFixed(digits)+ " }";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quatToStr = function(q, digits) {
|
||||||
|
if (!digits) { digits = 3; }
|
||||||
|
return "{ " + q.w.toFixed(digits) + ", " + q.x.toFixed(digits) + ", " +
|
||||||
|
q.y.toFixed(digits) + ", " + q.z.toFixed(digits)+ " }";
|
||||||
|
}
|
||||||
|
|
||||||
vec3equal = function(v0, v1) {
|
vec3equal = function(v0, v1) {
|
||||||
return (v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z);
|
return (v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z);
|
||||||
}
|
}
|
||||||
|
@ -51,7 +57,7 @@ addLine = function(origin, vector, color) {
|
||||||
// FIXME fetch from a subkey of user data to support non-destructive modifications
|
// FIXME fetch from a subkey of user data to support non-destructive modifications
|
||||||
setEntityUserData = function(id, data) {
|
setEntityUserData = function(id, data) {
|
||||||
var json = JSON.stringify(data)
|
var json = JSON.stringify(data)
|
||||||
Entities.editEntity(id, { userData: json });
|
Entities.editEntity(id, { userData: json });
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME do non-destructive modification of the existing user data
|
// FIXME do non-destructive modification of the existing user data
|
||||||
|
@ -60,7 +66,7 @@ getEntityUserData = function(id) {
|
||||||
var properties = Entities.getEntityProperties(id, "userData");
|
var properties = Entities.getEntityProperties(id, "userData");
|
||||||
if (properties.userData) {
|
if (properties.userData) {
|
||||||
try {
|
try {
|
||||||
results = JSON.parse(properties.userData);
|
results = JSON.parse(properties.userData);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
logDebug(err);
|
logDebug(err);
|
||||||
logDebug(properties.userData);
|
logDebug(properties.userData);
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
Script.include("../../libraries/utils.js");
|
Script.include("../../libraries/utils.js");
|
||||||
|
|
||||||
var BUBBLE_MODEL = "http://hifi-public.s3.amazonaws.com/models/bubblewand/bubble.fbx";
|
var BUBBLE_MODEL = "http://hifi-content.s3.amazonaws.com/james/bubblewand/bubble.fbx";
|
||||||
|
|
||||||
var BUBBLE_INITIAL_DIMENSIONS = {
|
var BUBBLE_INITIAL_DIMENSIONS = {
|
||||||
x: 0.01,
|
x: 0.01,
|
||||||
|
|
|
@ -88,6 +88,7 @@
|
||||||
#include <RenderDeferredTask.h>
|
#include <RenderDeferredTask.h>
|
||||||
#include <ResourceCache.h>
|
#include <ResourceCache.h>
|
||||||
#include <SceneScriptingInterface.h>
|
#include <SceneScriptingInterface.h>
|
||||||
|
#include <RecordingScriptingInterface.h>
|
||||||
#include <ScriptCache.h>
|
#include <ScriptCache.h>
|
||||||
#include <SoundCache.h>
|
#include <SoundCache.h>
|
||||||
#include <TextureCache.h>
|
#include <TextureCache.h>
|
||||||
|
@ -128,7 +129,6 @@
|
||||||
#include "scripting/LocationScriptingInterface.h"
|
#include "scripting/LocationScriptingInterface.h"
|
||||||
#include "scripting/MenuScriptingInterface.h"
|
#include "scripting/MenuScriptingInterface.h"
|
||||||
#include "scripting/SettingsScriptingInterface.h"
|
#include "scripting/SettingsScriptingInterface.h"
|
||||||
#include "scripting/RecordingScriptingInterface.h"
|
|
||||||
#include "scripting/WebWindowClass.h"
|
#include "scripting/WebWindowClass.h"
|
||||||
#include "scripting/WindowScriptingInterface.h"
|
#include "scripting/WindowScriptingInterface.h"
|
||||||
#include "scripting/ControllerScriptingInterface.h"
|
#include "scripting/ControllerScriptingInterface.h"
|
||||||
|
@ -745,6 +745,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog);
|
connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog);
|
||||||
applicationUpdater->checkForUpdate();
|
applicationUpdater->checkForUpdate();
|
||||||
|
|
||||||
|
// Assign MyAvatar to th eRecording Singleton
|
||||||
|
DependencyManager::get<RecordingScriptingInterface>()->setControlledAvatar(getMyAvatar());
|
||||||
|
|
||||||
|
|
||||||
// Now that menu is initalized we can sync myAvatar with it's state.
|
// Now that menu is initalized we can sync myAvatar with it's state.
|
||||||
getMyAvatar()->updateMotionBehaviorFromMenu();
|
getMyAvatar()->updateMotionBehaviorFromMenu();
|
||||||
|
|
||||||
|
@ -839,6 +843,7 @@ void Application::cleanupBeforeQuit() {
|
||||||
#ifdef HAVE_IVIEWHMD
|
#ifdef HAVE_IVIEWHMD
|
||||||
DependencyManager::get<EyeTracker>()->setEnabled(false, true);
|
DependencyManager::get<EyeTracker>()->setEnabled(false, true);
|
||||||
#endif
|
#endif
|
||||||
|
DependencyManager::get<RecordingScriptingInterface>()->setControlledAvatar(nullptr);
|
||||||
|
|
||||||
AnimDebugDraw::getInstance().shutdown();
|
AnimDebugDraw::getInstance().shutdown();
|
||||||
|
|
||||||
|
@ -1005,10 +1010,6 @@ void Application::initializeGL() {
|
||||||
connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);
|
connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);
|
||||||
_entityEditSender.initialize(_enableProcessOctreeThread);
|
_entityEditSender.initialize(_enableProcessOctreeThread);
|
||||||
|
|
||||||
// call our timer function every second
|
|
||||||
connect(&pingTimer, &QTimer::timeout, this, &Application::ping);
|
|
||||||
pingTimer.start(1000);
|
|
||||||
|
|
||||||
_idleLoopStdev.reset();
|
_idleLoopStdev.reset();
|
||||||
|
|
||||||
// update before the first render
|
// update before the first render
|
||||||
|
@ -1232,6 +1233,19 @@ void Application::paintGL() {
|
||||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
||||||
}
|
}
|
||||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||||
|
} else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) {
|
||||||
|
EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer();
|
||||||
|
if (cameraEntity != nullptr) {
|
||||||
|
if (isHMDMode()) {
|
||||||
|
glm::quat hmdRotation = extractRotation(myAvatar->getHMDSensorMatrix());
|
||||||
|
_myCamera.setRotation(cameraEntity->getRotation() * hmdRotation);
|
||||||
|
glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix());
|
||||||
|
_myCamera.setPosition(cameraEntity->getPosition() + (hmdRotation * hmdOffset));
|
||||||
|
} else {
|
||||||
|
_myCamera.setRotation(cameraEntity->getRotation());
|
||||||
|
_myCamera.setPosition(cameraEntity->getPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Update camera position
|
// Update camera position
|
||||||
if (!isHMDMode()) {
|
if (!isHMDMode()) {
|
||||||
|
@ -2136,13 +2150,6 @@ bool Application::acceptSnapshot(const QString& urlString) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every second, send a ping, if menu item is checked.
|
|
||||||
void Application::ping() {
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
|
||||||
DependencyManager::get<NodeList>()->sendPingPackets();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::idle(uint64_t now) {
|
void Application::idle(uint64_t now) {
|
||||||
if (_aboutToQuit) {
|
if (_aboutToQuit) {
|
||||||
return; // bail early, nothing to do here.
|
return; // bail early, nothing to do here.
|
||||||
|
@ -2682,8 +2689,8 @@ void Application::cycleCamera() {
|
||||||
menu->setIsOptionChecked(MenuOption::ThirdPerson, false);
|
menu->setIsOptionChecked(MenuOption::ThirdPerson, false);
|
||||||
menu->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
menu->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||||
|
|
||||||
} else if (menu->isOptionChecked(MenuOption::IndependentMode)) {
|
} else if (menu->isOptionChecked(MenuOption::IndependentMode) || menu->isOptionChecked(MenuOption::CameraEntityMode)) {
|
||||||
// do nothing if in independe mode
|
// do nothing if in independent or camera entity modes
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cameraMenuChanged(); // handle the menu change
|
cameraMenuChanged(); // handle the menu change
|
||||||
|
@ -2710,6 +2717,10 @@ void Application::cameraMenuChanged() {
|
||||||
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
|
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
|
||||||
_myCamera.setMode(CAMERA_MODE_INDEPENDENT);
|
_myCamera.setMode(CAMERA_MODE_INDEPENDENT);
|
||||||
}
|
}
|
||||||
|
} else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) {
|
||||||
|
if (_myCamera.getMode() != CAMERA_MODE_ENTITY) {
|
||||||
|
_myCamera.setMode(CAMERA_MODE_ENTITY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3995,7 +4006,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
||||||
RayToOverlayIntersectionResultFromScriptValue);
|
RayToOverlayIntersectionResultFromScriptValue);
|
||||||
|
|
||||||
scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
|
scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
|
||||||
scriptEngine->registerGlobalObject("Recording", DependencyManager::get<RecordingScriptingInterface>().data());
|
|
||||||
|
|
||||||
scriptEngine->registerGlobalObject("Window", DependencyManager::get<WindowScriptingInterface>().data());
|
scriptEngine->registerGlobalObject("Window", DependencyManager::get<WindowScriptingInterface>().data());
|
||||||
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
||||||
|
|
|
@ -306,7 +306,6 @@ public slots:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void clearDomainOctreeDetails();
|
void clearDomainOctreeDetails();
|
||||||
void ping();
|
|
||||||
void idle(uint64_t now);
|
void idle(uint64_t now);
|
||||||
void aboutToQuit();
|
void aboutToQuit();
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ CameraMode stringToMode(const QString& mode) {
|
||||||
return CAMERA_MODE_MIRROR;
|
return CAMERA_MODE_MIRROR;
|
||||||
} else if (mode == "independent") {
|
} else if (mode == "independent") {
|
||||||
return CAMERA_MODE_INDEPENDENT;
|
return CAMERA_MODE_INDEPENDENT;
|
||||||
|
} else if (mode == "entity") {
|
||||||
|
return CAMERA_MODE_ENTITY;
|
||||||
}
|
}
|
||||||
return CAMERA_MODE_NULL;
|
return CAMERA_MODE_NULL;
|
||||||
}
|
}
|
||||||
|
@ -41,6 +43,8 @@ QString modeToString(CameraMode mode) {
|
||||||
return "mirror";
|
return "mirror";
|
||||||
} else if (mode == CAMERA_MODE_INDEPENDENT) {
|
} else if (mode == CAMERA_MODE_INDEPENDENT) {
|
||||||
return "independent";
|
return "independent";
|
||||||
|
} else if (mode == CAMERA_MODE_ENTITY) {
|
||||||
|
return "entity";
|
||||||
}
|
}
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
@ -94,6 +98,17 @@ void Camera::setMode(CameraMode mode) {
|
||||||
emit modeUpdated(modeToString(mode));
|
emit modeUpdated(modeToString(mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUuid Camera::getCameraEntity() const {
|
||||||
|
if (_cameraEntity != nullptr) {
|
||||||
|
return _cameraEntity->getID();
|
||||||
|
}
|
||||||
|
return QUuid();
|
||||||
|
};
|
||||||
|
|
||||||
|
void Camera::setCameraEntity(QUuid entityID) {
|
||||||
|
_cameraEntity = qApp->getEntities()->getTree()->findEntityByID(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
void Camera::setProjection(const glm::mat4& projection) {
|
void Camera::setProjection(const glm::mat4& projection) {
|
||||||
_projection = projection;
|
_projection = projection;
|
||||||
}
|
}
|
||||||
|
@ -118,6 +133,9 @@ void Camera::setModeString(const QString& mode) {
|
||||||
case CAMERA_MODE_INDEPENDENT:
|
case CAMERA_MODE_INDEPENDENT:
|
||||||
Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true);
|
Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true);
|
||||||
break;
|
break;
|
||||||
|
case CAMERA_MODE_ENTITY:
|
||||||
|
Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ enum CameraMode
|
||||||
CAMERA_MODE_FIRST_PERSON,
|
CAMERA_MODE_FIRST_PERSON,
|
||||||
CAMERA_MODE_MIRROR,
|
CAMERA_MODE_MIRROR,
|
||||||
CAMERA_MODE_INDEPENDENT,
|
CAMERA_MODE_INDEPENDENT,
|
||||||
|
CAMERA_MODE_ENTITY,
|
||||||
NUM_CAMERA_MODES
|
NUM_CAMERA_MODES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,6 +37,7 @@ class Camera : public QObject {
|
||||||
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
|
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
|
||||||
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
|
Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation)
|
||||||
Q_PROPERTY(QString mode READ getModeString WRITE setModeString)
|
Q_PROPERTY(QString mode READ getModeString WRITE setModeString)
|
||||||
|
Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity)
|
||||||
public:
|
public:
|
||||||
Camera();
|
Camera();
|
||||||
|
|
||||||
|
@ -49,6 +51,8 @@ public:
|
||||||
void loadViewFrustum(ViewFrustum& frustum) const;
|
void loadViewFrustum(ViewFrustum& frustum) const;
|
||||||
ViewFrustum toViewFrustum() const;
|
ViewFrustum toViewFrustum() const;
|
||||||
|
|
||||||
|
EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QString getModeString() const;
|
QString getModeString() const;
|
||||||
void setModeString(const QString& mode);
|
void setModeString(const QString& mode);
|
||||||
|
@ -68,6 +72,9 @@ public slots:
|
||||||
const glm::mat4& getProjection() const { return _projection; }
|
const glm::mat4& getProjection() const { return _projection; }
|
||||||
void setProjection(const glm::mat4& projection);
|
void setProjection(const glm::mat4& projection);
|
||||||
|
|
||||||
|
QUuid getCameraEntity() const;
|
||||||
|
void setCameraEntity(QUuid entityID);
|
||||||
|
|
||||||
PickRay computePickRay(float x, float y);
|
PickRay computePickRay(float x, float y);
|
||||||
|
|
||||||
// These only work on independent cameras
|
// These only work on independent cameras
|
||||||
|
@ -97,6 +104,7 @@ private:
|
||||||
glm::quat _rotation;
|
glm::quat _rotation;
|
||||||
bool _isKeepLookingAt{ false };
|
bool _isKeepLookingAt{ false };
|
||||||
glm::vec3 _lookingAt;
|
glm::vec3 _lookingAt;
|
||||||
|
EntityItemPointer _cameraEntity;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Camera_h
|
#endif // hifi_Camera_h
|
||||||
|
|
|
@ -268,6 +268,9 @@ Menu::Menu() {
|
||||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||||
MenuOption::IndependentMode, 0,
|
MenuOption::IndependentMode, 0,
|
||||||
false, qApp, SLOT(cameraMenuChanged())));
|
false, qApp, SLOT(cameraMenuChanged())));
|
||||||
|
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||||
|
MenuOption::CameraEntityMode, 0,
|
||||||
|
false, qApp, SLOT(cameraMenuChanged())));
|
||||||
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(cameraModeMenu,
|
||||||
MenuOption::FullscreenMirror, 0, // QML Qt::Key_H,
|
MenuOption::FullscreenMirror, 0, // QML Qt::Key_H,
|
||||||
false, qApp, SLOT(cameraMenuChanged())));
|
false, qApp, SLOT(cameraMenuChanged())));
|
||||||
|
@ -502,7 +505,6 @@ Menu::Menu() {
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false);
|
||||||
|
|
||||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true);
|
|
||||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
|
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer);
|
||||||
addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests()));
|
addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests()));
|
||||||
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
|
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
|
||||||
|
|
|
@ -155,6 +155,7 @@ namespace MenuOption {
|
||||||
const QString Bookmarks = "Bookmarks";
|
const QString Bookmarks = "Bookmarks";
|
||||||
const QString CachesSize = "RAM Caches Size";
|
const QString CachesSize = "RAM Caches Size";
|
||||||
const QString CalibrateCamera = "Calibrate Camera";
|
const QString CalibrateCamera = "Calibrate Camera";
|
||||||
|
const QString CameraEntityMode = "Entity Mode";
|
||||||
const QString CenterPlayerInView = "Center Player In View";
|
const QString CenterPlayerInView = "Center Player In View";
|
||||||
const QString Chat = "Chat...";
|
const QString Chat = "Chat...";
|
||||||
const QString Collisions = "Collisions";
|
const QString Collisions = "Collisions";
|
||||||
|
@ -280,7 +281,6 @@ namespace MenuOption {
|
||||||
const QString Stats = "Stats";
|
const QString Stats = "Stats";
|
||||||
const QString StopAllScripts = "Stop All Scripts";
|
const QString StopAllScripts = "Stop All Scripts";
|
||||||
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
||||||
const QString TestPing = "Test Ping";
|
|
||||||
const QString ThirdPerson = "Third Person";
|
const QString ThirdPerson = "Third Person";
|
||||||
const QString ThreePointCalibration = "3 Point Calibration";
|
const QString ThreePointCalibration = "3 Point Calibration";
|
||||||
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp
|
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "QVariantGLM.h"
|
#include "QVariantGLM.h"
|
||||||
#include "avatar/MyAvatar.h"
|
|
||||||
#include "avatar/AvatarManager.h"
|
#include "avatar/AvatarManager.h"
|
||||||
|
|
||||||
#include "AvatarActionHold.h"
|
#include "AvatarActionHold.h"
|
||||||
|
@ -22,8 +21,7 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit
|
||||||
_relativePosition(glm::vec3(0.0f)),
|
_relativePosition(glm::vec3(0.0f)),
|
||||||
_relativeRotation(glm::quat()),
|
_relativeRotation(glm::quat()),
|
||||||
_hand("right"),
|
_hand("right"),
|
||||||
_holderID(QUuid())
|
_holderID(QUuid()) {
|
||||||
{
|
|
||||||
_type = ACTION_TYPE_HOLD;
|
_type = ACTION_TYPE_HOLD;
|
||||||
#if WANT_DEBUG
|
#if WANT_DEBUG
|
||||||
qDebug() << "AvatarActionHold::AvatarActionHold";
|
qDebug() << "AvatarActionHold::AvatarActionHold";
|
||||||
|
@ -36,13 +34,10 @@ AvatarActionHold::~AvatarActionHold() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) {
|
||||||
bool gotLock = false;
|
|
||||||
glm::quat rotation;
|
|
||||||
glm::vec3 position;
|
|
||||||
std::shared_ptr<Avatar> holdingAvatar = nullptr;
|
std::shared_ptr<Avatar> holdingAvatar = nullptr;
|
||||||
|
|
||||||
gotLock = withTryReadLock([&]{
|
withTryReadLock([&]{
|
||||||
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
|
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
|
||||||
AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID);
|
AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID);
|
||||||
holdingAvatar = std::static_pointer_cast<Avatar>(holdingAvatarData);
|
holdingAvatar = std::static_pointer_cast<Avatar>(holdingAvatarData);
|
||||||
|
@ -65,22 +60,53 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (holdingAvatar) {
|
return holdingAvatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
||||||
|
glm::quat rotation;
|
||||||
|
glm::vec3 position;
|
||||||
|
bool valid = false;
|
||||||
|
int holdCount = 0;
|
||||||
|
|
||||||
|
auto ownerEntity = _ownerEntity.lock();
|
||||||
|
if (!ownerEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QList<EntityActionPointer> holdActions = ownerEntity->getActionsOfType(ACTION_TYPE_HOLD);
|
||||||
|
foreach (EntityActionPointer action, holdActions) {
|
||||||
|
std::shared_ptr<AvatarActionHold> holdAction = std::static_pointer_cast<AvatarActionHold>(action);
|
||||||
|
glm::quat rotationForAction;
|
||||||
|
glm::vec3 positionForAction;
|
||||||
|
std::shared_ptr<Avatar> holdingAvatar = holdAction->getTarget(rotationForAction, positionForAction);
|
||||||
|
if (holdingAvatar) {
|
||||||
|
holdCount ++;
|
||||||
|
if (holdAction.get() == this) {
|
||||||
|
// only use the rotation for this action
|
||||||
|
valid = true;
|
||||||
|
rotation = rotationForAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
position += positionForAction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid && holdCount > 0) {
|
||||||
|
position /= holdCount;
|
||||||
|
|
||||||
|
bool gotLock = withTryWriteLock([&]{
|
||||||
|
_positionalTarget = position;
|
||||||
|
_rotationalTarget = rotation;
|
||||||
|
_positionalTargetSet = true;
|
||||||
|
_rotationalTargetSet = true;
|
||||||
|
_active = true;
|
||||||
|
});
|
||||||
if (gotLock) {
|
if (gotLock) {
|
||||||
gotLock = withTryWriteLock([&]{
|
if (_kinematic) {
|
||||||
_positionalTarget = position;
|
doKinematicUpdate(deltaTimeStep);
|
||||||
_rotationalTarget = rotation;
|
} else {
|
||||||
_positionalTargetSet = true;
|
activateBody();
|
||||||
_rotationalTargetSet = true;
|
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
||||||
_active = true;
|
|
||||||
});
|
|
||||||
if (gotLock) {
|
|
||||||
if (_kinematic) {
|
|
||||||
doKinematicUpdate(deltaTimeStep);
|
|
||||||
} else {
|
|
||||||
activateBody();
|
|
||||||
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +135,8 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) {
|
||||||
if (_previousSet) {
|
if (_previousSet) {
|
||||||
// smooth velocity over 2 frames
|
// smooth velocity over 2 frames
|
||||||
glm::vec3 positionalDelta = _positionalTarget - _previousPositionalTarget;
|
glm::vec3 positionalDelta = _positionalTarget - _previousPositionalTarget;
|
||||||
glm::vec3 positionalVelocity = (positionalDelta + _previousPositionalDelta) / (deltaTimeStep + _previousDeltaTimeStep);
|
glm::vec3 positionalVelocity =
|
||||||
|
(positionalDelta + _previousPositionalDelta) / (deltaTimeStep + _previousDeltaTimeStep);
|
||||||
rigidBody->setLinearVelocity(glmToBullet(positionalVelocity));
|
rigidBody->setLinearVelocity(glmToBullet(positionalVelocity));
|
||||||
_previousPositionalDelta = positionalDelta;
|
_previousPositionalDelta = positionalDelta;
|
||||||
_previousDeltaTimeStep = deltaTimeStep;
|
_previousDeltaTimeStep = deltaTimeStep;
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
#include <EntityItem.h>
|
#include <EntityItem.h>
|
||||||
#include <ObjectActionSpring.h>
|
#include <ObjectActionSpring.h>
|
||||||
|
|
||||||
|
#include "avatar/MyAvatar.h"
|
||||||
|
|
||||||
|
|
||||||
class AvatarActionHold : public ObjectActionSpring {
|
class AvatarActionHold : public ObjectActionSpring {
|
||||||
public:
|
public:
|
||||||
AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity);
|
AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity);
|
||||||
|
@ -32,6 +35,8 @@ public:
|
||||||
|
|
||||||
virtual bool shouldSuppressLocationEdits() { return _active && !_ownerEntity.expired(); }
|
virtual bool shouldSuppressLocationEdits() { return _active && !_ownerEntity.expired(); }
|
||||||
|
|
||||||
|
std::shared_ptr<Avatar> getTarget(glm::quat& rotation, glm::vec3& position);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const uint16_t holdVersion;
|
static const uint16_t holdVersion;
|
||||||
|
|
||||||
|
|
|
@ -625,19 +625,15 @@ void SkeletonModel::computeBoundingShape() {
|
||||||
totalExtents.addPoint(glm::vec3(0.0f));
|
totalExtents.addPoint(glm::vec3(0.0f));
|
||||||
int numStates = _rig->getJointStateCount();
|
int numStates = _rig->getJointStateCount();
|
||||||
for (int i = 0; i < numStates; i++) {
|
for (int i = 0; i < numStates; i++) {
|
||||||
const JointState& state = _rig->getJointState(i);
|
|
||||||
|
|
||||||
const glm::mat4& jointTransform = state.getTransform();
|
|
||||||
float scale = extractUniformScale(jointTransform);
|
|
||||||
|
|
||||||
// Each joint contributes a capsule defined by FBXJoint.shapeInfo.
|
// Each joint contributes a capsule defined by FBXJoint.shapeInfo.
|
||||||
// For totalExtents we use the capsule endpoints expanded by the radius.
|
// For totalExtents we use the capsule endpoints expanded by the radius.
|
||||||
|
const JointState& state = _rig->getJointState(i);
|
||||||
|
const glm::mat4& jointTransform = state.getTransform();
|
||||||
const FBXJointShapeInfo& shapeInfo = geometry.joints.at(i).shapeInfo;
|
const FBXJointShapeInfo& shapeInfo = geometry.joints.at(i).shapeInfo;
|
||||||
for (int j = 0; j < shapeInfo.points.size(); ++j) {
|
if (shapeInfo.points.size() > 0) {
|
||||||
glm::vec3 transformedPoint = extractTranslation(jointTransform * glm::translate(shapeInfo.points[j]));
|
for (int j = 0; j < shapeInfo.points.size(); ++j) {
|
||||||
vec3 radius(scale * shapeInfo.radius);
|
totalExtents.addPoint(extractTranslation(jointTransform * glm::translate(shapeInfo.points[j])));
|
||||||
totalExtents.addPoint(transformedPoint + radius);
|
}
|
||||||
totalExtents.addPoint(transformedPoint - radius);
|
|
||||||
}
|
}
|
||||||
// HACK so that default legless robot doesn't knuckle-drag
|
// HACK so that default legless robot doesn't knuckle-drag
|
||||||
if (shapeInfo.points.size() == 0 && (state.getName() == "LeftFoot" || state.getName() == "RightFoot")) {
|
if (shapeInfo.points.size() == 0 && (state.getName() == "LeftFoot" || state.getName() == "RightFoot")) {
|
||||||
|
@ -668,7 +664,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha
|
||||||
// draw a blue sphere at the capsule top point
|
// draw a blue sphere at the capsule top point
|
||||||
glm::vec3 topPoint = _translation + _boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f);
|
glm::vec3 topPoint = _translation + _boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f);
|
||||||
|
|
||||||
deferredLighting->renderSolidSphereInstance(batch,
|
deferredLighting->renderSolidSphereInstance(batch,
|
||||||
Transform().setTranslation(topPoint).postScale(_boundingCapsuleRadius),
|
Transform().setTranslation(topPoint).postScale(_boundingCapsuleRadius),
|
||||||
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
||||||
|
|
||||||
|
@ -676,7 +672,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha
|
||||||
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, _boundingCapsuleHeight, 0.0f);
|
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, _boundingCapsuleHeight, 0.0f);
|
||||||
glm::vec3 axis = topPoint - bottomPoint;
|
glm::vec3 axis = topPoint - bottomPoint;
|
||||||
|
|
||||||
deferredLighting->renderSolidSphereInstance(batch,
|
deferredLighting->renderSolidSphereInstance(batch,
|
||||||
Transform().setTranslation(bottomPoint).postScale(_boundingCapsuleRadius),
|
Transform().setTranslation(bottomPoint).postScale(_boundingCapsuleRadius),
|
||||||
glm::vec4(0.8f, 0.8f, 0.6f, alpha));
|
glm::vec4(0.8f, 0.8f, 0.6f, alpha));
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ public:
|
||||||
bool getNeckPosition(glm::vec3& neckPosition) const;
|
bool getNeckPosition(glm::vec3& neckPosition) const;
|
||||||
|
|
||||||
bool getLocalNeckPosition(glm::vec3& neckPosition) const;
|
bool getLocalNeckPosition(glm::vec3& neckPosition) const;
|
||||||
|
|
||||||
/// Returns the rotation of the neck joint's parent from default orientation
|
/// Returns the rotation of the neck joint's parent from default orientation
|
||||||
/// \return whether or not the neck was found
|
/// \return whether or not the neck was found
|
||||||
bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const;
|
bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const;
|
||||||
|
|
|
@ -127,36 +127,30 @@ void Stats::updateStats(bool force) {
|
||||||
STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f);
|
STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f);
|
||||||
|
|
||||||
// Second column: ping
|
// Second column: ping
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) {
|
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||||
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
||||||
SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer);
|
SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||||
SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer);
|
STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1);
|
||||||
STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1);
|
STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1);
|
||||||
STAT_UPDATE(avatarPing, avatarMixerNode ? avatarMixerNode->getPingMs() : -1);
|
STAT_UPDATE(assetPing, assetServerNode ? assetServerNode->getPingMs() : -1);
|
||||||
STAT_UPDATE(assetPing, assetServerNode ? assetServerNode->getPingMs() : -1);
|
|
||||||
|
|
||||||
//// Now handle entity servers, since there could be more than one, we average their ping times
|
|
||||||
int totalPingOctree = 0;
|
|
||||||
int octreeServerCount = 0;
|
|
||||||
int pingOctreeMax = 0;
|
|
||||||
nodeList->eachNode([&](const SharedNodePointer& node) {
|
|
||||||
// TODO: this should also support entities
|
|
||||||
if (node->getType() == NodeType::EntityServer) {
|
|
||||||
totalPingOctree += node->getPingMs();
|
|
||||||
octreeServerCount++;
|
|
||||||
if (pingOctreeMax < node->getPingMs()) {
|
|
||||||
pingOctreeMax = node->getPingMs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// update the entities ping with the average for all connected entity servers
|
|
||||||
STAT_UPDATE(entitiesPing, octreeServerCount ? totalPingOctree / octreeServerCount : -1);
|
|
||||||
} else {
|
|
||||||
// -2 causes the QML to hide the ping column
|
|
||||||
STAT_UPDATE(audioPing, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//// Now handle entity servers, since there could be more than one, we average their ping times
|
||||||
|
int totalPingOctree = 0;
|
||||||
|
int octreeServerCount = 0;
|
||||||
|
int pingOctreeMax = 0;
|
||||||
|
nodeList->eachNode([&](const SharedNodePointer& node) {
|
||||||
|
// TODO: this should also support entities
|
||||||
|
if (node->getType() == NodeType::EntityServer) {
|
||||||
|
totalPingOctree += node->getPingMs();
|
||||||
|
octreeServerCount++;
|
||||||
|
if (pingOctreeMax < node->getPingMs()) {
|
||||||
|
pingOctreeMax = node->getPingMs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// update the entities ping with the average for all connected entity servers
|
||||||
|
STAT_UPDATE(entitiesPing, octreeServerCount ? totalPingOctree / octreeServerCount : -1);
|
||||||
|
|
||||||
// Third column, avatar stats
|
// Third column, avatar stats
|
||||||
MyAvatar* myAvatar = avatarManager->getMyAvatar();
|
MyAvatar* myAvatar = avatarManager->getMyAvatar();
|
||||||
|
|
|
@ -743,19 +743,9 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioClient::handleAudioInput() {
|
void AudioClient::handleAudioInput() {
|
||||||
if (!_audioPacket) {
|
|
||||||
// we don't have an audioPacket yet - set that up now
|
|
||||||
_audioPacket = NLPacket::create(PacketType::MicrophoneAudioNoEcho);
|
|
||||||
}
|
|
||||||
|
|
||||||
const float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio();
|
const float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio();
|
||||||
|
|
||||||
const int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio);
|
const int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio);
|
||||||
const auto inputAudioSamples = std::unique_ptr<int16_t[]>(new int16_t[inputSamplesRequired]);
|
const auto inputAudioSamples = std::unique_ptr<int16_t[]>(new int16_t[inputSamplesRequired]);
|
||||||
|
|
||||||
static const int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8);
|
|
||||||
int16_t* const networkAudioSamples = (int16_t*)(_audioPacket->getPayload() + leadingBytes);
|
|
||||||
|
|
||||||
QByteArray inputByteArray = _inputDevice->readAll();
|
QByteArray inputByteArray = _inputDevice->readAll();
|
||||||
|
|
||||||
// Add audio source injection if enabled
|
// Add audio source injection if enabled
|
||||||
|
@ -784,30 +774,30 @@ void AudioClient::handleAudioInput() {
|
||||||
float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC));
|
float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC));
|
||||||
_stats.updateInputMsecsRead(audioInputMsecsRead);
|
_stats.updateInputMsecsRead(audioInputMsecsRead);
|
||||||
|
|
||||||
while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) {
|
const int numNetworkBytes = _isStereoInput
|
||||||
|
? AudioConstants::NETWORK_FRAME_BYTES_STEREO
|
||||||
|
: AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
|
||||||
|
const int numNetworkSamples = _isStereoInput
|
||||||
|
? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO
|
||||||
|
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
|
||||||
|
|
||||||
const int numNetworkBytes = _isStereoInput
|
static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
|
||||||
? AudioConstants::NETWORK_FRAME_BYTES_STEREO
|
|
||||||
: AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
|
while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) {
|
||||||
const int numNetworkSamples = _isStereoInput
|
|
||||||
? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO
|
|
||||||
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
|
|
||||||
|
|
||||||
if (!_muted) {
|
if (!_muted) {
|
||||||
|
|
||||||
// zero out the monoAudioSamples array and the locally injected audio
|
|
||||||
memset(networkAudioSamples, 0, numNetworkBytes);
|
|
||||||
|
|
||||||
// Increment the time since the last clip
|
// Increment the time since the last clip
|
||||||
if (_timeSinceLastClip >= 0.0f) {
|
if (_timeSinceLastClip >= 0.0f) {
|
||||||
_timeSinceLastClip += (float) numNetworkSamples / (float) AudioConstants::SAMPLE_RATE;
|
_timeSinceLastClip += (float)numNetworkSamples / (float)AudioConstants::SAMPLE_RATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
_inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired);
|
_inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired);
|
||||||
possibleResampling(_inputToNetworkResampler,
|
possibleResampling(_inputToNetworkResampler,
|
||||||
inputAudioSamples.get(), networkAudioSamples,
|
inputAudioSamples.get(), networkAudioSamples,
|
||||||
inputSamplesRequired, numNetworkSamples,
|
inputSamplesRequired, numNetworkSamples,
|
||||||
_inputFormat, _desiredInputFormat);
|
_inputFormat, _desiredInputFormat);
|
||||||
|
|
||||||
// Remove DC offset
|
// Remove DC offset
|
||||||
if (!_isStereoInput && !_audioSourceInjectEnabled) {
|
if (!_isStereoInput && !_audioSourceInjectEnabled) {
|
||||||
|
@ -829,7 +819,7 @@ void AudioClient::handleAudioInput() {
|
||||||
|
|
||||||
for (int i = 0; i < numNetworkSamples; i++) {
|
for (int i = 0; i < numNetworkSamples; i++) {
|
||||||
int thisSample = std::abs(networkAudioSamples[i]);
|
int thisSample = std::abs(networkAudioSamples[i]);
|
||||||
loudness += (float) thisSample;
|
loudness += (float)thisSample;
|
||||||
|
|
||||||
if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) {
|
if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) {
|
||||||
_timeSinceLastClip = 0.0f;
|
_timeSinceLastClip = 0.0f;
|
||||||
|
@ -839,7 +829,7 @@ void AudioClient::handleAudioInput() {
|
||||||
_lastInputLoudness = fabs(loudness / numNetworkSamples);
|
_lastInputLoudness = fabs(loudness / numNetworkSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit inputReceived({reinterpret_cast<char*>(networkAudioSamples), numNetworkBytes});
|
emit inputReceived({ reinterpret_cast<char*>(networkAudioSamples), numNetworkBytes });
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// our input loudness is 0, since we're muted
|
// our input loudness is 0, since we're muted
|
||||||
|
@ -849,14 +839,38 @@ void AudioClient::handleAudioInput() {
|
||||||
_inputRingBuffer.shiftReadPosition(inputSamplesRequired);
|
_inputRingBuffer.shiftReadPosition(inputSamplesRequired);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
emitAudioPacket(networkAudioSamples);
|
||||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (audioMixer && audioMixer->getActiveSocket()) {
|
void AudioClient::emitAudioPacket(const int16_t* audioData, PacketType packetType) {
|
||||||
glm::vec3 headPosition = _positionGetter();
|
static std::mutex _mutex;
|
||||||
glm::quat headOrientation = _orientationGetter();
|
using Locker = std::unique_lock<std::mutex>;
|
||||||
quint8 isStereo = _isStereoInput ? 1 : 0;
|
|
||||||
|
|
||||||
|
// FIXME recorded audio isn't guaranteed to have the same stereo state
|
||||||
|
// as the current system
|
||||||
|
const int numNetworkBytes = _isStereoInput
|
||||||
|
? AudioConstants::NETWORK_FRAME_BYTES_STEREO
|
||||||
|
: AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL;
|
||||||
|
const int numNetworkSamples = _isStereoInput
|
||||||
|
? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO
|
||||||
|
: AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
|
||||||
|
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
||||||
|
|
||||||
|
if (audioMixer && audioMixer->getActiveSocket()) {
|
||||||
|
Locker lock(_mutex);
|
||||||
|
if (!_audioPacket) {
|
||||||
|
// we don't have an audioPacket yet - set that up now
|
||||||
|
_audioPacket = NLPacket::create(PacketType::MicrophoneAudioWithEcho);
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 headPosition = _positionGetter();
|
||||||
|
glm::quat headOrientation = _orientationGetter();
|
||||||
|
quint8 isStereo = _isStereoInput ? 1 : 0;
|
||||||
|
|
||||||
|
if (packetType == PacketType::Unknown) {
|
||||||
if (_lastInputLoudness == 0) {
|
if (_lastInputLoudness == 0) {
|
||||||
_audioPacket->setType(PacketType::SilentAudioFrame);
|
_audioPacket->setType(PacketType::SilentAudioFrame);
|
||||||
} else {
|
} else {
|
||||||
|
@ -866,70 +880,52 @@ void AudioClient::handleAudioInput() {
|
||||||
_audioPacket->setType(PacketType::MicrophoneAudioNoEcho);
|
_audioPacket->setType(PacketType::MicrophoneAudioNoEcho);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// reset the audio packet so we can start writing
|
_audioPacket->setType(packetType);
|
||||||
_audioPacket->reset();
|
|
||||||
|
|
||||||
// write sequence number
|
|
||||||
_audioPacket->writePrimitive(_outgoingAvatarAudioSequenceNumber);
|
|
||||||
|
|
||||||
if (_audioPacket->getType() == PacketType::SilentAudioFrame) {
|
|
||||||
// pack num silent samples
|
|
||||||
quint16 numSilentSamples = numNetworkSamples;
|
|
||||||
_audioPacket->writePrimitive(numSilentSamples);
|
|
||||||
} else {
|
|
||||||
// set the mono/stereo byte
|
|
||||||
_audioPacket->writePrimitive(isStereo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// pack the three float positions
|
|
||||||
_audioPacket->writePrimitive(headPosition);
|
|
||||||
|
|
||||||
// pack the orientation
|
|
||||||
_audioPacket->writePrimitive(headOrientation);
|
|
||||||
|
|
||||||
if (_audioPacket->getType() != PacketType::SilentAudioFrame) {
|
|
||||||
// audio samples have already been packed (written to networkAudioSamples)
|
|
||||||
_audioPacket->setPayloadSize(_audioPacket->getPayloadSize() + numNetworkBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
_stats.sentPacket();
|
|
||||||
|
|
||||||
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket);
|
|
||||||
|
|
||||||
nodeList->sendUnreliablePacket(*_audioPacket, *audioMixer);
|
|
||||||
|
|
||||||
_outgoingAvatarAudioSequenceNumber++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset the audio packet so we can start writing
|
||||||
|
_audioPacket->reset();
|
||||||
|
|
||||||
|
// write sequence number
|
||||||
|
_audioPacket->writePrimitive(_outgoingAvatarAudioSequenceNumber);
|
||||||
|
|
||||||
|
if (_audioPacket->getType() == PacketType::SilentAudioFrame) {
|
||||||
|
// pack num silent samples
|
||||||
|
quint16 numSilentSamples = numNetworkSamples;
|
||||||
|
_audioPacket->writePrimitive(numSilentSamples);
|
||||||
|
} else {
|
||||||
|
// set the mono/stereo byte
|
||||||
|
_audioPacket->writePrimitive(isStereo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack the three float positions
|
||||||
|
_audioPacket->writePrimitive(headPosition);
|
||||||
|
|
||||||
|
// pack the orientation
|
||||||
|
_audioPacket->writePrimitive(headOrientation);
|
||||||
|
|
||||||
|
if (_audioPacket->getType() != PacketType::SilentAudioFrame) {
|
||||||
|
// audio samples have already been packed (written to networkAudioSamples)
|
||||||
|
_audioPacket->setPayloadSize(_audioPacket->getPayloadSize() + numNetworkBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int leadingBytes = sizeof(quint16) + sizeof(glm::vec3) + sizeof(glm::quat) + sizeof(quint8);
|
||||||
|
int16_t* const networkAudioSamples = (int16_t*)(_audioPacket->getPayload() + leadingBytes);
|
||||||
|
memcpy(networkAudioSamples, audioData, numNetworkBytes);
|
||||||
|
|
||||||
|
_stats.sentPacket();
|
||||||
|
|
||||||
|
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket);
|
||||||
|
|
||||||
|
nodeList->sendUnreliablePacket(*_audioPacket, *audioMixer);
|
||||||
|
|
||||||
|
_outgoingAvatarAudioSequenceNumber++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
|
void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
|
||||||
if (!_audioPacket) {
|
emitAudioPacket((int16_t*)audio.data(), PacketType::MicrophoneAudioWithEcho);
|
||||||
// we don't have an audioPacket yet - set that up now
|
|
||||||
_audioPacket = NLPacket::create(PacketType::MicrophoneAudioWithEcho);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME either discard stereo in the recording or record a stereo flag
|
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
|
||||||
SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer);
|
|
||||||
if (audioMixer && audioMixer->getActiveSocket()) {
|
|
||||||
glm::vec3 headPosition = _positionGetter();
|
|
||||||
glm::quat headOrientation = _orientationGetter();
|
|
||||||
quint8 isStereo = _isStereoInput ? 1 : 0;
|
|
||||||
_audioPacket->reset();
|
|
||||||
_audioPacket->setType(PacketType::MicrophoneAudioWithEcho);
|
|
||||||
_audioPacket->writePrimitive(_outgoingAvatarAudioSequenceNumber);
|
|
||||||
_audioPacket->writePrimitive(isStereo);
|
|
||||||
_audioPacket->writePrimitive(headPosition);
|
|
||||||
_audioPacket->writePrimitive(headOrientation);
|
|
||||||
_audioPacket->write(audio);
|
|
||||||
_stats.sentPacket();
|
|
||||||
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket);
|
|
||||||
nodeList->sendUnreliablePacket(*_audioPacket, *audioMixer);
|
|
||||||
_outgoingAvatarAudioSequenceNumber++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer) {
|
void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer) {
|
||||||
|
|
|
@ -212,6 +212,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void emitAudioPacket(const int16_t* audioData, PacketType packetType = PacketType::Unknown);
|
||||||
void outputFormatChanged();
|
void outputFormatChanged();
|
||||||
|
|
||||||
QByteArray firstInputFrame;
|
QByteArray firstInputFrame;
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
set(TARGET_NAME avatars)
|
set(TARGET_NAME avatars)
|
||||||
setup_hifi_library(Network Script)
|
setup_hifi_library(Network Script)
|
||||||
link_hifi_libraries(audio shared networking recording)
|
link_hifi_libraries(shared networking)
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
#include <StreamUtils.h>
|
#include <StreamUtils.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
#include <shared/JSONHelpers.h>
|
#include <shared/JSONHelpers.h>
|
||||||
#include <recording/Frame.h>
|
|
||||||
|
|
||||||
#include "AvatarLogging.h"
|
#include "AvatarLogging.h"
|
||||||
|
|
||||||
|
@ -178,7 +177,7 @@ float AvatarData::getTargetScale() const {
|
||||||
|
|
||||||
void AvatarData::setTargetScale(float targetScale, bool overideReferential) {
|
void AvatarData::setTargetScale(float targetScale, bool overideReferential) {
|
||||||
if (!_referential || overideReferential) {
|
if (!_referential || overideReferential) {
|
||||||
_targetScale = targetScale;
|
_targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, targetScale));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,7 +531,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
}
|
}
|
||||||
return maxAvailableSize;
|
return maxAvailableSize;
|
||||||
}
|
}
|
||||||
_targetScale = scale;
|
_targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, scale));
|
||||||
} // 20 bytes
|
} // 20 bytes
|
||||||
|
|
||||||
{ // Lookat Position
|
{ // Lookat Position
|
||||||
|
@ -1443,14 +1442,10 @@ QByteArray AvatarData::toFrame(const AvatarData& avatar) {
|
||||||
|
|
||||||
auto recordingBasis = avatar.getRecordingBasis();
|
auto recordingBasis = avatar.getRecordingBasis();
|
||||||
if (recordingBasis) {
|
if (recordingBasis) {
|
||||||
|
root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis);
|
||||||
// Find the relative transform
|
// Find the relative transform
|
||||||
auto relativeTransform = recordingBasis->relativeTransform(avatar.getTransform());
|
auto relativeTransform = recordingBasis->relativeTransform(avatar.getTransform());
|
||||||
|
root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform);
|
||||||
// if the resulting relative basis is identity, we shouldn't record anything
|
|
||||||
if (!relativeTransform.isIdentity()) {
|
|
||||||
root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform);
|
|
||||||
root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
root[JSON_AVATAR_RELATIVE] = Transform::toJson(avatar.getTransform());
|
root[JSON_AVATAR_RELATIVE] = Transform::toJson(avatar.getTransform());
|
||||||
}
|
}
|
||||||
|
@ -1484,6 +1479,9 @@ QByteArray AvatarData::toFrame(const AvatarData& avatar) {
|
||||||
|
|
||||||
void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) {
|
void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) {
|
||||||
QJsonDocument doc = QJsonDocument::fromBinaryData(frameData);
|
QJsonDocument doc = QJsonDocument::fromBinaryData(frameData);
|
||||||
|
#ifdef WANT_JSON_DEBUG
|
||||||
|
qDebug() << doc.toJson(QJsonDocument::JsonFormat::Indented);
|
||||||
|
#endif
|
||||||
QJsonObject root = doc.object();
|
QJsonObject root = doc.object();
|
||||||
|
|
||||||
if (root.contains(JSON_AVATAR_HEAD_MODEL)) {
|
if (root.contains(JSON_AVATAR_HEAD_MODEL)) {
|
||||||
|
|
|
@ -50,7 +50,6 @@ typedef unsigned long long quint64;
|
||||||
#include <Node.h>
|
#include <Node.h>
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
#include <SimpleMovingAverage.h>
|
#include <SimpleMovingAverage.h>
|
||||||
#include <recording/Forward.h>
|
|
||||||
|
|
||||||
#include "AABox.h"
|
#include "AABox.h"
|
||||||
#include "HandData.h"
|
#include "HandData.h"
|
||||||
|
|
|
@ -12,11 +12,14 @@
|
||||||
#ifndef hifi_EntityActionInterface_h
|
#ifndef hifi_EntityActionInterface_h
|
||||||
#define hifi_EntityActionInterface_h
|
#define hifi_EntityActionInterface_h
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include "EntityItem.h"
|
class EntityItem;
|
||||||
|
|
||||||
class EntitySimulation;
|
class EntitySimulation;
|
||||||
|
using EntityItemPointer = std::shared_ptr<EntityItem>;
|
||||||
|
using EntityItemWeakPointer = std::weak_ptr<EntityItem>;
|
||||||
|
|
||||||
enum EntityActionType {
|
enum EntityActionType {
|
||||||
// keep these synchronized with actionTypeFromString and actionTypeToString
|
// keep these synchronized with actionTypeFromString and actionTypeToString
|
||||||
|
@ -34,6 +37,8 @@ public:
|
||||||
const QUuid& getID() const { return _id; }
|
const QUuid& getID() const { return _id; }
|
||||||
EntityActionType getType() const { return _type; }
|
EntityActionType getType() const { return _type; }
|
||||||
|
|
||||||
|
bool isActive() { return _active; }
|
||||||
|
|
||||||
virtual void removeFromSimulation(EntitySimulation* simulation) const = 0;
|
virtual void removeFromSimulation(EntitySimulation* simulation) const = 0;
|
||||||
virtual EntityItemWeakPointer getOwnerEntity() const = 0;
|
virtual EntityItemWeakPointer getOwnerEntity() const = 0;
|
||||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0;
|
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0;
|
||||||
|
@ -81,6 +86,7 @@ protected:
|
||||||
|
|
||||||
QUuid _id;
|
QUuid _id;
|
||||||
EntityActionType _type;
|
EntityActionType _type;
|
||||||
|
bool _active { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1844,3 +1844,18 @@ bool EntityItem::shouldSuppressLocationEdits() const {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<EntityActionPointer> EntityItem::getActionsOfType(EntityActionType typeToGet) {
|
||||||
|
QList<EntityActionPointer> result;
|
||||||
|
|
||||||
|
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||||
|
while (i != _objectActions.end()) {
|
||||||
|
EntityActionPointer action = i.value();
|
||||||
|
if (action->getType() == typeToGet && action->isActive()) {
|
||||||
|
result += action;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "EntityTypes.h"
|
#include "EntityTypes.h"
|
||||||
#include "SimulationOwner.h"
|
#include "SimulationOwner.h"
|
||||||
#include "SimulationFlags.h"
|
#include "SimulationFlags.h"
|
||||||
|
#include "EntityActionInterface.h"
|
||||||
|
|
||||||
class EntitySimulation;
|
class EntitySimulation;
|
||||||
class EntityTreeElement;
|
class EntityTreeElement;
|
||||||
|
@ -419,7 +420,9 @@ public:
|
||||||
|
|
||||||
void setSourceUUID(const QUuid& sourceUUID) { _sourceUUID = sourceUUID; }
|
void setSourceUUID(const QUuid& sourceUUID) { _sourceUUID = sourceUUID; }
|
||||||
const QUuid& getSourceUUID() const { return _sourceUUID; }
|
const QUuid& getSourceUUID() const { return _sourceUUID; }
|
||||||
bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; }
|
bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; }
|
||||||
|
|
||||||
|
QList<EntityActionPointer> getActionsOfType(EntityActionType typeToGet);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
|
@ -704,6 +704,14 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
|
||||||
changedProperties[index] = QString("locked:") + changeHint;
|
changedProperties[index] = QString("locked:") + changeHint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (properties.userDataChanged()) {
|
||||||
|
int index = changedProperties.indexOf("userData");
|
||||||
|
if (index >= 0) {
|
||||||
|
QString changeHint = properties.getUserData();
|
||||||
|
changedProperties[index] = QString("userData:") + changeHint;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength,
|
int EntityTree::processEditPacketData(NLPacket& packet, const unsigned char* editData, int maxLength,
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
#include <OctreeRenderer.h> // for RenderArgs
|
#include <OctreeRenderer.h> // for RenderArgs
|
||||||
|
|
||||||
class EntityItem;
|
class EntityItem;
|
||||||
typedef std::shared_ptr<EntityItem> EntityItemPointer;
|
using EntityItemPointer = std::shared_ptr<EntityItem>;
|
||||||
typedef std::weak_ptr<EntityItem> EntityItemWeakPointer;
|
using EntityItemWeakPointer = std::weak_ptr<EntityItem>;
|
||||||
|
|
||||||
inline uint qHash(const EntityItemPointer& a, uint seed) {
|
inline uint qHash(const EntityItemPointer& a, uint seed) {
|
||||||
return qHash(a.get(), seed);
|
return qHash(a.get(), seed);
|
||||||
|
|
|
@ -1460,7 +1460,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
it != extracted.newIndices.end() && it.key() == oldIndex; it++) {
|
it != extracted.newIndices.end() && it.key() == oldIndex; it++) {
|
||||||
|
|
||||||
// remember vertices with at least 1/4 weight
|
// remember vertices with at least 1/4 weight
|
||||||
const float EXPANSION_WEIGHT_THRESHOLD = 0.25f;
|
const float EXPANSION_WEIGHT_THRESHOLD = 0.99f;
|
||||||
if (weight > EXPANSION_WEIGHT_THRESHOLD) {
|
if (weight > EXPANSION_WEIGHT_THRESHOLD) {
|
||||||
// transform to joint-frame and save for later
|
// transform to joint-frame and save for later
|
||||||
const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(it.value()));
|
const glm::mat4 vertexTransform = meshToJoint * glm::translate(extracted.mesh.vertices.at(it.value()));
|
||||||
|
@ -1535,63 +1535,49 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
|
||||||
meshIDsToMeshIndices.insert(it.key(), meshIndex);
|
meshIDsToMeshIndices.insert(it.key(), meshIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now that all joints have been scanned, compute a radius for each bone
|
const float INV_SQRT_3 = 0.57735026918f;
|
||||||
|
ShapeVertices cardinalDirections = {
|
||||||
|
Vectors::UNIT_X,
|
||||||
|
Vectors::UNIT_Y,
|
||||||
|
Vectors::UNIT_Z,
|
||||||
|
glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
|
||||||
|
glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
|
||||||
|
glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
|
||||||
|
glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)
|
||||||
|
};
|
||||||
|
|
||||||
|
// now that all joints have been scanned compute a k-Dop bounding volume of mesh
|
||||||
glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f);
|
glm::vec3 defaultCapsuleAxis(0.0f, 1.0f, 0.0f);
|
||||||
for (int i = 0; i < geometry.joints.size(); ++i) {
|
for (int i = 0; i < geometry.joints.size(); ++i) {
|
||||||
FBXJoint& joint = geometry.joints[i];
|
FBXJoint& joint = geometry.joints[i];
|
||||||
|
|
||||||
// NOTE: points are in joint-frame
|
// NOTE: points are in joint-frame
|
||||||
// compute average point
|
|
||||||
ShapeVertices& points = shapeVertices[i];
|
ShapeVertices& points = shapeVertices[i];
|
||||||
glm::vec3 avgPoint = glm::vec3(0.0f);
|
|
||||||
for (uint32_t j = 0; j < points.size(); ++j) {
|
|
||||||
avgPoint += points[j];
|
|
||||||
}
|
|
||||||
avgPoint /= (float)points.size();
|
|
||||||
|
|
||||||
// compute axis from begin to avgPoint
|
|
||||||
glm::vec3 begin(0.0f);
|
|
||||||
glm::vec3 end = avgPoint;
|
|
||||||
glm::vec3 axis = end - begin;
|
|
||||||
float axisLength = glm::length(axis);
|
|
||||||
if (axisLength > EPSILON) {
|
|
||||||
axis /= axisLength;
|
|
||||||
} else {
|
|
||||||
axis = glm::vec3(0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
// measure average cylindrical radius
|
|
||||||
float avgRadius = 0.0f;
|
|
||||||
if (points.size() > 0) {
|
if (points.size() > 0) {
|
||||||
float minProjection = FLT_MAX;
|
// compute average point
|
||||||
float maxProjection = -FLT_MIN;
|
glm::vec3 avgPoint = glm::vec3(0.0f);
|
||||||
for (uint32_t j = 0; j < points.size(); ++j) {
|
for (uint32_t j = 0; j < points.size(); ++j) {
|
||||||
glm::vec3 offset = points[j] - avgPoint;
|
avgPoint += points[j];
|
||||||
float projection = glm::dot(offset, axis);
|
|
||||||
maxProjection = glm::max(maxProjection, projection);
|
|
||||||
minProjection = glm::min(minProjection, projection);
|
|
||||||
avgRadius += glm::length(offset - projection * axis);
|
|
||||||
}
|
}
|
||||||
avgRadius /= (float)points.size();
|
avgPoint /= (float)points.size();
|
||||||
|
|
||||||
// compute endpoints of capsule in joint-frame
|
// compute a k-Dop bounding volume
|
||||||
glm::vec3 capsuleBegin = avgPoint;
|
for (uint32_t j = 0; j < cardinalDirections.size(); ++j) {
|
||||||
glm::vec3 capsuleEnd = avgPoint;
|
float maxDot = -FLT_MAX;
|
||||||
if (maxProjection - minProjection < 2.0f * avgRadius) {
|
float minDot = FLT_MIN;
|
||||||
// the mesh-as-cylinder approximation is too short to collide as a capsule
|
for (uint32_t k = 0; k < points.size(); ++k) {
|
||||||
// so we'll collapse it to a sphere (although that isn't a very good approximation)
|
float kDot = glm::dot(cardinalDirections[j], points[k] - avgPoint);
|
||||||
capsuleBegin = avgPoint + 0.5f * (maxProjection + minProjection) * axis;
|
if (kDot > maxDot) {
|
||||||
capsuleEnd = capsuleBegin;
|
maxDot = kDot;
|
||||||
} else {
|
}
|
||||||
capsuleBegin = avgPoint + (minProjection + avgRadius) * axis;
|
if (kDot < minDot) {
|
||||||
capsuleEnd = avgPoint + (maxProjection - avgRadius) * axis;
|
minDot = kDot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
joint.shapeInfo.points.push_back(avgPoint + maxDot * cardinalDirections[j]);
|
||||||
|
joint.shapeInfo.points.push_back(avgPoint + minDot * cardinalDirections[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save points for later
|
|
||||||
joint.shapeInfo.points.push_back(capsuleBegin);
|
|
||||||
joint.shapeInfo.points.push_back(capsuleEnd);
|
|
||||||
}
|
}
|
||||||
joint.shapeInfo.radius = avgRadius;
|
|
||||||
}
|
}
|
||||||
geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
|
geometry.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,6 @@ public:
|
||||||
struct FBXJointShapeInfo {
|
struct FBXJointShapeInfo {
|
||||||
// same units and frame as FBXJoint.translation
|
// same units and frame as FBXJoint.translation
|
||||||
QVector<glm::vec3> points;
|
QVector<glm::vec3> points;
|
||||||
float radius;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A single joint (transformation node) extracted from an FBX document.
|
/// A single joint (transformation node) extracted from an FBX document.
|
||||||
|
|
|
@ -700,22 +700,6 @@ void LimitedNodeList::sendSTUNRequest() {
|
||||||
_nodeSocket.writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr);
|
_nodeSocket.writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimitedNodeList::sendPingPackets() {
|
|
||||||
eachMatchingNode([](const SharedNodePointer& node)->bool {
|
|
||||||
switch (node->getType()) {
|
|
||||||
case NodeType::AvatarMixer:
|
|
||||||
case NodeType::AudioMixer:
|
|
||||||
case NodeType::EntityServer:
|
|
||||||
case NodeType::AssetServer:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, [&](const SharedNodePointer& node) {
|
|
||||||
sendPacket(constructPingPacket(), *node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packet) {
|
void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packet) {
|
||||||
// check the cookie to make sure this is actually a STUN response
|
// check the cookie to make sure this is actually a STUN response
|
||||||
// and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS
|
// and read the first attribute and make sure it is a XOR_MAPPED_ADDRESS
|
||||||
|
|
|
@ -228,7 +228,6 @@ public slots:
|
||||||
|
|
||||||
void startSTUNPublicSocketUpdate();
|
void startSTUNPublicSocketUpdate();
|
||||||
virtual void sendSTUNRequest();
|
virtual void sendSTUNRequest();
|
||||||
void sendPingPackets();
|
|
||||||
|
|
||||||
bool killNodeWithUUID(const QUuid& nodeUUID);
|
bool killNodeWithUUID(const QUuid& nodeUUID);
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "udt/PacketHeaders.h"
|
#include "udt/PacketHeaders.h"
|
||||||
#include "SharedUtil.h"
|
#include "SharedUtil.h"
|
||||||
|
|
||||||
|
const int KEEPALIVE_PING_INTERVAL_MS = 1000;
|
||||||
|
|
||||||
NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned short dtlsListenPort) :
|
NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned short dtlsListenPort) :
|
||||||
LimitedNodeList(socketListenPort, dtlsListenPort),
|
LimitedNodeList(socketListenPort, dtlsListenPort),
|
||||||
|
@ -87,6 +88,12 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
|
||||||
|
|
||||||
// anytime we get a new node we will want to attempt to punch to it
|
// anytime we get a new node we will want to attempt to punch to it
|
||||||
connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch);
|
connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch);
|
||||||
|
|
||||||
|
// setup our timer to send keepalive pings (it's started and stopped on domain connect/disconnect)
|
||||||
|
_keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS);
|
||||||
|
connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings);
|
||||||
|
connect(&_domainHandler, SIGNAL(connectedToDomain(QString)), &_keepAlivePingTimer, SLOT(start()));
|
||||||
|
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop);
|
||||||
|
|
||||||
// we definitely want STUN to update our public socket, so call the LNL to kick that off
|
// we definitely want STUN to update our public socket, so call the LNL to kick that off
|
||||||
startSTUNPublicSocketUpdate();
|
startSTUNPublicSocketUpdate();
|
||||||
|
@ -632,3 +639,11 @@ void NodeList::activateSocketFromNodeCommunication(QSharedPointer<NLPacket> pack
|
||||||
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetAudioMixerSocket);
|
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetAudioMixerSocket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NodeList::sendKeepAlivePings() {
|
||||||
|
eachMatchingNode([this](const SharedNodePointer& node)->bool {
|
||||||
|
return _nodeTypesOfInterest.contains(node->getType());
|
||||||
|
}, [&](const SharedNodePointer& node) {
|
||||||
|
sendPacket(constructPingPacket(), *node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ public slots:
|
||||||
void processPingReplyPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
void processPingReplyPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
|
||||||
|
|
||||||
void processICEPingPacket(QSharedPointer<NLPacket> packet);
|
void processICEPingPacket(QSharedPointer<NLPacket> packet);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void limitOfSilentDomainCheckInsReached();
|
void limitOfSilentDomainCheckInsReached();
|
||||||
private slots:
|
private slots:
|
||||||
|
@ -95,6 +96,8 @@ private slots:
|
||||||
void handleNodePingTimeout();
|
void handleNodePingTimeout();
|
||||||
|
|
||||||
void pingPunchForDomainServer();
|
void pingPunchForDomainServer();
|
||||||
|
|
||||||
|
void sendKeepAlivePings();
|
||||||
private:
|
private:
|
||||||
NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile
|
NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile
|
||||||
NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
|
NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
|
||||||
|
@ -118,6 +121,7 @@ private:
|
||||||
int _numNoReplyDomainCheckIns;
|
int _numNoReplyDomainCheckIns;
|
||||||
HifiSockAddr _assignmentServerSocket;
|
HifiSockAddr _assignmentServerSocket;
|
||||||
bool _isShuttingDown { false };
|
bool _isShuttingDown { false };
|
||||||
|
QTimer _keepAlivePingTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_NodeList_h
|
#endif // hifi_NodeList_h
|
||||||
|
|
|
@ -67,7 +67,6 @@ protected:
|
||||||
EntityItemWeakPointer _ownerEntity;
|
EntityItemWeakPointer _ownerEntity;
|
||||||
QString _tag;
|
QString _tag;
|
||||||
quint64 _expires { 0 }; // in seconds since epoch
|
quint64 _expires { 0 }; // in seconds since epoch
|
||||||
bool _active { false };
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int getEntityServerClockSkew() const;
|
int getEntityServerClockSkew() const;
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include "Deck.h"
|
#include "Deck.h"
|
||||||
|
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
|
|
||||||
|
@ -101,9 +103,13 @@ float Deck::position() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Frame::Time MIN_FRAME_WAIT_INTERVAL = Frame::secondsToFrameTime(0.001f);
|
static const Frame::Time MIN_FRAME_WAIT_INTERVAL = Frame::secondsToFrameTime(0.001f);
|
||||||
static const Frame::Time MAX_FRAME_PROCESSING_TIME = Frame::secondsToFrameTime(0.002f);
|
static const Frame::Time MAX_FRAME_PROCESSING_TIME = Frame::secondsToFrameTime(0.004f);
|
||||||
|
|
||||||
void Deck::processFrames() {
|
void Deck::processFrames() {
|
||||||
|
if (qApp->thread() != QThread::currentThread()) {
|
||||||
|
qWarning() << "Processing frames must only happen on the main thread.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
Locker lock(_mutex);
|
Locker lock(_mutex);
|
||||||
if (_pause) {
|
if (_pause) {
|
||||||
return;
|
return;
|
||||||
|
@ -115,10 +121,17 @@ void Deck::processFrames() {
|
||||||
// FIXME add code to start dropping frames if we fall behind.
|
// FIXME add code to start dropping frames if we fall behind.
|
||||||
// Alternatively, add code to cache frames here and then process only the last frame of a given type
|
// Alternatively, add code to cache frames here and then process only the last frame of a given type
|
||||||
// ... the latter will work for Avatar, but not well for audio I suspect.
|
// ... the latter will work for Avatar, but not well for audio I suspect.
|
||||||
|
bool overLimit = false;
|
||||||
for (nextClip = getNextClip(); nextClip; nextClip = getNextClip()) {
|
for (nextClip = getNextClip(); nextClip; nextClip = getNextClip()) {
|
||||||
auto currentPosition = Frame::frameTimeFromEpoch(_startEpoch);
|
auto currentPosition = Frame::frameTimeFromEpoch(_startEpoch);
|
||||||
if ((currentPosition - startingPosition) >= MAX_FRAME_PROCESSING_TIME) {
|
if ((currentPosition - startingPosition) >= MAX_FRAME_PROCESSING_TIME) {
|
||||||
qCWarning(recordingLog) << "Exceeded maximum frame processing time, breaking early";
|
qCWarning(recordingLog) << "Exceeded maximum frame processing time, breaking early";
|
||||||
|
#ifdef WANT_RECORDING_DEBUG
|
||||||
|
qCDebug(recordingLog) << "Starting: " << currentPosition;
|
||||||
|
qCDebug(recordingLog) << "Current: " << startingPosition;
|
||||||
|
qCDebug(recordingLog) << "Trigger: " << triggerPosition;
|
||||||
|
#endif
|
||||||
|
overLimit = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,9 +163,19 @@ void Deck::processFrames() {
|
||||||
|
|
||||||
// If we have more clip frames available, set the timer for the next one
|
// If we have more clip frames available, set the timer for the next one
|
||||||
_position = Frame::frameTimeFromEpoch(_startEpoch);
|
_position = Frame::frameTimeFromEpoch(_startEpoch);
|
||||||
auto nextFrameTime = nextClip->positionFrameTime();
|
int nextInterval = 1;
|
||||||
auto interval = Frame::frameTimeToMilliseconds(nextFrameTime - _position);
|
if (!overLimit) {
|
||||||
_timer.singleShot(interval, [this] {
|
auto nextFrameTime = nextClip->positionFrameTime();
|
||||||
|
nextInterval = (int)Frame::frameTimeToMilliseconds(nextFrameTime - _position);
|
||||||
|
#ifdef WANT_RECORDING_DEBUG
|
||||||
|
qCDebug(recordingLog) << "Now " << _position;
|
||||||
|
qCDebug(recordingLog) << "Next frame time " << nextInterval;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef WANT_RECORDING_DEBUG
|
||||||
|
qCDebug(recordingLog) << "Setting timer for next processing " << nextInterval;
|
||||||
|
#endif
|
||||||
|
_timer.singleShot(nextInterval, [this] {
|
||||||
processFrames();
|
processFrames();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,19 @@
|
||||||
|
|
||||||
#include "RecordingScriptingInterface.h"
|
#include "RecordingScriptingInterface.h"
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
#include <recording/Deck.h>
|
#include <recording/Deck.h>
|
||||||
#include <recording/Recorder.h>
|
#include <recording/Recorder.h>
|
||||||
#include <recording/Clip.h>
|
#include <recording/Clip.h>
|
||||||
#include <recording/Frame.h>
|
#include <recording/Frame.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <AudioClient.h>
|
// FiXME
|
||||||
|
//#include <AudioClient.h>
|
||||||
#include <AudioConstants.h>
|
#include <AudioConstants.h>
|
||||||
|
#include <Transform.h>
|
||||||
|
|
||||||
#include "avatar/AvatarManager.h"
|
#include "ScriptEngineLogging.h"
|
||||||
#include "Application.h"
|
|
||||||
#include "InterfaceLogging.h"
|
|
||||||
|
|
||||||
typedef int16_t AudioSample;
|
typedef int16_t AudioSample;
|
||||||
|
|
||||||
|
@ -43,23 +45,28 @@ RecordingScriptingInterface::RecordingScriptingInterface() {
|
||||||
_player = DependencyManager::get<Deck>();
|
_player = DependencyManager::get<Deck>();
|
||||||
_recorder = DependencyManager::get<Recorder>();
|
_recorder = DependencyManager::get<Recorder>();
|
||||||
|
|
||||||
auto audioClient = DependencyManager::get<AudioClient>();
|
// FIXME : Disabling Sound
|
||||||
connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput);
|
// auto audioClient = DependencyManager::get<AudioClient>();
|
||||||
|
// connect(audioClient.data(), &AudioClient::inputReceived, this, &RecordingScriptingInterface::processAudioInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RecordingScriptingInterface::isPlaying() {
|
void RecordingScriptingInterface::setControlledAvatar(AvatarData* avatar) {
|
||||||
|
_controlledAvatar = avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RecordingScriptingInterface::isPlaying() const {
|
||||||
return _player->isPlaying();
|
return _player->isPlaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RecordingScriptingInterface::isPaused() {
|
bool RecordingScriptingInterface::isPaused() const {
|
||||||
return _player->isPaused();
|
return _player->isPaused();
|
||||||
}
|
}
|
||||||
|
|
||||||
float RecordingScriptingInterface::playerElapsed() {
|
float RecordingScriptingInterface::playerElapsed() const {
|
||||||
return _player->position();
|
return _player->position();
|
||||||
}
|
}
|
||||||
|
|
||||||
float RecordingScriptingInterface::playerLength() {
|
float RecordingScriptingInterface::playerLength() const {
|
||||||
return _player->length();
|
return _player->length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,10 +91,10 @@ void RecordingScriptingInterface::startPlaying() {
|
||||||
QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection);
|
QMetaObject::invokeMethod(this, "startPlaying", Qt::BlockingQueuedConnection);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
|
||||||
// Playback from the current position
|
// Playback from the current position
|
||||||
if (_playFromCurrentLocation) {
|
if (_playFromCurrentLocation && _controlledAvatar) {
|
||||||
_dummyAvatar.setRecordingBasis(std::make_shared<Transform>(myAvatar->getTransform()));
|
_dummyAvatar.setRecordingBasis(std::make_shared<Transform>(_controlledAvatar->getTransform()));
|
||||||
} else {
|
} else {
|
||||||
_dummyAvatar.clearRecordingBasis();
|
_dummyAvatar.clearRecordingBasis();
|
||||||
}
|
}
|
||||||
|
@ -103,6 +110,10 @@ void RecordingScriptingInterface::setPlayerAudioOffset(float audioOffset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordingScriptingInterface::setPlayerTime(float time) {
|
void RecordingScriptingInterface::setPlayerTime(float time) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setPlayerTime", Qt::BlockingQueuedConnection, Q_ARG(float, time));
|
||||||
|
return;
|
||||||
|
}
|
||||||
_player->seek(time);
|
_player->seek(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,29 +141,33 @@ void RecordingScriptingInterface::setPlayerUseSkeletonModel(bool useSkeletonMode
|
||||||
_useSkeletonModel = useSkeletonModel;
|
_useSkeletonModel = useSkeletonModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordingScriptingInterface::play() {
|
|
||||||
_player->play();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RecordingScriptingInterface::pausePlayer() {
|
void RecordingScriptingInterface::pausePlayer() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "pausePlayer", Qt::BlockingQueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_player->pause();
|
_player->pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordingScriptingInterface::stopPlaying() {
|
void RecordingScriptingInterface::stopPlaying() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "stopPlaying", Qt::BlockingQueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_player->stop();
|
_player->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RecordingScriptingInterface::isRecording() {
|
bool RecordingScriptingInterface::isRecording() const {
|
||||||
return _recorder->isRecording();
|
return _recorder->isRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
float RecordingScriptingInterface::recorderElapsed() {
|
float RecordingScriptingInterface::recorderElapsed() const {
|
||||||
return _recorder->position();
|
return _recorder->position();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordingScriptingInterface::startRecording() {
|
void RecordingScriptingInterface::startRecording() {
|
||||||
if (_recorder->isRecording()) {
|
if (_recorder->isRecording()) {
|
||||||
qCWarning(interfaceapp) << "Recorder is already running";
|
qCWarning(scriptengine) << "Recorder is already running";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,25 +178,21 @@ void RecordingScriptingInterface::startRecording() {
|
||||||
|
|
||||||
_recordingEpoch = Frame::epochForFrameTime(0);
|
_recordingEpoch = Frame::epochForFrameTime(0);
|
||||||
|
|
||||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
if (_controlledAvatar) {
|
||||||
myAvatar->setRecordingBasis();
|
_controlledAvatar->setRecordingBasis();
|
||||||
|
}
|
||||||
|
|
||||||
_recorder->start();
|
_recorder->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordingScriptingInterface::stopRecording() {
|
void RecordingScriptingInterface::stopRecording() {
|
||||||
_recorder->stop();
|
_recorder->stop();
|
||||||
|
|
||||||
_lastClip = _recorder->getClip();
|
_lastClip = _recorder->getClip();
|
||||||
// post-process the audio into discreet chunks based on times of received samples
|
|
||||||
_lastClip->seek(0);
|
|
||||||
Frame::ConstPointer frame;
|
|
||||||
while (frame = _lastClip->nextFrame()) {
|
|
||||||
qDebug() << "Frame time " << frame->timeOffset << " size " << frame->data.size();
|
|
||||||
}
|
|
||||||
_lastClip->seek(0);
|
_lastClip->seek(0);
|
||||||
|
|
||||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
if (_controlledAvatar) {
|
||||||
myAvatar->clearRecordingBasis();
|
_controlledAvatar->clearRecordingBasis();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordingScriptingInterface::saveRecording(const QString& filename) {
|
void RecordingScriptingInterface::saveRecording(const QString& filename) {
|
||||||
|
@ -206,7 +217,7 @@ void RecordingScriptingInterface::loadLastRecording() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_lastClip) {
|
if (!_lastClip) {
|
||||||
qCDebug(interfaceapp) << "There is no recording to load";
|
qCDebug(scriptengine) << "There is no recording to load";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,27 +228,32 @@ void RecordingScriptingInterface::loadLastRecording() {
|
||||||
void RecordingScriptingInterface::processAvatarFrame(const Frame::ConstPointer& frame) {
|
void RecordingScriptingInterface::processAvatarFrame(const Frame::ConstPointer& frame) {
|
||||||
Q_ASSERT(QThread::currentThread() == thread());
|
Q_ASSERT(QThread::currentThread() == thread());
|
||||||
|
|
||||||
|
if (!_controlledAvatar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AvatarData::fromFrame(frame->data, _dummyAvatar);
|
AvatarData::fromFrame(frame->data, _dummyAvatar);
|
||||||
|
|
||||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
|
||||||
|
|
||||||
if (_useHeadModel && _dummyAvatar.getFaceModelURL().isValid() &&
|
if (_useHeadModel && _dummyAvatar.getFaceModelURL().isValid() &&
|
||||||
(_dummyAvatar.getFaceModelURL() != myAvatar->getFaceModelURL())) {
|
(_dummyAvatar.getFaceModelURL() != _controlledAvatar->getFaceModelURL())) {
|
||||||
// FIXME
|
// FIXME
|
||||||
//myAvatar->setFaceModelURL(_dummyAvatar.getFaceModelURL());
|
//myAvatar->setFaceModelURL(_dummyAvatar.getFaceModelURL());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_useSkeletonModel && _dummyAvatar.getSkeletonModelURL().isValid() &&
|
if (_useSkeletonModel && _dummyAvatar.getSkeletonModelURL().isValid() &&
|
||||||
(_dummyAvatar.getSkeletonModelURL() != myAvatar->getSkeletonModelURL())) {
|
(_dummyAvatar.getSkeletonModelURL() != _controlledAvatar->getSkeletonModelURL())) {
|
||||||
// FIXME
|
// FIXME
|
||||||
//myAvatar->useFullAvatarURL()
|
//myAvatar->useFullAvatarURL()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_useDisplayName && _dummyAvatar.getDisplayName() != myAvatar->getDisplayName()) {
|
if (_useDisplayName && _dummyAvatar.getDisplayName() != _controlledAvatar->getDisplayName()) {
|
||||||
myAvatar->setDisplayName(_dummyAvatar.getDisplayName());
|
_controlledAvatar->setDisplayName(_dummyAvatar.getDisplayName());
|
||||||
}
|
}
|
||||||
|
|
||||||
myAvatar->setPosition(_dummyAvatar.getPosition());
|
_controlledAvatar->setPosition(_dummyAvatar.getPosition());
|
||||||
myAvatar->setOrientation(_dummyAvatar.getOrientation());
|
_controlledAvatar->setOrientation(_dummyAvatar.getOrientation());
|
||||||
|
|
||||||
// FIXME attachments
|
// FIXME attachments
|
||||||
// FIXME joints
|
// FIXME joints
|
||||||
|
@ -253,6 +269,6 @@ void RecordingScriptingInterface::processAudioInput(const QByteArray& audio) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordingScriptingInterface::processAudioFrame(const recording::FrameConstPointer& frame) {
|
void RecordingScriptingInterface::processAudioFrame(const recording::FrameConstPointer& frame) {
|
||||||
auto audioClient = DependencyManager::get<AudioClient>();
|
// auto audioClient = DependencyManager::get<AudioClient>();
|
||||||
audioClient->handleRecordedAudioInput(frame->data);
|
// audioClient->handleRecordedAudioInput(frame->data);
|
||||||
}
|
}
|
|
@ -24,13 +24,20 @@ class RecordingScriptingInterface : public QObject, public Dependency {
|
||||||
public:
|
public:
|
||||||
RecordingScriptingInterface();
|
RecordingScriptingInterface();
|
||||||
|
|
||||||
|
void setControlledAvatar(AvatarData* avatar);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool isPlaying();
|
|
||||||
bool isPaused();
|
|
||||||
float playerElapsed();
|
|
||||||
float playerLength();
|
|
||||||
void loadRecording(const QString& filename);
|
void loadRecording(const QString& filename);
|
||||||
|
|
||||||
void startPlaying();
|
void startPlaying();
|
||||||
|
void pausePlayer();
|
||||||
|
void stopPlaying();
|
||||||
|
bool isPlaying() const;
|
||||||
|
bool isPaused() const;
|
||||||
|
|
||||||
|
float playerElapsed() const;
|
||||||
|
float playerLength() const;
|
||||||
|
|
||||||
void setPlayerVolume(float volume);
|
void setPlayerVolume(float volume);
|
||||||
void setPlayerAudioOffset(float audioOffset);
|
void setPlayerAudioOffset(float audioOffset);
|
||||||
void setPlayerTime(float time);
|
void setPlayerTime(float time);
|
||||||
|
@ -40,13 +47,13 @@ public slots:
|
||||||
void setPlayerUseAttachments(bool useAttachments);
|
void setPlayerUseAttachments(bool useAttachments);
|
||||||
void setPlayerUseHeadModel(bool useHeadModel);
|
void setPlayerUseHeadModel(bool useHeadModel);
|
||||||
void setPlayerUseSkeletonModel(bool useSkeletonModel);
|
void setPlayerUseSkeletonModel(bool useSkeletonModel);
|
||||||
void play();
|
|
||||||
void pausePlayer();
|
|
||||||
void stopPlaying();
|
|
||||||
bool isRecording();
|
|
||||||
float recorderElapsed();
|
|
||||||
void startRecording();
|
void startRecording();
|
||||||
void stopRecording();
|
void stopRecording();
|
||||||
|
bool isRecording() const;
|
||||||
|
|
||||||
|
float recorderElapsed() const;
|
||||||
|
|
||||||
void saveRecording(const QString& filename);
|
void saveRecording(const QString& filename);
|
||||||
void loadLastRecording();
|
void loadLastRecording();
|
||||||
|
|
||||||
|
@ -74,6 +81,7 @@ private:
|
||||||
Flag _useSkeletonModel { false };
|
Flag _useSkeletonModel { false };
|
||||||
recording::ClipPointer _lastClip;
|
recording::ClipPointer _lastClip;
|
||||||
AvatarData _dummyAvatar;
|
AvatarData _dummyAvatar;
|
||||||
|
AvatarData* _controlledAvatar;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_RecordingScriptingInterface_h
|
#endif // hifi_RecordingScriptingInterface_h
|
|
@ -47,6 +47,7 @@
|
||||||
#include "WebSocketClass.h"
|
#include "WebSocketClass.h"
|
||||||
|
|
||||||
#include "SceneScriptingInterface.h"
|
#include "SceneScriptingInterface.h"
|
||||||
|
#include "RecordingScriptingInterface.h"
|
||||||
|
|
||||||
#include "MIDIEvent.h"
|
#include "MIDIEvent.h"
|
||||||
|
|
||||||
|
@ -377,6 +378,9 @@ void ScriptEngine::init() {
|
||||||
auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
|
auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
|
||||||
registerGlobalObject("Controller", scriptingInterface.data());
|
registerGlobalObject("Controller", scriptingInterface.data());
|
||||||
UserInputMapper::registerControllerTypes(this);
|
UserInputMapper::registerControllerTypes(this);
|
||||||
|
|
||||||
|
auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();
|
||||||
|
registerGlobalObject("Recording", recordingInterface.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
|
void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
|
||||||
|
|
Loading…
Reference in a new issue