mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-05 06:36:21 +02:00
Merge remote-tracking branch 'upstream/master' into android_audio_fixes3
This commit is contained in:
commit
6e793a06a9
555 changed files with 14000 additions and 8427 deletions
|
@ -15,6 +15,7 @@ include("cmake/compiler.cmake")
|
|||
|
||||
if (BUILD_SCRIBE_ONLY)
|
||||
add_subdirectory(tools/scribe)
|
||||
add_subdirectory(tools/shader_reflect)
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ android {
|
|||
'-DANDROID_STL=c++_shared',
|
||||
'-DQT_CMAKE_PREFIX_PATH=' + HIFI_ANDROID_PRECOMPILED + '/qt/lib/cmake',
|
||||
'-DNATIVE_SCRIBE=' + HIFI_ANDROID_PRECOMPILED + '/scribe' + EXEC_SUFFIX,
|
||||
'-DNATIVE_SHREFLECT=' + HIFI_ANDROID_PRECOMPILED + '/shreflect' + EXEC_SUFFIX,
|
||||
'-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED,
|
||||
'-DRELEASE_NUMBER=' + RELEASE_NUMBER,
|
||||
'-DRELEASE_TYPE=' + RELEASE_TYPE,
|
||||
|
|
|
@ -164,18 +164,29 @@ def packages = [
|
|||
|
||||
|
||||
def scribeLocalFile='scribe' + EXEC_SUFFIX
|
||||
|
||||
def scribeFile='scribe_linux_x86_64'
|
||||
def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6'
|
||||
def scribeVersion='u_iTrJDaE95i2abTPXOpPZckGBIim53G'
|
||||
|
||||
def shreflectLocalFile='shreflect' + EXEC_SUFFIX
|
||||
def shreflectFile='shreflect_linux_x86_64'
|
||||
def shreflectChecksum='d6094a8580066c0b6f4e80b5adfb1d98'
|
||||
def shreflectVersion='jnrpudh6fptIg6T2.Z6fgKP2ultAdKmE'
|
||||
|
||||
if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
scribeFile = 'scribe_osx_x86_64'
|
||||
scribeChecksum='72db9d32d4e1e50add755570ac5eb749'
|
||||
scribeVersion='DAW0DmnjCRib4MD8x93bgc2Z2MpPojZC'
|
||||
shreflectFile='shreflect_osx_x86_64'
|
||||
shreflectChecksum='d613ef0703c21371fee93fd2e54b964f'
|
||||
shreflectVersion='.rYNzjSFq6WtWDnE5KIKRIAGyJtr__ad'
|
||||
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
scribeFile = 'scribe_win32_x86_64.exe'
|
||||
scribeChecksum='678e43d290c90fda670c6fefe038a06d'
|
||||
scribeVersion='PuullrA_bPlO9kXZRt8rLe536X1UI.m7'
|
||||
shreflectFile='shreflect_win32_x86_64.exe'
|
||||
shreflectChecksum='6f4a77b8cceb3f1bbc655132c3665060'
|
||||
shreflectVersion='iIyCyza1nelkbI7ihybF59bBlwrfAC3D'
|
||||
}
|
||||
|
||||
def options = [
|
||||
|
@ -450,11 +461,27 @@ task fixScribePermissions(type: Exec, dependsOn: verifyScribe) {
|
|||
commandLine 'chmod', 'a+x', HIFI_ANDROID_PRECOMPILED + '/' + scribeLocalFile
|
||||
}
|
||||
|
||||
task setupScribe(dependsOn: verifyScribe) { }
|
||||
task downloadShreflect(type: Download) {
|
||||
src baseUrl + shreflectFile + '?versionId=' + shreflectVersion
|
||||
dest new File(baseFolder, shreflectLocalFile)
|
||||
onlyIfNewer true
|
||||
}
|
||||
|
||||
task verifyShreflect(type: Verify, dependsOn: downloadShreflect) {
|
||||
src new File(baseFolder, shreflectLocalFile);
|
||||
checksum shreflectChecksum
|
||||
}
|
||||
|
||||
task fixShreflectPermissions(type: Exec, dependsOn: verifyShreflect) {
|
||||
commandLine 'chmod', 'a+x', HIFI_ANDROID_PRECOMPILED + '/' + shreflectLocalFile
|
||||
}
|
||||
|
||||
task setupScribe(dependsOn: [verifyScribe, verifyShreflect]) { }
|
||||
|
||||
// On Windows, we don't need to set the executable bit, but on OSX and Unix we do
|
||||
if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
setupScribe.dependsOn fixScribePermissions
|
||||
setupScribe.dependsOn fixShreflectPermissions
|
||||
}
|
||||
|
||||
task extractGvrBinaries(dependsOn: extractDependencies) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QThread>
|
||||
|
||||
#include <AnimationCacheScriptingInterface.h>
|
||||
#include <AssetClient.h>
|
||||
#include <AvatarHashMap.h>
|
||||
#include <AudioInjectorManager.h>
|
||||
|
@ -32,6 +33,7 @@
|
|||
#include <ResourceCache.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <SoundCacheScriptingInterface.h>
|
||||
#include <SoundCache.h>
|
||||
#include <UsersScriptingInterface.h>
|
||||
#include <UUID.h>
|
||||
|
@ -71,6 +73,7 @@ Agent::Agent(ReceivedMessage& message) :
|
|||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<SoundCacheScriptingInterface>();
|
||||
DependencyManager::set<AudioScriptingInterface>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
|
||||
|
@ -365,7 +368,6 @@ void Agent::executeScript() {
|
|||
// give scripts access to the Users object
|
||||
_scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
|
||||
|
||||
|
||||
auto player = DependencyManager::get<recording::Deck>();
|
||||
connect(player.data(), &recording::Deck::playbackStateChanged, [=] {
|
||||
if (player->isPlaying()) {
|
||||
|
@ -453,8 +455,8 @@ void Agent::executeScript() {
|
|||
// register ourselves to the script engine
|
||||
_scriptEngine->registerGlobalObject("Agent", new AgentScriptingInterface(this));
|
||||
|
||||
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
_scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
_scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCacheScriptingInterface>().data());
|
||||
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
|
||||
QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor);
|
||||
_scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||
|
@ -824,10 +826,6 @@ void Agent::processAgentAvatarAudio() {
|
|||
void Agent::aboutToFinish() {
|
||||
setIsAvatar(false);// will stop timers for sending identity packets
|
||||
|
||||
if (_scriptEngine) {
|
||||
_scriptEngine->stop();
|
||||
}
|
||||
|
||||
// our entity tree is going to go away so tell that to the EntityScriptingInterface
|
||||
DependencyManager::get<EntityScriptingInterface>()->setEntityTree(nullptr);
|
||||
|
||||
|
@ -840,9 +838,9 @@ void Agent::aboutToFinish() {
|
|||
|
||||
// destroy all other created dependencies
|
||||
DependencyManager::destroy<ScriptCache>();
|
||||
DependencyManager::destroy<ScriptEngines>();
|
||||
|
||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
DependencyManager::destroy<AudioScriptingInterface>();
|
||||
|
||||
|
@ -858,3 +856,11 @@ void Agent::aboutToFinish() {
|
|||
_encoder = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::stop() {
|
||||
if (_scriptEngine) {
|
||||
_scriptEngine->stop();
|
||||
} else {
|
||||
setFinished(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ public slots:
|
|||
void setIsAvatar(bool isAvatar);
|
||||
bool isAvatar() const { return _isAvatar; }
|
||||
|
||||
Q_INVOKABLE virtual void stop() override;
|
||||
|
||||
private slots:
|
||||
void requestScript();
|
||||
void scriptRequestFinished();
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <shared/QtHelpers.h>
|
||||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <AnimationCacheScriptingInterface.h>
|
||||
#include <Assignment.h>
|
||||
#include <AvatarHashMap.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
|
@ -63,6 +64,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
|
|||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);
|
||||
|
||||
auto animationCache = DependencyManager::set<AnimationCache>();
|
||||
DependencyManager::set<AnimationCacheScriptingInterface>();
|
||||
auto entityScriptingInterface = DependencyManager::set<EntityScriptingInterface>(false);
|
||||
|
||||
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, AssignmentDynamicFactory>();
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "AssignmentClientApp.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QtCore/QCommandLineParser>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QStandardPaths>
|
||||
|
@ -42,9 +44,8 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
// parse command-line
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Assignment Client");
|
||||
parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
const QCommandLineOption versionOption = parser.addVersionOption();
|
||||
|
||||
QString typeDescription = "run single assignment client of given type\n# | Type\n============================";
|
||||
for (Assignment::Type type = Assignment::FirstType;
|
||||
|
@ -97,11 +98,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
|||
parser.addOption(parentPIDOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qCritical() << parser.errorText() << endl;
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
parser.showVersion();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(helpOption)) {
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
#include "AssignmentClientChildData.h"
|
||||
#include "SharedUtil.h"
|
||||
#include <QtCore/QJsonDocument>
|
||||
#ifdef _POSIX_SOURCE
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
const QString ASSIGNMENT_CLIENT_MONITOR_TARGET_NAME = "assignment-client-monitor";
|
||||
const int WAIT_FOR_CHILD_MSECS = 1000;
|
||||
|
@ -71,6 +74,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
|
|||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AssignmentClientStatus, this, "handleChildStatusPacket");
|
||||
|
||||
adjustOSResources(std::max(_numAssignmentClientForks, _maxAssignmentClientForks));
|
||||
// use QProcess to fork off a process for each of the child assignment clients
|
||||
for (unsigned int i = 0; i < _numAssignmentClientForks; i++) {
|
||||
spawnChildClient();
|
||||
|
@ -372,3 +376,27 @@ bool AssignmentClientMonitor::handleHTTPRequest(HTTPConnection* connection, cons
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssignmentClientMonitor::adjustOSResources(unsigned int numForks) const
|
||||
{
|
||||
#ifdef _POSIX_SOURCE
|
||||
// QProcess on Unix uses six (I think) descriptors, some temporarily, for each child proc.
|
||||
// Formula based on tests with a Ubuntu 16.04 VM.
|
||||
unsigned requiredDescriptors = 30 + 6 * numForks;
|
||||
struct rlimit descLimits;
|
||||
if (getrlimit(RLIMIT_NOFILE, &descLimits) == 0) {
|
||||
if (descLimits.rlim_cur < requiredDescriptors) {
|
||||
descLimits.rlim_cur = requiredDescriptors;
|
||||
if (setrlimit(RLIMIT_NOFILE, &descLimits) == 0) {
|
||||
qDebug() << "Resetting descriptor limit to" << requiredDescriptors;
|
||||
} else {
|
||||
const char *const errorString = strerror(errno);
|
||||
qDebug() << "Failed to reset descriptor limit to" << requiredDescriptors << ":" << errorString;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char *const errorString = strerror(errno);
|
||||
qDebug() << "Failed to read descriptor limit:" << errorString;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ public slots:
|
|||
private:
|
||||
void spawnChildClient();
|
||||
void simultaneousWaitOnChildren(int waitMsecs);
|
||||
void adjustOSResources(unsigned int numForks) const;
|
||||
|
||||
QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include "EntityServer.h"
|
||||
|
||||
|
||||
EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) :
|
||||
OctreeSendThread(myServer, node)
|
||||
{
|
||||
|
@ -100,7 +99,7 @@ void EntityTreeSendThread::preDistributionProcessing() {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool viewFrustumChanged, bool isFullScene) {
|
||||
if (viewFrustumChanged || _traversal.finished()) {
|
||||
EntityTreeElementPointer root = std::dynamic_pointer_cast<EntityTreeElement>(_myServer->getOctree()->getRoot());
|
||||
|
@ -111,7 +110,7 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
|||
|
||||
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
|
||||
|
||||
|
||||
startNewTraversal(newView, root);
|
||||
|
||||
// When the viewFrustum changed the sort order may be incorrect, so we re-sort
|
||||
|
@ -156,7 +155,20 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
|||
OctreeServer::trackTreeTraverseTime((float)(usecTimestampNow() - startTime));
|
||||
}
|
||||
|
||||
OctreeSendThread::traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
|
||||
bool sendComplete = OctreeSendThread::traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
|
||||
|
||||
if (sendComplete && nodeData->wantReportInitialCompletion() && _traversal.finished()) {
|
||||
// Dealt with all nearby entities.
|
||||
nodeData->setReportInitialCompletion(false);
|
||||
|
||||
// Send EntityQueryInitialResultsComplete reliable packet ...
|
||||
auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete,
|
||||
sizeof(OCTREE_PACKET_SEQUENCE), true);
|
||||
initialCompletion->writePrimitive(OCTREE_PACKET_SEQUENCE(nodeData->getSequenceNumber() - 1U));
|
||||
DependencyManager::get<NodeList>()->sendPacket(std::move(initialCompletion), *node);
|
||||
}
|
||||
|
||||
return sendComplete;
|
||||
}
|
||||
|
||||
bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID,
|
||||
|
@ -301,6 +313,7 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
|||
|
||||
bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) {
|
||||
if (_sendQueue.empty()) {
|
||||
params.stopReason = EncodeBitstreamParams::FINISHED;
|
||||
OctreeServer::trackEncodeTime(OctreeServer::SKIP_TIME);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public:
|
|||
EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node);
|
||||
|
||||
protected:
|
||||
void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool viewFrustumChanged, bool isFullScene) override;
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -211,10 +211,9 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
//_totalWastedBytes += 0;
|
||||
_trueBytesSent += numBytes;
|
||||
numPackets++;
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
if (debug) {
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
|
||||
|
||||
OCTREE_PACKET_SEQUENCE sequence;
|
||||
|
@ -231,9 +230,9 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
|
||||
// second packet
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *node);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(sentPacket, *node);
|
||||
|
||||
numBytes = nodeData->getPacket().getDataSize();
|
||||
numBytes = sentPacket.getDataSize();
|
||||
_totalBytes += numBytes;
|
||||
_totalPackets++;
|
||||
// we count wasted bytes here because we were unable to fit the stats packet
|
||||
|
@ -243,8 +242,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
numPackets++;
|
||||
|
||||
if (debug) {
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
|
||||
|
||||
OCTREE_PACKET_SEQUENCE sequence;
|
||||
|
@ -265,9 +262,10 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) {
|
||||
// just send the octree packet
|
||||
OctreeServer::didCallWriteDatagram(this);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(nodeData->getPacket(), *node);
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(sentPacket, *node);
|
||||
|
||||
int numBytes = nodeData->getPacket().getDataSize();
|
||||
int numBytes = sentPacket.getDataSize();
|
||||
_totalBytes += numBytes;
|
||||
_totalPackets++;
|
||||
int thisWastedBytes = udt::MAX_PACKET_SIZE - numBytes;
|
||||
|
@ -276,8 +274,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
|
|||
_trueBytesSent += numBytes;
|
||||
|
||||
if (debug) {
|
||||
NLPacket& sentPacket = nodeData->getPacket();
|
||||
|
||||
sentPacket.seek(sizeof(OCTREE_PACKET_FLAGS));
|
||||
|
||||
OCTREE_PACKET_SEQUENCE sequence;
|
||||
|
@ -434,7 +430,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
|||
return _truePacketsSent;
|
||||
}
|
||||
|
||||
void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) {
|
||||
bool OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) {
|
||||
// calculate max number of packets that can be sent during this interval
|
||||
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
|
||||
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
|
||||
|
@ -517,4 +513,6 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
|
|||
<< " maxPacketsPerInterval = " << maxPacketsPerInterval
|
||||
<< " clientMaxPacketsPerInterval = " << clientMaxPacketsPerInterval;
|
||||
}
|
||||
|
||||
return params.stopReason == EncodeBitstreamParams::FINISHED;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ protected:
|
|||
/// Implements generic processing behavior for this thread.
|
||||
virtual bool process() override;
|
||||
|
||||
virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
virtual bool traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
|
||||
bool viewFrustumChanged, bool isFullScene);
|
||||
virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) = 0;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <ResourceManager.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <ScriptEngines.h>
|
||||
#include <SoundCache.h>
|
||||
#include <SoundCacheScriptingInterface.h>
|
||||
#include <UUID.h>
|
||||
#include <WebSocketServerClass.h>
|
||||
|
||||
|
@ -66,6 +66,7 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig
|
|||
|
||||
DependencyManager::set<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<SoundCacheScriptingInterface>();
|
||||
DependencyManager::set<AudioInjectorManager>();
|
||||
|
||||
DependencyManager::set<ScriptCache>();
|
||||
|
@ -438,7 +439,7 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
|
|||
auto webSocketServerConstructorValue = newEngine->newFunction(WebSocketServerClass::constructor);
|
||||
newEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||
|
||||
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
newEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
|
||||
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>().data();
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
message( FATAL_ERROR "Only 64 bit builds supported." )
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (NOT "${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
message( FATAL_ERROR "Only 64 bit builds supported." )
|
||||
endif()
|
||||
|
||||
add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
if (NOT WINDOW_SDK_PATH)
|
||||
|
@ -52,32 +54,27 @@ endif()
|
|||
|
||||
if (ANDROID)
|
||||
# assume that the toolchain selected for android has C++11 support
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
|
||||
elseif(APPLE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
|
||||
if (CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "YES")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] "YES")
|
||||
endif()
|
||||
elseif ((NOT MSVC12) AND (NOT MSVC14))
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
|
||||
if (COMPILER_SUPPORTS_CXX11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif(COMPILER_SUPPORTS_CXX0X)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
|
||||
if (COMPILER_SUPPORTS_CXX14)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
|
||||
else()
|
||||
message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "YES")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] "dwarf-with-dsym")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_DEPLOYMENT_POSTPROCESSING[variant=Release] "YES")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --stdlib=libc++")
|
||||
endif ()
|
||||
|
||||
if (NOT ANDROID_LIB_DIR)
|
||||
set(ANDROID_LIB_DIR $ENV{ANDROID_LIB_DIR})
|
||||
|
@ -110,4 +107,4 @@ if (APPLE)
|
|||
# set that as the SDK to use
|
||||
set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
|
22
cmake/externals/json/CMakeLists.txt
vendored
Normal file
22
cmake/externals/json/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
set(EXTERNAL_NAME json)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/json_3.1.2.zip
|
||||
URL_MD5 94dbf6ea25a7569ddc0ab6e20862cf16
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> ${EXTERNAL_ARGS}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${SOURCE_DIR} CACHE PATH "List of json include directories")
|
|
@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC69.zip
|
||||
URL_MD5 e2467b08de069da7e22ec8e032435592
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC70v2.zip
|
||||
URL_MD5 35fcc8e635e71d0b00a08455a2582448
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
function(global_append varName varValue)
|
||||
get_property(LOCAL_LIST GLOBAL PROPERTY ${varName})
|
||||
list(APPEND LOCAL_LIST ${varValue})
|
||||
set_property(GLOBAL PROPERTY ${varName} ${LOCAL_LIST})
|
||||
endfunction()
|
||||
|
||||
function(AUTOSCRIBE_SHADER SHADER_FILE)
|
||||
# Grab include files
|
||||
foreach(includeFile ${ARGN})
|
||||
|
@ -45,11 +51,8 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
|
|||
elseif(${SHADER_EXT} STREQUAL .slg)
|
||||
set(SHADER_TYPE geom)
|
||||
endif()
|
||||
set(SHADER_TARGET ${SHADER_TARGET}_${SHADER_TYPE})
|
||||
|
||||
set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}")
|
||||
set(SHADER_TARGET_HEADER ${SHADER_TARGET}.h)
|
||||
set(SHADER_TARGET_SOURCE ${SHADER_TARGET}.cpp)
|
||||
file(MAKE_DIRECTORY "${SHADERS_DIR}/${SHADER_LIB}")
|
||||
set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_LIB}/${SHADER_TARGET}.${SHADER_TYPE}")
|
||||
set(SCRIBE_COMMAND scribe)
|
||||
|
||||
# Target dependant Custom rule on the SHADER_FILE
|
||||
|
@ -63,65 +66,232 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
|
|||
else ()
|
||||
set(GLPROFILE PC_GL)
|
||||
endif()
|
||||
set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
|
||||
set(SCRIBE_ARGS -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
|
||||
add_custom_command(
|
||||
OUTPUT ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE}
|
||||
OUTPUT ${SHADER_TARGET}
|
||||
COMMAND ${SCRIBE_COMMAND} ${SCRIBE_ARGS}
|
||||
DEPENDS ${SCRIBE_COMMAND} ${SHADER_INCLUDE_FILES} ${SHADER_FILE}
|
||||
DEPENDS ${SHADER_FILE} ${SCRIBE_COMMAND} ${SHADER_INCLUDE_FILES}
|
||||
)
|
||||
|
||||
#output the generated file name
|
||||
set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} PARENT_SCOPE)
|
||||
|
||||
file(GLOB INCLUDE_FILES ${SHADER_TARGET_HEADER})
|
||||
|
||||
set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
macro(AUTOSCRIBE_APPEND_SHADER_SOURCES)
|
||||
if (NOT("${ARGN}" STREQUAL ""))
|
||||
set_property(GLOBAL PROPERTY ${TARGET_NAME}_SHADER_SOURCES "${ARGN}")
|
||||
global_append(GLOBAL_SHADER_LIBS ${TARGET_NAME})
|
||||
global_append(GLOBAL_SHADER_SOURCES "${ARGN}")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(AUTOSCRIBE_SHADER_LIB)
|
||||
set(HIFI_LIBRARIES_SHADER_INCLUDE_FILES "")
|
||||
unset(HIFI_LIBRARIES_SHADER_INCLUDE_FILES)
|
||||
set(SRC_FOLDER "${CMAKE_SOURCE_DIR}/libraries/${TARGET_NAME}/src")
|
||||
|
||||
file(GLOB_RECURSE SHADER_INCLUDE_FILES ${SRC_FOLDER}/*.slh)
|
||||
file(GLOB_RECURSE SHADER_VERTEX_FILES ${SRC_FOLDER}/*.slv)
|
||||
file(GLOB_RECURSE SHADER_FRAGMENT_FILES ${SRC_FOLDER}/*.slf)
|
||||
file(GLOB_RECURSE SHADER_GEOMETRY_FILES ${SRC_FOLDER}/*.slg)
|
||||
file(GLOB_RECURSE SHADER_COMPUTE_FILES ${SRC_FOLDER}/*.slc)
|
||||
|
||||
unset(SHADER_SOURCE_FILES)
|
||||
list(APPEND SHADER_SOURCE_FILES ${SHADER_VERTEX_FILES})
|
||||
list(APPEND SHADER_SOURCE_FILES ${SHADER_FRAGMENT_FILES})
|
||||
list(APPEND SHADER_SOURCE_FILES ${SHADER_GEOMETRY_FILES})
|
||||
list(APPEND SHADER_SOURCE_FILES ${SHADER_COMPUTE_FILES})
|
||||
|
||||
unset(LOCAL_SHADER_SOURCES)
|
||||
list(APPEND LOCAL_SHADER_SOURCES ${SHADER_SOURCE_FILES})
|
||||
list(APPEND LOCAL_SHADER_SOURCES ${SHADER_INCLUDE_FILES})
|
||||
|
||||
AUTOSCRIBE_APPEND_SHADER_SOURCES(${LOCAL_SHADER_SOURCES})
|
||||
|
||||
file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}")
|
||||
foreach(HIFI_LIBRARY ${ARGN})
|
||||
#if (NOT TARGET ${HIFI_LIBRARY})
|
||||
# file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${RELATIVE_LIBRARY_DIR_PATH}/${HIFI_LIBRARY}/src/)
|
||||
#endif ()
|
||||
|
||||
#file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src/*.slh)
|
||||
list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src)
|
||||
endforeach()
|
||||
#message("${TARGET_NAME} ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}")
|
||||
endmacro()
|
||||
|
||||
file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh)
|
||||
file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf src/*.slg)
|
||||
macro(AUTOSCRIBE_PROGRAM)
|
||||
set(oneValueArgs NAME VERTEX FRAGMENT GEOMETRY COMPUTE)
|
||||
cmake_parse_arguments(AUTOSCRIBE_PROGRAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
if (NOT (DEFINED AUTOSCRIBE_PROGRAM_NAME))
|
||||
message(FATAL_ERROR "Programs must have a name and both a vertex and fragment shader")
|
||||
endif()
|
||||
if (NOT (DEFINED AUTOSCRIBE_PROGRAM_VERTEX))
|
||||
set(AUTOSCRIBE_PROGRAM_VERTEX ${AUTOSCRIBE_PROGRAM_NAME})
|
||||
endif()
|
||||
if (NOT (DEFINED AUTOSCRIBE_PROGRAM_FRAGMENT))
|
||||
set(AUTOSCRIBE_PROGRAM_FRAGMENT ${AUTOSCRIBE_PROGRAM_NAME})
|
||||
endif()
|
||||
|
||||
#make the shader folder
|
||||
set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}")
|
||||
file(MAKE_DIRECTORY ${SHADERS_DIR})
|
||||
if (NOT (${AUTOSCRIBE_PROGRAM_VERTEX} MATCHES ".*::.*"))
|
||||
set(AUTOSCRIBE_PROGRAM_VERTEX "vertex::${AUTOSCRIBE_PROGRAM_VERTEX}")
|
||||
endif()
|
||||
if (NOT (${AUTOSCRIBE_PROGRAM_FRAGMENT} MATCHES ".*::.*"))
|
||||
set(AUTOSCRIBE_PROGRAM_FRAGMENT "fragment::${AUTOSCRIBE_PROGRAM_FRAGMENT}")
|
||||
endif()
|
||||
|
||||
#message("${TARGET_NAME} ${SHADER_INCLUDE_FILES}")
|
||||
set(AUTOSCRIBE_SHADER_SRC "")
|
||||
foreach(SHADER_FILE ${SHADER_SOURCE_FILES})
|
||||
AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES})
|
||||
unset(AUTOSCRIBE_PROGRAM_MAP)
|
||||
list(APPEND AUTOSCRIBE_PROGRAM_MAP AUTOSCRIBE_PROGRAM_VERTEX)
|
||||
list(APPEND AUTOSCRIBE_PROGRAM_MAP ${AUTOSCRIBE_PROGRAM_VERTEX})
|
||||
list(APPEND AUTOSCRIBE_PROGRAM_MAP AUTOSCRIBE_PROGRAM_FRAGMENT)
|
||||
list(APPEND AUTOSCRIBE_PROGRAM_MAP ${AUTOSCRIBE_PROGRAM_FRAGMENT})
|
||||
global_append(${TARGET_NAME}_PROGRAMS ${AUTOSCRIBE_PROGRAM_NAME})
|
||||
set_property(GLOBAL PROPERTY ${AUTOSCRIBE_PROGRAM_NAME} "${AUTOSCRIBE_PROGRAM_MAP}")
|
||||
endmacro()
|
||||
|
||||
macro(unpack_map)
|
||||
set(MAP_VAR "${ARGN}")
|
||||
list(LENGTH MAP_VAR MAP_SIZE)
|
||||
MATH(EXPR MAP_ENTRY_RANGE "(${MAP_SIZE} / 2) - 1")
|
||||
foreach(INDEX RANGE ${MAP_ENTRY_RANGE})
|
||||
MATH(EXPR INDEX_NAME "${INDEX} * 2")
|
||||
MATH(EXPR INDEX_VAL "${INDEX_NAME} + 1")
|
||||
list(GET MAP_VAR ${INDEX_NAME} VAR_NAME)
|
||||
list(GET MAP_VAR ${INDEX_VAL} VAR_VAL)
|
||||
set(${VAR_NAME} ${VAR_VAL})
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
macro(PROCESS_SHADER_FILE)
|
||||
AUTOSCRIBE_SHADER(${SHADER} ${ALL_SHADER_HEADERS} ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES})
|
||||
file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE)
|
||||
set_property(SOURCE ${AUTOSCRIBE_GENERATED_FILE} PROPERTY SKIP_AUTOMOC ON)
|
||||
list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE})
|
||||
endforeach()
|
||||
#message(${TARGET_NAME} ${AUTOSCRIBE_SHADER_SRC})
|
||||
|
||||
if (WIN32)
|
||||
source_group("Shaders" FILES ${SHADER_INCLUDE_FILES})
|
||||
source_group("Shaders" FILES ${SHADER_SOURCE_FILES})
|
||||
source_group("Shaders\\generated" FILES ${AUTOSCRIBE_SHADER_SRC})
|
||||
endif()
|
||||
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_INCLUDE_FILES})
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_SOURCE_FILES})
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${AUTOSCRIBE_SHADER_SRC})
|
||||
|
||||
# Link library shaders, if they exist
|
||||
include_directories("${SHADERS_DIR}")
|
||||
|
||||
# Add search directory to find gpu/Shader.h
|
||||
include_directories("${HIFI_LIBRARY_DIR}/gpu/src")
|
||||
|
||||
source_group("Compiled/${SHADER_LIB}" FILES ${AUTOSCRIBE_GENERATED_FILE})
|
||||
set(REFLECTED_SHADER "${AUTOSCRIBE_GENERATED_FILE}.json")
|
||||
source_group("Reflected/${SHADER_LIB}" FILES ${REFLECTED_SHADER})
|
||||
list(APPEND COMPILED_SHADERS ${AUTOSCRIBE_GENERATED_FILE})
|
||||
get_filename_component(ENUM_NAME ${SHADER} NAME_WE)
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "${ENUM_NAME} = ${SHADER_COUNT},\n")
|
||||
MATH(EXPR SHADER_COUNT "${SHADER_COUNT}+1")
|
||||
endmacro()
|
||||
|
||||
macro(AUTOSCRIBE_SHADER_FINISH)
|
||||
get_property(GLOBAL_SHADER_LIBS GLOBAL PROPERTY GLOBAL_SHADER_LIBS)
|
||||
list(REMOVE_DUPLICATES GLOBAL_SHADER_LIBS)
|
||||
set(SHADER_COUNT 0)
|
||||
set(PROGRAM_COUNT 0)
|
||||
set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders")
|
||||
set(SHADER_ENUMS "")
|
||||
file(MAKE_DIRECTORY ${SHADERS_DIR})
|
||||
|
||||
unset(COMPILED_SHADERS)
|
||||
foreach(SHADER_LIB ${GLOBAL_SHADER_LIBS})
|
||||
get_property(LIB_SHADER_SOURCES GLOBAL PROPERTY ${SHADER_LIB}_SHADER_SOURCES)
|
||||
get_property(LIB_PROGRAMS GLOBAL PROPERTY ${SHADER_LIB}_PROGRAMS)
|
||||
list(REMOVE_DUPLICATES LIB_SHADER_SOURCES)
|
||||
string(REGEX REPLACE "[-]" "_" SHADER_NAMESPACE ${SHADER_LIB})
|
||||
list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES "${CMAKE_SOURCE_DIR}/libraries/${SHADER_LIB}/src")
|
||||
|
||||
unset(VERTEX_SHADERS)
|
||||
unset(FRAGMENT_SHADERS)
|
||||
unset(SHADER_HEADERS)
|
||||
|
||||
foreach(SHADER_FILE ${LIB_SHADER_SOURCES})
|
||||
if (SHADER_FILE MATCHES ".*\\.slv")
|
||||
list(APPEND VERTEX_SHADERS ${SHADER_FILE})
|
||||
elseif (SHADER_FILE MATCHES ".*\\.slf")
|
||||
list(APPEND FRAGMENT_SHADERS ${SHADER_FILE})
|
||||
elseif (SHADER_FILE MATCHES ".*\\.slh")
|
||||
list(APPEND SHADER_HEADERS ${SHADER_FILE})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (DEFINED SHADER_HEADERS)
|
||||
list(REMOVE_DUPLICATES SHADER_HEADERS)
|
||||
source_group("${SHADER_LIB}/Headers" FILES ${SHADER_HEADERS})
|
||||
list(APPEND ALL_SHADER_HEADERS ${SHADER_HEADERS})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${SHADER_HEADERS})
|
||||
endif()
|
||||
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace ${SHADER_NAMESPACE} {\n")
|
||||
if (DEFINED VERTEX_SHADERS)
|
||||
list(REMOVE_DUPLICATES VERTEX_SHADERS)
|
||||
source_group("${SHADER_LIB}/Vertex" FILES ${VERTEX_SHADERS})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${VERTEX_SHADERS})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace vertex { enum {\n")
|
||||
foreach(SHADER ${VERTEX_SHADERS})
|
||||
process_shader_file()
|
||||
endforeach()
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "}; } // vertex \n")
|
||||
endif()
|
||||
|
||||
if (DEFINED FRAGMENT_SHADERS)
|
||||
list(REMOVE_DUPLICATES FRAGMENT_SHADERS)
|
||||
source_group("${SHADER_LIB}/Fragment" FILES ${FRAGMENT_SHADERS})
|
||||
list(APPEND ALL_SCRIBE_SHADERS ${FRAGMENT_SHADERS})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace fragment { enum {\n")
|
||||
foreach(SHADER ${FRAGMENT_SHADERS})
|
||||
process_shader_file()
|
||||
endforeach()
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "}; } // fragment \n")
|
||||
endif()
|
||||
|
||||
if (DEFINED LIB_PROGRAMS)
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "namespace program { enum {\n")
|
||||
foreach(LIB_PROGRAM ${LIB_PROGRAMS})
|
||||
get_property(LIB_PROGRAM_MAP GLOBAL PROPERTY ${LIB_PROGRAM})
|
||||
unpack_map(${LIB_PROGRAM_MAP})
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "${LIB_PROGRAM} = (${AUTOSCRIBE_PROGRAM_VERTEX} << 16) | ${AUTOSCRIBE_PROGRAM_FRAGMENT},\n")
|
||||
MATH(EXPR PROGRAM_COUNT "${PROGRAM_COUNT}+1")
|
||||
list(APPEND SHADER_ALL_PROGRAMS "${SHADER_NAMESPACE}::program::${LIB_PROGRAM}")
|
||||
endforeach()
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "}; } // program \n")
|
||||
endif()
|
||||
string(CONCAT SHADER_ENUMS "${SHADER_ENUMS}" "} // namespace ${SHADER_NAMESPACE}\n")
|
||||
endforeach()
|
||||
|
||||
set(SHADER_PROGRAMS_ARRAY "")
|
||||
foreach(SHADER_PROGRAM ${SHADER_ALL_PROGRAMS})
|
||||
string(CONCAT SHADER_PROGRAMS_ARRAY "${SHADER_PROGRAMS_ARRAY}" " ${SHADER_PROGRAM},\n")
|
||||
endforeach()
|
||||
|
||||
set(SHREFLECT_COMMAND shreflect)
|
||||
set(SHREFLECT_DEPENDENCY shreflect)
|
||||
if (ANDROID)
|
||||
set(SHREFLECT_COMMAND ${NATIVE_SHREFLECT})
|
||||
unset(SHREFLECT_DEPENDENCY)
|
||||
endif()
|
||||
|
||||
set(SHADER_COUNT 0)
|
||||
foreach(COMPILED_SHADER ${COMPILED_SHADERS})
|
||||
set(REFLECTED_SHADER "${COMPILED_SHADER}.json")
|
||||
list(APPEND COMPILED_SHADER_REFLECTIONS ${REFLECTED_SHADER})
|
||||
string(CONCAT SHADER_QRC "${SHADER_QRC}" "<file alias=\"${SHADER_COUNT}\">${COMPILED_SHADER}</file>\n")
|
||||
string(CONCAT SHADER_QRC "${SHADER_QRC}" "<file alias=\"${SHADER_COUNT}_reflection\">${REFLECTED_SHADER}</file>\n")
|
||||
MATH(EXPR SHADER_COUNT "${SHADER_COUNT}+1")
|
||||
add_custom_command(
|
||||
OUTPUT ${REFLECTED_SHADER}
|
||||
COMMAND ${SHREFLECT_COMMAND} ${COMPILED_SHADER}
|
||||
DEPENDS ${SHREFLECT_DEPENDENCY} ${COMPILED_SHADER}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# Custom targets required to force generation of the shaders via scribe
|
||||
add_custom_target(scribe_shaders SOURCES ${ALL_SCRIBE_SHADERS})
|
||||
add_custom_target(compiled_shaders SOURCES ${COMPILED_SHADERS})
|
||||
add_custom_target(reflected_shaders SOURCES ${COMPILED_SHADER_REFLECTIONS})
|
||||
set_target_properties(scribe_shaders PROPERTIES FOLDER "Shaders")
|
||||
set_target_properties(compiled_shaders PROPERTIES FOLDER "Shaders")
|
||||
set_target_properties(reflected_shaders PROPERTIES FOLDER "Shaders")
|
||||
|
||||
configure_file(
|
||||
ShaderEnums.cpp.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.cpp
|
||||
)
|
||||
configure_file(
|
||||
ShaderEnums.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.h
|
||||
)
|
||||
configure_file(
|
||||
shaders.qrc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc
|
||||
)
|
||||
set(AUTOSCRIBE_SHADER_LIB_SRC "${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.h;${CMAKE_CURRENT_BINARY_DIR}/shaders/ShaderEnums.cpp")
|
||||
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${COMPILED_SHADERS})
|
||||
set(QT_RESOURCES_FILE ${CMAKE_CURRENT_BINARY_DIR}/shaders.qrc)
|
||||
get_property(GLOBAL_SHADER_SOURCES GLOBAL PROPERTY GLOBAL_SHADER_SOURCES)
|
||||
list(REMOVE_DUPLICATES GLOBAL_SHADER_SOURCES)
|
||||
endmacro()
|
||||
|
|
|
@ -19,12 +19,8 @@ function(LINK_HIFI_LIBRARIES)
|
|||
endforeach()
|
||||
|
||||
foreach(HIFI_LIBRARY ${LIBRARIES_TO_LINK})
|
||||
|
||||
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
|
||||
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}/shaders")
|
||||
|
||||
#add_dependencies(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||
|
||||
include_directories("${CMAKE_BINARY_DIR}/libraries/${HIFI_LIBRARY}")
|
||||
# link the actual library - it is static so don't bubble it up
|
||||
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||
endforeach()
|
||||
|
|
13
cmake/macros/TargetJson.cmake
Normal file
13
cmake/macros/TargetJson.cmake
Normal file
|
@ -0,0 +1,13 @@
|
|||
#
|
||||
# Created by Bradley Austin Davis on 2018/07/22
|
||||
# Copyright 2013-2018 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_JSON)
|
||||
add_dependency_external_projects(json)
|
||||
find_package(JSON REQUIRED)
|
||||
message("JSON_INCLUDE_DIRS ${JSON_INCLUDE_DIRS}")
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${JSON_INCLUDE_DIRS})
|
||||
endmacro()
|
19
cmake/modules/FindJSON.cmake
Normal file
19
cmake/modules/FindJSON.cmake
Normal file
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# Created by Bradley Austin Davis on 2018/07/22
|
||||
# Copyright 2013-2018 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
# setup hints for JSON search
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("json")
|
||||
|
||||
# locate header
|
||||
find_path(JSON_INCLUDE_DIRS "json/json.hpp" HINTS ${JSON_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(JSON DEFAULT_MSG JSON_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(JSON_INCLUDE_DIRS JSON_SEARCH_DIRS)
|
|
@ -46,6 +46,14 @@
|
|||
"default": "40102",
|
||||
"type": "int",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "enable_packet_verification",
|
||||
"label": "Enable Packet Verification",
|
||||
"help": "Enable secure checksums on communication that uses the High Fidelity protocol. Increases security with possibly a small performance penalty.",
|
||||
"default": true,
|
||||
"type": "checkbox",
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <iostream>
|
||||
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
|
@ -69,6 +70,14 @@ const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com";
|
|||
const QString ICE_SERVER_DEFAULT_HOSTNAME = "dev-ice.highfidelity.com";
|
||||
#endif
|
||||
|
||||
QString DomainServer::_iceServerAddr { ICE_SERVER_DEFAULT_HOSTNAME };
|
||||
int DomainServer::_iceServerPort { ICE_SERVER_DEFAULT_PORT };
|
||||
bool DomainServer::_overrideDomainID { false };
|
||||
QUuid DomainServer::_overridingDomainID;
|
||||
bool DomainServer::_getTempName { false };
|
||||
QString DomainServer::_userConfigFilename;
|
||||
int DomainServer::_parentPID { -1 };
|
||||
|
||||
bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
||||
const QString& metaversePath,
|
||||
const QString& requestSubobjectKey,
|
||||
|
@ -148,24 +157,13 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_gatekeeper(this),
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this),
|
||||
_allAssignments(),
|
||||
_unfulfilledAssignments(),
|
||||
_isUsingDTLS(false),
|
||||
_oauthProviderURL(),
|
||||
_oauthClientID(),
|
||||
_hostname(),
|
||||
_ephemeralACScripts(),
|
||||
_webAuthenticationStateSet(),
|
||||
_cookieSessionHash(),
|
||||
_automaticNetworkingSetting(),
|
||||
_settingsManager(),
|
||||
_iceServerAddr(ICE_SERVER_DEFAULT_HOSTNAME),
|
||||
_iceServerPort(ICE_SERVER_DEFAULT_PORT)
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this)
|
||||
{
|
||||
PathUtils::removeTemporaryApplicationDirs();
|
||||
if (_parentPID != -1) {
|
||||
watchParentProcess(_parentPID);
|
||||
}
|
||||
|
||||
parseCommandLine();
|
||||
PathUtils::removeTemporaryApplicationDirs();
|
||||
|
||||
DependencyManager::set<tracing::Tracer>();
|
||||
DependencyManager::set<StatTracker>();
|
||||
|
@ -185,9 +183,16 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
// (need this since domain-server can restart itself and maintain static variables)
|
||||
DependencyManager::set<AccountManager>();
|
||||
|
||||
auto args = arguments();
|
||||
|
||||
_settingsManager.setupConfigMap(args);
|
||||
// load the user config
|
||||
QString userConfigFilename;
|
||||
if (!_userConfigFilename.isEmpty()) {
|
||||
userConfigFilename = _userConfigFilename;
|
||||
} else {
|
||||
// we weren't passed a user config path
|
||||
static const QString USER_CONFIG_FILE_NAME = "config.json";
|
||||
userConfigFilename = PathUtils::getAppDataFilePath(USER_CONFIG_FILE_NAME);
|
||||
}
|
||||
_settingsManager.setupConfigMap(userConfigFilename);
|
||||
|
||||
// setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us
|
||||
#ifdef _WIN32
|
||||
|
@ -246,8 +251,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
}
|
||||
|
||||
// check for the temporary name parameter
|
||||
const QString GET_TEMPORARY_NAME_SWITCH = "--get-temp-name";
|
||||
if (args.contains(GET_TEMPORARY_NAME_SWITCH)) {
|
||||
if (_getTempName) {
|
||||
getTemporaryName();
|
||||
}
|
||||
|
||||
|
@ -316,28 +320,45 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
connect(_contentManager.get(), &DomainContentBackupManager::recoveryCompleted, this, &DomainServer::restart);
|
||||
}
|
||||
|
||||
void DomainServer::parseCommandLine() {
|
||||
void DomainServer::parseCommandLine(int argc, char* argv[]) {
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Domain Server");
|
||||
parser.addHelpOption();
|
||||
const QCommandLineOption versionOption = parser.addVersionOption();
|
||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||
|
||||
const QCommandLineOption iceServerAddressOption("i", "ice-server address", "IP:PORT or HOSTNAME:PORT");
|
||||
parser.addOption(iceServerAddressOption);
|
||||
|
||||
const QCommandLineOption domainIDOption("d", "domain-server uuid");
|
||||
const QCommandLineOption domainIDOption("d", "domain-server uuid", "uuid");
|
||||
parser.addOption(domainIDOption);
|
||||
|
||||
const QCommandLineOption getTempNameOption("get-temp-name", "Request a temporary domain-name");
|
||||
parser.addOption(getTempNameOption);
|
||||
|
||||
const QCommandLineOption masterConfigOption("master-config", "Deprecated config-file option");
|
||||
parser.addOption(masterConfigOption);
|
||||
const QCommandLineOption userConfigOption("user-config", "Pass user config file pass", "path");
|
||||
parser.addOption(userConfigOption);
|
||||
|
||||
const QCommandLineOption parentPIDOption(PARENT_PID_OPTION, "PID of the parent process", "parent-pid");
|
||||
parser.addOption(parentPIDOption);
|
||||
|
||||
if (!parser.parse(QCoreApplication::arguments())) {
|
||||
qWarning() << parser.errorText() << endl;
|
||||
|
||||
QStringList arguments;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
arguments << argv[i];
|
||||
}
|
||||
if (!parser.parse(arguments)) {
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
parser.showVersion();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
if (parser.isSet(helpOption)) {
|
||||
QCoreApplication mockApp(argc, argv); // required for call to showHelp()
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -354,7 +375,7 @@ void DomainServer::parseCommandLine() {
|
|||
|
||||
if (_iceServerAddr.isEmpty()) {
|
||||
qWarning() << "Could not parse an IP address and port combination from" << hostnamePortString;
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,14 +385,21 @@ void DomainServer::parseCommandLine() {
|
|||
qDebug() << "domain-server ID is" << _overridingDomainID;
|
||||
}
|
||||
|
||||
if (parser.isSet(getTempNameOption)) {
|
||||
_getTempName = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(userConfigOption)) {
|
||||
_userConfigFilename = parser.value(userConfigOption);
|
||||
}
|
||||
|
||||
if (parser.isSet(parentPIDOption)) {
|
||||
bool ok = false;
|
||||
int parentPID = parser.value(parentPIDOption).toInt(&ok);
|
||||
|
||||
if (ok) {
|
||||
qDebug() << "Parent process PID is" << parentPID;
|
||||
watchParentProcess(parentPID);
|
||||
_parentPID = parentPID;
|
||||
qDebug() << "Parent process PID is" << _parentPID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -630,6 +658,7 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) {
|
|||
|
||||
void DomainServer::setupNodeListAndAssignments() {
|
||||
const QString CUSTOM_LOCAL_PORT_OPTION = "metaverse.local_port";
|
||||
static const QString ENABLE_PACKET_AUTHENTICATION = "metaverse.enable_packet_verification";
|
||||
|
||||
QVariant localPortValue = _settingsManager.valueOrDefaultValueForKeyPath(CUSTOM_LOCAL_PORT_OPTION);
|
||||
int domainServerPort = localPortValue.toInt();
|
||||
|
@ -696,6 +725,9 @@ void DomainServer::setupNodeListAndAssignments() {
|
|||
}
|
||||
}
|
||||
|
||||
bool isAuthEnabled = _settingsManager.valueOrDefaultValueForKeyPath(ENABLE_PACKET_AUTHENTICATION).toBool();
|
||||
nodeList->setAuthenticatePackets(isAuthEnabled);
|
||||
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeAdded, this, &DomainServer::nodeAdded);
|
||||
connect(nodeList.data(), &LimitedNodeList::nodeKilled, this, &DomainServer::nodeKilled);
|
||||
|
||||
|
@ -1133,7 +1165,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif
|
|||
extendedHeaderStream << node->getUUID();
|
||||
extendedHeaderStream << node->getLocalID();
|
||||
extendedHeaderStream << node->getPermissions();
|
||||
|
||||
extendedHeaderStream << limitedNodeList->getAuthenticatePackets();
|
||||
auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader);
|
||||
|
||||
// always send the node their own UUID back
|
||||
|
|
|
@ -59,6 +59,8 @@ public:
|
|||
DomainServer(int argc, char* argv[]);
|
||||
~DomainServer();
|
||||
|
||||
static void parseCommandLine(int argc, char* argv[]);
|
||||
|
||||
enum DomainType {
|
||||
NonMetaverse,
|
||||
MetaverseDomain,
|
||||
|
@ -138,7 +140,6 @@ signals:
|
|||
|
||||
private:
|
||||
QUuid getID();
|
||||
void parseCommandLine();
|
||||
|
||||
QString getContentBackupDir();
|
||||
QString getEntitiesDirPath();
|
||||
|
@ -228,7 +229,7 @@ private:
|
|||
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
|
||||
TransactionHash _pendingAssignmentCredits;
|
||||
|
||||
bool _isUsingDTLS;
|
||||
bool _isUsingDTLS { false };
|
||||
|
||||
QUrl _oauthProviderURL;
|
||||
QString _oauthClientID;
|
||||
|
@ -265,10 +266,13 @@ private:
|
|||
friend class DomainGatekeeper;
|
||||
friend class DomainMetadata;
|
||||
|
||||
QString _iceServerAddr;
|
||||
int _iceServerPort;
|
||||
bool _overrideDomainID { false }; // should we override the domain-id from settings?
|
||||
QUuid _overridingDomainID { QUuid() }; // what should we override it with?
|
||||
static QString _iceServerAddr;
|
||||
static int _iceServerPort;
|
||||
static bool _overrideDomainID; // should we override the domain-id from settings?
|
||||
static QUuid _overridingDomainID; // what should we override it with?
|
||||
static bool _getTempName;
|
||||
static QString _userConfigFilename;
|
||||
static int _parentPID;
|
||||
|
||||
bool _sendICEServerAddressToMetaverseAPIInProgress { false };
|
||||
bool _sendICEServerAddressToMetaverseAPIRedo { false };
|
||||
|
|
|
@ -191,13 +191,12 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer<Re
|
|||
nodeList->sendPacketList(std::move(packetList), message->getSenderSockAddr());
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||
void DomainServerSettingsManager::setupConfigMap(const QString& userConfigFilename) {
|
||||
// since we're called from the DomainServerSettingsManager constructor, we don't take a write lock here
|
||||
// even though we change the underlying config map
|
||||
|
||||
_argumentList = argumentList;
|
||||
|
||||
_configMap.loadConfig(_argumentList);
|
||||
_configMap.setUserConfigFilename(userConfigFilename);
|
||||
_configMap.loadConfig();
|
||||
|
||||
static const auto VERSION_SETTINGS_KEYPATH = "version";
|
||||
QVariant* versionVariant = _configMap.valueForKeyPath(VERSION_SETTINGS_KEYPATH);
|
||||
|
@ -1736,7 +1735,7 @@ void DomainServerSettingsManager::persistToFile() {
|
|||
// failed to write, reload whatever the current config state is
|
||||
// with a write lock since we're about to overwrite the config map
|
||||
QWriteLocker locker(&_settingsLock);
|
||||
_configMap.loadConfig(_argumentList);
|
||||
_configMap.loadConfig();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ public:
|
|||
DomainServerSettingsManager();
|
||||
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||
|
||||
void setupConfigMap(const QStringList& argumentList);
|
||||
void setupConfigMap(const QString& userConfigFilename);
|
||||
|
||||
// each of the three methods in this group takes a read lock of _settingsLock
|
||||
// and cannot be called when the a write lock is held by the same thread
|
||||
|
@ -144,8 +144,6 @@ private slots:
|
|||
void processUsernameFromIDRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
|
||||
private:
|
||||
QStringList _argumentList;
|
||||
|
||||
QJsonArray filteredDescriptionArray(bool isContentSettings);
|
||||
void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
|
||||
const QJsonObject& settingDescription);
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
int main(int argc, char* argv[]) {
|
||||
setupHifiApplication(BuildInfo::DOMAIN_SERVER_NAME);
|
||||
|
||||
DomainServer::parseCommandLine(argc, argv);
|
||||
|
||||
Setting::init();
|
||||
|
||||
int currentExitCode = 0;
|
||||
|
|
|
@ -214,6 +214,7 @@ link_hifi_libraries(
|
|||
controllers plugins image trackers
|
||||
ui-plugins display-plugins input-plugins
|
||||
${PLATFORM_GL_BACKEND}
|
||||
shaders
|
||||
)
|
||||
|
||||
# include the binary directory of render-utils for shader includes
|
||||
|
|
File diff suppressed because it is too large
Load diff
1228
interface/resources/avatar/old-avatar-animation.json
Normal file
1228
interface/resources/avatar/old-avatar-animation.json
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
|
@ -62,6 +62,7 @@ Windows.ScrollingWindow {
|
|||
url: "about:blank"
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
profile: HFWebEngineProfile;
|
||||
|
||||
property string userScriptUrl: ""
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ Original.Button {
|
|||
property int color: 0
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
property int fontSize: hifi.fontSizes.buttonLabel
|
||||
property int radius: hifi.buttons.radius
|
||||
property alias implicitTextWidth: buttonText.implicitWidth
|
||||
property string buttonGlyph: "";
|
||||
property int fontCapitalization: Font.AllUppercase
|
||||
|
@ -46,7 +47,7 @@ Original.Button {
|
|||
}
|
||||
|
||||
background: Rectangle {
|
||||
radius: hifi.buttons.radius
|
||||
radius: control.radius
|
||||
|
||||
border.width: (control.color === hifi.buttons.none ||
|
||||
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
|
||||
|
|
|
@ -124,6 +124,11 @@ SpinBox {
|
|||
color: spinBox.up.pressed || spinBox.up.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
||||
}
|
||||
}
|
||||
up.onPressedChanged: {
|
||||
if(value) {
|
||||
spinBox.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
down.indicator: Item {
|
||||
x: spinBox.width - implicitWidth - 5
|
||||
|
@ -138,6 +143,11 @@ SpinBox {
|
|||
color: spinBox.down.pressed || spinBox.down.hovered ? (isLightColorScheme ? hifi.colors.black : hifi.colors.white) : hifi.colors.gray
|
||||
}
|
||||
}
|
||||
down.onPressedChanged: {
|
||||
if(value) {
|
||||
spinBox.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: spinBoxLabel
|
||||
|
|
|
@ -476,17 +476,13 @@ Rectangle {
|
|||
anchors.verticalCenter: avatarNameLabel.verticalCenter
|
||||
glyphText: "."
|
||||
glyphSize: 22
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
popup.showSpecifyAvatarUrl(currentAvatar.avatarUrl, function() {
|
||||
var url = popup.inputText.text;
|
||||
emitSendToScript({'method' : 'applyExternalAvatar', 'avatarURL' : url})
|
||||
}, function(link) {
|
||||
Qt.openUrlExternally(link);
|
||||
});
|
||||
}
|
||||
onClicked: {
|
||||
popup.showSpecifyAvatarUrl(currentAvatar.avatarUrl, function() {
|
||||
var url = popup.inputText.text;
|
||||
emitSendToScript({'method' : 'applyExternalAvatar', 'avatarURL' : url})
|
||||
}, function(link) {
|
||||
Qt.openUrlExternally(link);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,12 +492,8 @@ Rectangle {
|
|||
glyphText: "\ue02e"
|
||||
|
||||
visible: avatarWearablesCount !== 0
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
adjustWearables.open(currentAvatar);
|
||||
}
|
||||
onClicked: {
|
||||
adjustWearables.open(currentAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -326,7 +326,7 @@ Rectangle {
|
|||
height: 40
|
||||
anchors.right: parent.right
|
||||
color: hifi.buttons.red;
|
||||
colorScheme: hifi.colorSchemes.dark;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
text: "TAKE IT OFF"
|
||||
onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id);
|
||||
enabled: wearablesCombobox.model.count !== 0
|
||||
|
|
|
@ -1,25 +1,42 @@
|
|||
import "../../styles-uit"
|
||||
import "../../controls-uit" as HifiControlsUit
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
ShadowRectangle {
|
||||
Item {
|
||||
id: root
|
||||
width: 44
|
||||
height: 28
|
||||
AvatarAppStyle {
|
||||
id: style
|
||||
signal clicked();
|
||||
|
||||
HifiControlsUit.Button {
|
||||
id: button
|
||||
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
color: hifi.buttons.blue;
|
||||
colorScheme: hifi.colorSchemes.light;
|
||||
radius: 3
|
||||
onClicked: root.clicked();
|
||||
}
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: style.colors.blueHighlight }
|
||||
GradientStop { position: 1.0; color: style.colors.blueAccent }
|
||||
DropShadow {
|
||||
id: shadow
|
||||
anchors.fill: button
|
||||
radius: 6
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 3
|
||||
color: Qt.rgba(0, 0, 0, 0.25)
|
||||
source: button
|
||||
}
|
||||
|
||||
property alias glyphText: glyph.text
|
||||
property alias glyphRotation: glyph.rotation
|
||||
property alias glyphSize: glyph.size
|
||||
|
||||
radius: 3
|
||||
|
||||
HiFiGlyphs {
|
||||
id: glyph
|
||||
color: 'white'
|
||||
|
|
|
@ -876,7 +876,7 @@ Rectangle {
|
|||
horizontalAlignment: Text.AlignLeft;
|
||||
verticalAlignment: Text.AlignVCenter;
|
||||
onLinkActivated: {
|
||||
sendToScript({method: 'checkout_goToPurchases'});
|
||||
sendToScript({method: 'checkout_goToPurchases', filterText: root.itemName});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ Item {
|
|||
|
||||
Timer {
|
||||
id: refreshTimer;
|
||||
interval: 4000;
|
||||
interval: 6000;
|
||||
onTriggered: {
|
||||
if (transactionHistory.atYBeginning) {
|
||||
console.log("Refreshing 1st Page of Recent Activity...");
|
||||
|
@ -211,6 +211,7 @@ Item {
|
|||
|
||||
HifiModels.PSFListModel {
|
||||
id: transactionHistoryModel;
|
||||
property int lastPendingCount: 0;
|
||||
listModelName: "transaction history"; // For debugging. Alternatively, we could specify endpoint for that purpose, even though it's not used directly.
|
||||
listView: transactionHistory;
|
||||
itemsPerPage: 6;
|
||||
|
@ -221,8 +222,26 @@ Item {
|
|||
processPage: function (data) {
|
||||
console.debug('processPage', transactionHistoryModel.listModelName, JSON.stringify(data));
|
||||
var result, pending; // Set up or get the accumulator for pending.
|
||||
if (transactionHistoryModel.currentPageToRetrieve == 1) {
|
||||
pending = {transaction_type: "pendingCount", count: 0};
|
||||
if (transactionHistoryModel.currentPageToRetrieve === 1) {
|
||||
// The initial data elements inside the ListModel MUST contain all keys
|
||||
// that will be used in future data.
|
||||
pending = {
|
||||
transaction_type: "pendingCount",
|
||||
count: 0,
|
||||
created_at: 0,
|
||||
hfc_text: "",
|
||||
id: "",
|
||||
message: "",
|
||||
place_name: "",
|
||||
received_certs: 0,
|
||||
received_money: 0,
|
||||
recipient_name: "",
|
||||
sender_name: "",
|
||||
sent_certs: 0,
|
||||
sent_money: 0,
|
||||
status: "",
|
||||
transaction_text: ""
|
||||
};
|
||||
result = [pending];
|
||||
} else {
|
||||
pending = transactionHistoryModel.get(0);
|
||||
|
@ -239,6 +258,15 @@ Item {
|
|||
}
|
||||
});
|
||||
|
||||
if (lastPendingCount === 0) {
|
||||
lastPendingCount = pending.count;
|
||||
} else {
|
||||
if (lastPendingCount !== pending.count) {
|
||||
transactionHistoryModel.getNextPageIfNotEnoughVerticalResults();
|
||||
}
|
||||
lastPendingCount = pending.count;
|
||||
}
|
||||
|
||||
// Only auto-refresh if the user hasn't scrolled
|
||||
// and there is more data to grab
|
||||
if (transactionHistory.atYBeginning && data.history.length) {
|
||||
|
@ -257,13 +285,13 @@ Item {
|
|||
ListView {
|
||||
id: transactionHistory;
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
policy: transactionHistory.contentHeight > parent.parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded;
|
||||
parent: transactionHistory.parent;
|
||||
anchors.top: transactionHistory.top;
|
||||
anchors.left: transactionHistory.right;
|
||||
anchors.leftMargin: 4;
|
||||
anchors.bottom: transactionHistory.bottom;
|
||||
width: 20;
|
||||
policy: transactionHistory.contentHeight > parent.parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded;
|
||||
parent: transactionHistory.parent;
|
||||
anchors.top: transactionHistory.top;
|
||||
anchors.left: transactionHistory.right;
|
||||
anchors.leftMargin: 4;
|
||||
anchors.bottom: transactionHistory.bottom;
|
||||
width: 20;
|
||||
}
|
||||
anchors.centerIn: parent;
|
||||
width: parent.width - 12;
|
||||
|
@ -340,7 +368,7 @@ Item {
|
|||
}
|
||||
|
||||
HifiControlsUit.Separator {
|
||||
colorScheme: 1;
|
||||
colorScheme: 1;
|
||||
anchors.left: parent.left;
|
||||
anchors.right: parent.right;
|
||||
anchors.bottom: parent.bottom;
|
||||
|
|
|
@ -38,6 +38,16 @@ ListModel {
|
|||
onSearchFilterChanged: if (initialized) { getFirstPage('delayClear'); }
|
||||
onTagsFilterChanged: if (initialized) { getFirstPage('delayClear'); }
|
||||
|
||||
// When considering a value for `itemsPerPage` in YOUR model, consider the following:
|
||||
// - If your ListView delegates are of variable width/height, ensure you select
|
||||
// an `itemsPerPage` value that would be sufficient to show one full page of data
|
||||
// if all of the delegates were at their minimum heights.
|
||||
// - If your first ListView delegate contains some special data (as in WalletHome's
|
||||
// "Recent Activity" view), beware that your `itemsPerPage` value may _never_ reasonably be
|
||||
// high enough such that the first page of data causes the view to be one-screen in height
|
||||
// after retrieving the first page. This means data will automatically pop-in (after a short delay)
|
||||
// until the combined heights of your View's delegates reach one-screen in height OR there is
|
||||
// no more data to retrieve. See "needsMoreVerticalResults()" below.
|
||||
property int itemsPerPage: 100;
|
||||
|
||||
// State.
|
||||
|
@ -81,12 +91,35 @@ ListModel {
|
|||
function getNextPageIfVerticalScroll() {
|
||||
if (needsEarlyYFetch()) { getNextPage(); }
|
||||
}
|
||||
function needsMoreHorizontalResults() {
|
||||
return flickable
|
||||
&& currentPageToRetrieve > 0
|
||||
&& flickable.contentWidth < flickable.width;
|
||||
}
|
||||
function needsMoreVerticalResults() {
|
||||
return flickable
|
||||
&& currentPageToRetrieve > 0
|
||||
&& flickable.contentHeight < flickable.height;
|
||||
}
|
||||
function getNextPageIfNotEnoughHorizontalResults() {
|
||||
if (needsMoreHorizontalResults()) {
|
||||
getNextPage();
|
||||
}
|
||||
}
|
||||
function getNextPageIfNotEnoughVerticalResults() {
|
||||
if (needsMoreVerticalResults()) {
|
||||
getNextPage();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
initialized = true;
|
||||
if (flickable && pageAhead > 0.0) {
|
||||
// Pun: Scrollers are usually one direction or another, such that only one of the following will actually fire.
|
||||
flickable.contentXChanged.connect(getNextPageIfHorizontalScroll);
|
||||
flickable.contentYChanged.connect(getNextPageIfVerticalScroll);
|
||||
flickable.contentWidthChanged.connect(getNextPageIfNotEnoughHorizontalResults);
|
||||
flickable.contentHeightChanged.connect(getNextPageIfNotEnoughVerticalResults);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -299,7 +299,7 @@ Item {
|
|||
anchors.fill: stackView
|
||||
id: controllerPrefereneces
|
||||
objectName: "TabletControllerPreferences"
|
||||
showCategories: [( (HMD.active) ? "VR Movement" : "Movement"), "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"]
|
||||
showCategories: ["VR Movement", "Game Controller", "Sixense Controllers", "Perception Neuron", "Leap Motion"]
|
||||
categoryProperties: {
|
||||
"VR Movement" : {
|
||||
"User real-world height (meters)" : { "anchors.right" : "undefined" },
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#include <AddressManager.h>
|
||||
#include <AnimDebugDraw.h>
|
||||
#include <BuildInfo.h>
|
||||
#include <AnimationCacheScriptingInterface.h>
|
||||
#include <AssetClient.h>
|
||||
#include <AssetUpload.h>
|
||||
#include <AutoUpdater.h>
|
||||
|
@ -98,6 +99,8 @@
|
|||
#include <MainWindow.h>
|
||||
#include <MappingRequest.h>
|
||||
#include <MessagesClient.h>
|
||||
#include <model-networking/ModelCacheScriptingInterface.h>
|
||||
#include <model-networking/TextureCacheScriptingInterface.h>
|
||||
#include <ModelEntityItem.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
|
@ -127,7 +130,7 @@
|
|||
#include <ScriptEngines.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <ShapeEntityItem.h>
|
||||
#include <SoundCache.h>
|
||||
#include <SoundCacheScriptingInterface.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
#include <ui/ToolbarScriptingInterface.h>
|
||||
#include <InteractiveWindow.h>
|
||||
|
@ -143,6 +146,7 @@
|
|||
#include <QmlFragmentClass.h>
|
||||
#include <Preferences.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
#include <display-plugins/hmd/HmdDisplayPlugin.h>
|
||||
#include <trackers/EyeTracker.h>
|
||||
#include <avatars-renderer/ScriptAvatar.h>
|
||||
#include <RenderableEntityItem.h>
|
||||
|
@ -374,6 +378,7 @@ static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SI
|
|||
static const uint32_t INVALID_FRAME = UINT32_MAX;
|
||||
|
||||
static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check for entities that aren't ready for simulation
|
||||
static const float INITIAL_QUERY_RADIUS = 10.0f; // priority radius for entities before physics enabled
|
||||
|
||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
|
||||
|
@ -866,16 +871,20 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<recording::ClipCache>();
|
||||
DependencyManager::set<GeometryCache>();
|
||||
DependencyManager::set<ModelCache>();
|
||||
DependencyManager::set<ModelCacheScriptingInterface>();
|
||||
DependencyManager::set<ScriptCache>();
|
||||
DependencyManager::set<SoundCache>();
|
||||
DependencyManager::set<SoundCacheScriptingInterface>();
|
||||
DependencyManager::set<DdeFaceTracker>();
|
||||
DependencyManager::set<EyeTracker>();
|
||||
DependencyManager::set<AudioClient>();
|
||||
DependencyManager::set<AudioScope>();
|
||||
DependencyManager::set<DeferredLightingEffect>();
|
||||
DependencyManager::set<TextureCache>();
|
||||
DependencyManager::set<TextureCacheScriptingInterface>();
|
||||
DependencyManager::set<FramebufferCache>();
|
||||
DependencyManager::set<AnimationCache>();
|
||||
DependencyManager::set<AnimationCacheScriptingInterface>();
|
||||
DependencyManager::set<ModelBlender>();
|
||||
DependencyManager::set<UsersScriptingInterface>();
|
||||
DependencyManager::set<AvatarManager>();
|
||||
|
@ -1238,7 +1247,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL());
|
||||
|
||||
// use our MyAvatar position and quat for address manager path
|
||||
addressManager->setPositionGetter([this]{ return getMyAvatar()->getWorldPosition(); });
|
||||
addressManager->setPositionGetter([this]{ return getMyAvatar()->getWorldFeetPosition(); });
|
||||
addressManager->setOrientationGetter([this]{ return getMyAvatar()->getWorldOrientation(); });
|
||||
|
||||
connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
|
||||
|
@ -2562,12 +2571,18 @@ Application::~Application() {
|
|||
|
||||
DependencyManager::destroy<CompositorHelper>(); // must be destroyed before the FramebufferCache
|
||||
|
||||
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||
|
||||
DependencyManager::destroy<AvatarManager>();
|
||||
DependencyManager::destroy<AnimationCacheScriptingInterface>();
|
||||
DependencyManager::destroy<AnimationCache>();
|
||||
DependencyManager::destroy<FramebufferCache>();
|
||||
DependencyManager::destroy<TextureCacheScriptingInterface>();
|
||||
DependencyManager::destroy<TextureCache>();
|
||||
DependencyManager::destroy<ModelCacheScriptingInterface>();
|
||||
DependencyManager::destroy<ModelCache>();
|
||||
DependencyManager::destroy<ScriptCache>();
|
||||
DependencyManager::destroy<SoundCacheScriptingInterface>();
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
DependencyManager::destroy<OctreeStatsProvider>();
|
||||
DependencyManager::destroy<GeometryCache>();
|
||||
|
@ -2688,8 +2703,6 @@ void Application::initializeGL() {
|
|||
// contexts
|
||||
_glWidget->makeCurrent();
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK,
|
||||
QVariant::fromValue((void*)(&gpu::gl::GLBackend::makeProgram)));
|
||||
_glWidget->makeCurrent();
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
|
||||
|
@ -2717,6 +2730,10 @@ void Application::initializeDisplayPlugins() {
|
|||
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged,
|
||||
[this](const QSize& size) { resizeGL(); });
|
||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset);
|
||||
if (displayPlugin->isHmd()) {
|
||||
QObject::connect(dynamic_cast<HmdDisplayPlugin*>(displayPlugin.get()), &HmdDisplayPlugin::hmdMountedChanged,
|
||||
DependencyManager::get<HMDScriptingInterface>().data(), &HMDScriptingInterface::mountedChanged);
|
||||
}
|
||||
}
|
||||
|
||||
// The default display plugin needs to be activated first, otherwise the display plugin thread
|
||||
|
@ -2989,10 +3006,11 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
|
|||
surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
|
||||
|
||||
// Caches
|
||||
surfaceContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
surfaceContext->setContextProperty("TextureCache", DependencyManager::get<TextureCache>().data());
|
||||
surfaceContext->setContextProperty("ModelCache", DependencyManager::get<ModelCache>().data());
|
||||
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
surfaceContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCacheScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("TextureCache", DependencyManager::get<TextureCacheScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("ModelCache", DependencyManager::get<ModelCacheScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
|
||||
surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||
|
||||
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
|
@ -4554,11 +4572,14 @@ void Application::idle() {
|
|||
_lastTimeUpdated.start();
|
||||
|
||||
// If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
|
||||
if (_keyboardDeviceHasFocus && offscreenUi && offscreenUi->getWindow()->activeFocusItem() != offscreenUi->getRootItem()) {
|
||||
_keyboardMouseDevice->pluginFocusOutEvent();
|
||||
_keyboardDeviceHasFocus = false;
|
||||
} else if (offscreenUi && offscreenUi->getWindow()->activeFocusItem() == offscreenUi->getRootItem()) {
|
||||
_keyboardDeviceHasFocus = true;
|
||||
if (offscreenUi && offscreenUi->getWindow()) {
|
||||
auto activeFocusItem = offscreenUi->getWindow()->activeFocusItem();
|
||||
if (_keyboardDeviceHasFocus && activeFocusItem != offscreenUi->getRootItem()) {
|
||||
_keyboardMouseDevice->pluginFocusOutEvent();
|
||||
_keyboardDeviceHasFocus = false;
|
||||
} else if (activeFocusItem == offscreenUi->getRootItem()) {
|
||||
_keyboardDeviceHasFocus = true;
|
||||
}
|
||||
}
|
||||
|
||||
checkChangeCursor();
|
||||
|
@ -5264,6 +5285,7 @@ void Application::reloadResourceCaches() {
|
|||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
|
||||
|
||||
DependencyManager::get<AssetClient>()->clearCache();
|
||||
DependencyManager::get<ScriptCache>()->clearCache();
|
||||
|
||||
DependencyManager::get<AnimationCache>()->refreshAll();
|
||||
DependencyManager::get<ModelCache>()->refreshAll();
|
||||
|
@ -5480,12 +5502,14 @@ void Application::update(float deltaTime) {
|
|||
// we haven't yet enabled physics. we wait until we think we have all the collision information
|
||||
// for nearby entities before starting bullet up.
|
||||
quint64 now = usecTimestampNow();
|
||||
const int PHYSICS_CHECK_TIMEOUT = 2 * USECS_PER_SECOND;
|
||||
|
||||
if (now - _lastPhysicsCheckTime > PHYSICS_CHECK_TIMEOUT || _fullSceneReceivedCounter > _fullSceneCounterAtLastPhysicsCheck) {
|
||||
// Check for flagged EntityData having arrived.
|
||||
auto entityTreeRenderer = getEntities();
|
||||
if (isServerlessMode() ||
|
||||
(entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) )) {
|
||||
// we've received a new full-scene octree stats packet, or it's been long enough to try again anyway
|
||||
_lastPhysicsCheckTime = now;
|
||||
_fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter;
|
||||
_lastQueriedViews.clear(); // Force new view.
|
||||
|
||||
// process octree stats packets are sent in between full sends of a scene (this isn't currently true).
|
||||
// We keep physics disabled until we've received a full scene and everything near the avatar in that
|
||||
|
@ -6134,11 +6158,23 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
|
|||
return; // bail early if settings are not loaded
|
||||
}
|
||||
|
||||
_octreeQuery.setConicalViews(_conicalViews);
|
||||
const bool isModifiedQuery = !_physicsEnabled;
|
||||
if (isModifiedQuery) {
|
||||
// Create modified view that is a simple sphere.
|
||||
ConicalViewFrustum sphericalView;
|
||||
sphericalView.setSimpleRadius(INITIAL_QUERY_RADIUS);
|
||||
_octreeQuery.setConicalViews({ sphericalView });
|
||||
_octreeQuery.setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE);
|
||||
static constexpr float MIN_LOD_ADJUST = -20.0f;
|
||||
_octreeQuery.setBoundaryLevelAdjust(MIN_LOD_ADJUST);
|
||||
} else {
|
||||
_octreeQuery.setConicalViews(_conicalViews);
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
|
||||
_octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
|
||||
}
|
||||
_octreeQuery.setReportInitialCompletion(isModifiedQuery);
|
||||
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
|
||||
_octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
|
@ -6286,6 +6322,7 @@ void Application::clearDomainOctreeDetails() {
|
|||
_octreeServerSceneStats.clear();
|
||||
});
|
||||
|
||||
_octreeProcessor.resetCompletionSequenceNumber();
|
||||
// reset the model renderer
|
||||
getEntities()->clear();
|
||||
|
||||
|
@ -6584,11 +6621,12 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,
|
||||
LocationScriptingInterface::locationSetter);
|
||||
|
||||
bool clientScript = scriptEngine->isClientScript();
|
||||
scriptEngine->registerFunction("OverlayWindow", clientScript ? QmlWindowClass::constructor : QmlWindowClass::restricted_constructor);
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor);
|
||||
scriptEngine->registerFunction("OverlayWebWindow", clientScript ? QmlWebWindowClass::constructor : QmlWebWindowClass::restricted_constructor);
|
||||
#endif
|
||||
scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);
|
||||
scriptEngine->registerFunction("QmlFragment", QmlFragmentClass::constructor);
|
||||
scriptEngine->registerFunction("QmlFragment", clientScript ? QmlFragmentClass::constructor : QmlFragmentClass::restricted_constructor);
|
||||
|
||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||
scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get<DesktopPreviewProvider>().data());
|
||||
|
@ -6606,10 +6644,10 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerGlobalObject("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
|
||||
|
||||
// Caches
|
||||
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
|
||||
scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCache>().data());
|
||||
scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCache>().data());
|
||||
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCacheScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCacheScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCacheScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);
|
||||
|
||||
|
|
|
@ -412,9 +412,6 @@ void AvatarManager::clearOtherAvatars() {
|
|||
while (avatarIterator != _avatarHash.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
|
||||
if (avatar != _myAvatar) {
|
||||
if (avatar->isInScene()) {
|
||||
avatar->removeFromScene(avatar, scene, transaction);
|
||||
}
|
||||
handleRemovedAvatar(avatar);
|
||||
avatarIterator = _avatarHash.erase(avatarIterator);
|
||||
} else {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
||||
assert(_avatar);
|
||||
_type = MOTIONSTATE_TYPE_AVATAR;
|
||||
cacheShapeDiameter();
|
||||
}
|
||||
|
||||
void AvatarMotionState::handleEasyChanges(uint32_t& flags) {
|
||||
|
@ -57,9 +58,6 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const {
|
|||
const btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||
ShapeInfo shapeInfo;
|
||||
std::static_pointer_cast<Avatar>(_avatar)->computeShapeInfo(shapeInfo);
|
||||
glm::vec3 halfExtents = shapeInfo.getHalfExtents();
|
||||
halfExtents.y = 0.0f;
|
||||
_diameter = 2.0f * glm::length(halfExtents);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
|
@ -98,6 +96,10 @@ void AvatarMotionState::setWorldTransform(const btTransform& worldTrans) {
|
|||
btVector3 velocity = glmToBullet(getObjectLinearVelocity()) + (1.0f / SPRING_TIMESCALE) * offsetToTarget;
|
||||
_body->setLinearVelocity(velocity);
|
||||
_body->setAngularVelocity(glmToBullet(getObjectAngularVelocity()));
|
||||
// slam its rotation
|
||||
btTransform newTransform = worldTrans;
|
||||
newTransform.setRotation(glmToBullet(getObjectRotation()));
|
||||
_body->setWorldTransform(newTransform);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +143,10 @@ glm::vec3 AvatarMotionState::getObjectLinearVelocity() const {
|
|||
|
||||
// virtual
|
||||
glm::vec3 AvatarMotionState::getObjectAngularVelocity() const {
|
||||
return _avatar->getWorldAngularVelocity();
|
||||
// HACK: avatars use a capusle collision shape and their angularVelocity in the local simulation is unimportant.
|
||||
// Therefore, as optimization toward support for larger crowds we ignore it and return zero.
|
||||
//return _avatar->getWorldAngularVelocity();
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
@ -174,3 +179,28 @@ float AvatarMotionState::getMass() const {
|
|||
return std::static_pointer_cast<Avatar>(_avatar)->computeMass();
|
||||
}
|
||||
|
||||
void AvatarMotionState::cacheShapeDiameter() {
|
||||
if (_shape) {
|
||||
// shape is capsuleY
|
||||
btVector3 aabbMin, aabbMax;
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
_shape->getAabb(transform, aabbMin, aabbMax);
|
||||
_diameter = (aabbMax - aabbMin).getX();
|
||||
} else {
|
||||
_diameter = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMotionState::setRigidBody(btRigidBody* body) {
|
||||
ObjectMotionState::setRigidBody(body);
|
||||
if (_body) {
|
||||
// remove angular dynamics from this body
|
||||
_body->setAngularFactor(0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMotionState::setShape(const btCollisionShape* shape) {
|
||||
ObjectMotionState::setShape(shape);
|
||||
cacheShapeDiameter();
|
||||
}
|
||||
|
|
|
@ -74,6 +74,10 @@ public:
|
|||
friend class Avatar;
|
||||
|
||||
protected:
|
||||
void setRigidBody(btRigidBody* body) override;
|
||||
void setShape(const btCollisionShape* shape) override;
|
||||
void cacheShapeDiameter();
|
||||
|
||||
// the dtor had been made protected to force the compiler to verify that it is only
|
||||
// ever called by the Avatar class dtor.
|
||||
~AvatarMotionState();
|
||||
|
|
|
@ -154,7 +154,7 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
|
||||
// connect to AddressManager signal for location jumps
|
||||
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
|
||||
this, static_cast<SlotType>(&MyAvatar::goToLocation));
|
||||
this, static_cast<SlotType>(&MyAvatar::goToFeetLocation));
|
||||
|
||||
// handle scale constraints imposed on us by the domain-server
|
||||
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
||||
|
@ -1135,7 +1135,6 @@ void MyAvatar::saveData() {
|
|||
settings.setValue("collisionSoundURL", _collisionSoundURL);
|
||||
settings.setValue("useSnapTurn", _useSnapTurn);
|
||||
settings.setValue("userHeight", getUserHeight());
|
||||
settings.setValue("flyingDesktop", getFlyingDesktopPref());
|
||||
settings.setValue("flyingHMD", getFlyingHMDPref());
|
||||
|
||||
settings.endGroup();
|
||||
|
@ -1289,7 +1288,6 @@ void MyAvatar::loadData() {
|
|||
|
||||
// Flying preferences must be loaded before calling setFlyingEnabled()
|
||||
Setting::Handle<bool> firstRunVal { Settings::firstRun, true };
|
||||
setFlyingDesktopPref(firstRunVal.get() ? true : settings.value("flyingDesktop").toBool());
|
||||
setFlyingHMDPref(firstRunVal.get() ? false : settings.value("flyingHMD").toBool());
|
||||
setFlyingEnabled(getFlyingEnabled());
|
||||
|
||||
|
@ -2625,6 +2623,49 @@ void MyAvatar::goToLocation(const QVariant& propertiesVar) {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::goToFeetLocation(const glm::vec3& newPosition,
|
||||
bool hasOrientation, const glm::quat& newOrientation,
|
||||
bool shouldFaceLocation) {
|
||||
|
||||
qCDebug(interfaceapp).nospace() << "MyAvatar goToFeetLocation - moving to " << newPosition.x << ", "
|
||||
<< newPosition.y << ", " << newPosition.z;
|
||||
|
||||
ShapeInfo shapeInfo;
|
||||
computeShapeInfo(shapeInfo);
|
||||
glm::vec3 halfExtents = shapeInfo.getHalfExtents();
|
||||
glm::vec3 localFeetPos = shapeInfo.getOffset() - glm::vec3(0.0f, halfExtents.y + halfExtents.x, 0.0f);
|
||||
glm::mat4 localFeet = createMatFromQuatAndPos(Quaternions::IDENTITY, localFeetPos);
|
||||
|
||||
glm::mat4 worldFeet = createMatFromQuatAndPos(Quaternions::IDENTITY, newPosition);
|
||||
|
||||
glm::mat4 avatarMat = worldFeet * glm::inverse(localFeet);
|
||||
|
||||
glm::vec3 adjustedPosition = extractTranslation(avatarMat);
|
||||
|
||||
_goToPending = true;
|
||||
_goToPosition = adjustedPosition;
|
||||
_goToOrientation = getWorldOrientation();
|
||||
if (hasOrientation) {
|
||||
qCDebug(interfaceapp).nospace() << "MyAvatar goToFeetLocation - new orientation is "
|
||||
<< newOrientation.x << ", " << newOrientation.y << ", " << newOrientation.z << ", " << newOrientation.w;
|
||||
|
||||
// orient the user to face the target
|
||||
glm::quat quatOrientation = cancelOutRollAndPitch(newOrientation);
|
||||
|
||||
if (shouldFaceLocation) {
|
||||
quatOrientation = newOrientation * glm::angleAxis(PI, Vectors::UP);
|
||||
|
||||
// move the user a couple units away
|
||||
const float DISTANCE_TO_USER = 2.0f;
|
||||
_goToPosition = adjustedPosition - quatOrientation * IDENTITY_FORWARD * DISTANCE_TO_USER;
|
||||
}
|
||||
|
||||
_goToOrientation = quatOrientation;
|
||||
}
|
||||
|
||||
emit transformChanged();
|
||||
}
|
||||
|
||||
void MyAvatar::goToLocation(const glm::vec3& newPosition,
|
||||
bool hasOrientation, const glm::quat& newOrientation,
|
||||
bool shouldFaceLocation) {
|
||||
|
|
|
@ -1133,6 +1133,20 @@ public slots:
|
|||
*/
|
||||
float getGravity();
|
||||
|
||||
/**jsdoc
|
||||
* Move the avatar to a new position and/or orientation in the domain, while taking into account Avatar leg-length.
|
||||
* @function MyAvatar.goToFeetLocation
|
||||
* @param {Vec3} position - The new position for the avatar, in world coordinates.
|
||||
* @param {boolean} [hasOrientation=false] - Set to <code>true</code> to set the orientation of the avatar.
|
||||
* @param {Quat} [orientation=Quat.IDENTITY] - The new orientation for the avatar.
|
||||
* @param {boolean} [shouldFaceLocation=false] - Set to <code>true</code> to position the avatar a short distance away from
|
||||
* the new position and orientate the avatar to face the position.
|
||||
*/
|
||||
|
||||
void goToFeetLocation(const glm::vec3& newPosition,
|
||||
bool hasOrientation, const glm::quat& newOrientation,
|
||||
bool shouldFaceLocation);
|
||||
|
||||
/**jsdoc
|
||||
* Move the avatar to a new position and/or orientation in the domain.
|
||||
* @function MyAvatar.goToLocation
|
||||
|
|
|
@ -42,6 +42,48 @@ extern "C" {
|
|||
int main(int argc, const char* argv[]) {
|
||||
setupHifiApplication(BuildInfo::INTERFACE_NAME);
|
||||
|
||||
QStringList arguments;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
arguments << argv[i];
|
||||
}
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription("High Fidelity Interface");
|
||||
QCommandLineOption versionOption = parser.addVersionOption();
|
||||
QCommandLineOption helpOption = parser.addHelpOption();
|
||||
|
||||
QCommandLineOption urlOption("url", "", "value");
|
||||
QCommandLineOption noUpdaterOption("no-updater", "Do not show auto-updater");
|
||||
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
|
||||
QCommandLineOption runServerOption("runServer", "Whether to run the server");
|
||||
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
|
||||
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
|
||||
QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir");
|
||||
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
|
||||
|
||||
parser.addOption(urlOption);
|
||||
parser.addOption(noUpdaterOption);
|
||||
parser.addOption(checkMinSpecOption);
|
||||
parser.addOption(runServerOption);
|
||||
parser.addOption(serverContentPathOption);
|
||||
parser.addOption(overrideAppLocalDataPathOption);
|
||||
parser.addOption(overrideScriptsPathOption);
|
||||
parser.addOption(allowMultipleInstancesOption);
|
||||
|
||||
if (!parser.parse(arguments)) {
|
||||
std::cout << parser.errorText().toStdString() << std::endl; // Avoid Qt log spam
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
parser.showVersion();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
if (parser.isSet(helpOption)) {
|
||||
QCoreApplication mockApp(argc, const_cast<char**>(argv)); // required for call to showHelp()
|
||||
parser.showHelp();
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
// Early check for --traceFile argument
|
||||
auto tracer = DependencyManager::set<tracing::Tracer>();
|
||||
const char * traceFile = nullptr;
|
||||
|
@ -95,30 +137,6 @@ int main(int argc, const char* argv[]) {
|
|||
qDebug() << "Crash handler started:" << crashHandlerStarted;
|
||||
}
|
||||
|
||||
QStringList arguments;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
arguments << argv[i];
|
||||
}
|
||||
|
||||
QCommandLineParser parser;
|
||||
QCommandLineOption urlOption("url", "", "value");
|
||||
QCommandLineOption noUpdaterOption("no-updater", "Do not show auto-updater");
|
||||
QCommandLineOption checkMinSpecOption("checkMinSpec", "Check if machine meets minimum specifications");
|
||||
QCommandLineOption runServerOption("runServer", "Whether to run the server");
|
||||
QCommandLineOption serverContentPathOption("serverContentPath", "Where to find server content", "serverContentPath");
|
||||
QCommandLineOption allowMultipleInstancesOption("allowMultipleInstances", "Allow multiple instances to run");
|
||||
QCommandLineOption overrideAppLocalDataPathOption("cache", "set test cache <dir>", "dir");
|
||||
QCommandLineOption overrideScriptsPathOption(SCRIPTS_SWITCH, "set scripts <path>", "path");
|
||||
parser.addOption(urlOption);
|
||||
parser.addOption(noUpdaterOption);
|
||||
parser.addOption(checkMinSpecOption);
|
||||
parser.addOption(runServerOption);
|
||||
parser.addOption(serverContentPathOption);
|
||||
parser.addOption(overrideAppLocalDataPathOption);
|
||||
parser.addOption(overrideScriptsPathOption);
|
||||
parser.addOption(allowMultipleInstancesOption);
|
||||
parser.parse(arguments);
|
||||
|
||||
|
||||
const QString& applicationName = getInterfaceSharedMemoryName();
|
||||
bool instanceMightBeRunning = true;
|
||||
|
|
|
@ -21,9 +21,9 @@ OctreePacketProcessor::OctreePacketProcessor() {
|
|||
setObjectName("Octree Packet Processor");
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
|
||||
packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
this, "handleOctreePacket");
|
||||
const PacketReceiver::PacketTypeList octreePackets =
|
||||
{ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase, PacketType::EntityQueryInitialResultsComplete };
|
||||
packetReceiver.registerDirectListenerForTypes(octreePackets, this, "handleOctreePacket");
|
||||
}
|
||||
|
||||
void OctreePacketProcessor::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
|
@ -111,8 +111,36 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
|||
}
|
||||
} break;
|
||||
|
||||
case PacketType::EntityQueryInitialResultsComplete: {
|
||||
// Read sequence #
|
||||
OCTREE_PACKET_SEQUENCE completionNumber;
|
||||
message->readPrimitive(&completionNumber);
|
||||
|
||||
_completionSequenceNumber = completionNumber;
|
||||
} break;
|
||||
|
||||
default: {
|
||||
// nothing to do
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void OctreePacketProcessor::resetCompletionSequenceNumber() {
|
||||
_completionSequenceNumber = INVALID_SEQUENCE;
|
||||
}
|
||||
|
||||
namespace {
|
||||
template<typename T> bool lessThanWraparound(int a, int b) {
|
||||
constexpr int MAX_T_VALUE = std::numeric_limits<T>::max();
|
||||
if (b <= a) {
|
||||
b += MAX_T_VALUE;
|
||||
}
|
||||
return (b - a) < (MAX_T_VALUE / 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const {
|
||||
// If we've received the flagged seq # and the current one is >= it.
|
||||
return _completionSequenceNumber != INVALID_SEQUENCE &&
|
||||
!lessThanWraparound<OCTREE_PACKET_SEQUENCE>(sequenceNumber, _completionSequenceNumber);
|
||||
}
|
||||
|
|
|
@ -22,13 +22,23 @@ class OctreePacketProcessor : public ReceivedPacketProcessor {
|
|||
public:
|
||||
OctreePacketProcessor();
|
||||
|
||||
bool octreeSequenceIsComplete(int sequenceNumber) const;
|
||||
|
||||
signals:
|
||||
void packetVersionMismatch();
|
||||
|
||||
public slots:
|
||||
void resetCompletionSequenceNumber();
|
||||
|
||||
protected:
|
||||
virtual void processPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) override;
|
||||
|
||||
private slots:
|
||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
||||
private:
|
||||
static constexpr int INVALID_SEQUENCE = -1;
|
||||
std::atomic<int> _completionSequenceNumber { INVALID_SEQUENCE };
|
||||
|
||||
};
|
||||
#endif // hifi_OctreePacketProcessor_h
|
||||
|
|
|
@ -12,11 +12,9 @@
|
|||
#include <StencilMaskPass.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <shaders/Shaders.h>
|
||||
|
||||
#include "ParabolaPick.h"
|
||||
|
||||
#include "render-utils/parabola_vert.h"
|
||||
#include "render-utils/parabola_frag.h"
|
||||
|
||||
const glm::vec4 ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_COLOR { 1.0f };
|
||||
const float ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_WIDTH { 0.01f };
|
||||
const bool ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_ISVISIBLEINSECONDARYCAMERA { false };
|
||||
|
@ -117,8 +115,6 @@ ParabolaPointer::RenderState::RenderState(const OverlayID& startID, const Overla
|
|||
_pathWidth = pathWidth;
|
||||
if (render::Item::isValidID(_pathID)) {
|
||||
auto renderItem = std::make_shared<ParabolaRenderItem>(pathColor, pathAlpha, pathWidth, isVisibleInSecondaryCamera, pathEnabled);
|
||||
// TODO: update bounds properly
|
||||
renderItem->editBound().setBox(glm::vec3(-16000.0f), 32000.0f);
|
||||
transaction.resetItem(_pathID, std::make_shared<ParabolaRenderItem::Payload>(renderItem));
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
@ -182,6 +178,7 @@ void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::ve
|
|||
item.setAcceleration(acceleration);
|
||||
item.setParabolicDistance(parabolicDistance);
|
||||
item.setWidth(width);
|
||||
item.updateBounds();
|
||||
});
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
@ -304,10 +301,10 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::setVisible(bool visible)
|
|||
}
|
||||
|
||||
void ParabolaPointer::RenderState::ParabolaRenderItem::updateKey() {
|
||||
// FIXME: There's no way to designate a render item as non-shadow-reciever, and since a parabola's bounding box covers the entire domain,
|
||||
// it seems to block all shadows. I think this is a bug with shadows.
|
||||
//auto builder = _parabolaData.color.a < 1.0f ? render::ItemKey::Builder::transparentShape() : render::ItemKey::Builder::opaqueShape();
|
||||
auto builder = render::ItemKey::Builder::transparentShape();
|
||||
auto builder = _parabolaData.color.a < 1.0f ? render::ItemKey::Builder::transparentShape() : render::ItemKey::Builder::opaqueShape();
|
||||
|
||||
// TODO: parabolas should cast shadows, but they're so thin that the cascaded shadow maps make them look pretty bad
|
||||
//builder.withShadowCaster();
|
||||
|
||||
if (_enabled && _visible) {
|
||||
builder.withVisible();
|
||||
|
@ -324,15 +321,36 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::updateKey() {
|
|||
_key = builder.build();
|
||||
}
|
||||
|
||||
void ParabolaPointer::RenderState::ParabolaRenderItem::updateBounds() {
|
||||
glm::vec3 max = _origin;
|
||||
glm::vec3 min = _origin;
|
||||
|
||||
glm::vec3 end = _origin + _parabolaData.velocity * _parabolaData.parabolicDistance +
|
||||
0.5f * _parabolaData.acceleration * _parabolaData.parabolicDistance * _parabolaData.parabolicDistance;
|
||||
max = glm::max(max, end);
|
||||
min = glm::min(min, end);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (fabsf(_parabolaData.velocity[i]) > EPSILON && fabsf(_parabolaData.acceleration[i]) > EPSILON) {
|
||||
float maxT = -_parabolaData.velocity[i] / _parabolaData.acceleration[i];
|
||||
if (maxT > 0.0f && maxT < _parabolaData.parabolicDistance) {
|
||||
glm::vec3 maxPoint = _origin + _parabolaData.velocity * maxT + 0.5f * _parabolaData.acceleration * maxT * maxT;
|
||||
max = glm::max(max, maxPoint);
|
||||
min = glm::min(min, maxPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 halfWidth = glm::vec3(0.5f * _parabolaData.width);
|
||||
max += halfWidth;
|
||||
min -= halfWidth;
|
||||
|
||||
_bound = AABox(min, max - min);
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::getParabolaPipeline() {
|
||||
if (!_parabolaPipeline || !_transparentParabolaPipeline) {
|
||||
auto vs = parabola_vert::getShader();
|
||||
auto ps = parabola_frag::getShader();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("parabolaData"), 0));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::parabola);
|
||||
|
||||
{
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
|
|
|
@ -36,6 +36,7 @@ public:
|
|||
void setVisible(bool visible);
|
||||
void updateKey();
|
||||
void updateUniformBuffer() { _uniformBuffer->setSubData(0, _parabolaData); }
|
||||
void updateBounds();
|
||||
|
||||
void setColor(const glm::vec3& color) { _parabolaData.color = glm::vec4(color, _parabolaData.color.a); }
|
||||
void setAlpha(const float& alpha) { _parabolaData.color.a = alpha; }
|
||||
|
|
|
@ -138,7 +138,7 @@ public:
|
|||
* @property {Vec3} intersection The intersection point in world-space.
|
||||
* @property {Vec3} surfaceNormal The surface normal at the intersected point. All NANs if type == INTERSECTED_HUD.
|
||||
* @property {Variant} extraInfo Additional intersection details when available for Model objects.
|
||||
* @property {StylusTip} parabola The PickParabola that was used. Valid even if there was no intersection.
|
||||
* @property {PickParabola} parabola The PickParabola that was used. Valid even if there was no intersection.
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
|
|
|
@ -92,7 +92,7 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
|
|||
* @typedef {object} Pointers.LaserPointerProperties
|
||||
* @property {boolean} [faceAvatar=false] If true, the end of the Pointer will always rotate to face the avatar.
|
||||
* @property {boolean} [centerEndY=true] If false, the end of the Pointer will be moved up by half of its height.
|
||||
* @property {boolean} [lockEnd=false] If true, the end of the Pointer will lock on to the center of the object at which the laser is pointing.
|
||||
* @property {boolean} [lockEnd=false] If true, the end of the Pointer will lock on to the center of the object at which the pointer is pointing.
|
||||
* @property {boolean} [distanceScaleEnd=false] If true, the dimensions of the end of the Pointer will scale linearly with distance.
|
||||
* @property {boolean} [scaleWithAvatar=false] If true, the width of the Pointer's path will scale linearly with your avatar's scale.
|
||||
* @property {boolean} [followNormal=false] If true, the end of the Pointer will rotate to follow the normal of the intersected surface.
|
||||
|
@ -207,9 +207,10 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
|
|||
* The rendering properties of the parabolic path
|
||||
*
|
||||
* @typedef {object} Pointers.ParabolaProperties
|
||||
* @property {Color} color The color of the parabola.
|
||||
* @property {number} alpha The alpha of the parabola.
|
||||
* @property {number} width The width of the parabola, in meters.
|
||||
* @property {Color} color=255,255,255 The color of the parabola.
|
||||
* @property {number} alpha=1.0 The alpha of the parabola.
|
||||
* @property {number} width=0.01 The width of the parabola, in meters.
|
||||
* @property {boolean} isVisibleInSecondaryCamera=false The width of the parabola, in meters.
|
||||
*/
|
||||
/**jsdoc
|
||||
* A set of properties used to define the visual aspect of a Parabola Pointer in the case that the Pointer is not intersecting something. Same as a {@link Pointers.ParabolaPointerRenderState},
|
||||
|
@ -232,10 +233,10 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
|
|||
*/
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick.
|
||||
* @typedef {object} Pointers.LaserPointerProperties
|
||||
* @typedef {object} Pointers.ParabolaPointerProperties
|
||||
* @property {boolean} [faceAvatar=false] If true, the end of the Pointer will always rotate to face the avatar.
|
||||
* @property {boolean} [centerEndY=true] If false, the end of the Pointer will be moved up by half of its height.
|
||||
* @property {boolean} [lockEnd=false] If true, the end of the Pointer will lock on to the center of the object at which the laser is pointing.
|
||||
* @property {boolean} [lockEnd=false] If true, the end of the Pointer will lock on to the center of the object at which the pointer is pointing.
|
||||
* @property {boolean} [distanceScaleEnd=false] If true, the dimensions of the end of the Pointer will scale linearly with distance.
|
||||
* @property {boolean} [scaleWithAvatar=false] If true, the width of the Pointer's path will scale linearly with your avatar's scale.
|
||||
* @property {boolean} [followNormal=false] If true, the end of the Pointer will rotate to follow the normal of the intersected surface.
|
||||
|
|
|
@ -26,7 +26,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
SINGLETON_DEPENDENCY
|
||||
|
||||
/**jsdoc
|
||||
* The Audio API features tools to help control audio contexts and settings.
|
||||
* The <code>Audio</code> API provides facilities to interact with audio inputs and outputs and to play sounds.
|
||||
*
|
||||
* @namespace Audio
|
||||
*
|
||||
|
@ -35,14 +35,23 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {boolean} muted
|
||||
* @property {boolean} noiseReduction
|
||||
* @property {number} inputVolume
|
||||
* @property {number} inputLevel <em>Read-only.</em>
|
||||
* @property {string} context <em>Read-only.</em>
|
||||
* @property {} devices <em>Read-only.</em>
|
||||
* @property {boolean} muted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
|
||||
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
|
||||
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
|
||||
* above the noise floor.
|
||||
* @property {number} inputLevel - The loudness of the audio input, range <code>0.0</code> (no sound) –
|
||||
* <code>1.0</code> (the onset of clipping). <em>Read-only.</em>
|
||||
* @property {number} inputVolume - Adjusts the volume of the input audio; range <code>0.0</code> – <code>1.0</code>.
|
||||
* If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some
|
||||
* devices, and others might only support values of <code>0.0</code> and <code>1.0</code>.
|
||||
* @property {boolean} isStereoInput - <code>true</code> if the input audio is being used in stereo, otherwise
|
||||
* <code>false</code>. Some devices do not support stereo, in which case the value is always <code>false</code>.
|
||||
* @property {string} context - The current context of the audio: either <code>"Desktop"</code> or <code>"HMD"</code>.
|
||||
* <em>Read-only.</em>
|
||||
* @property {object} devices <em>Read-only.</em> <strong>Deprecated:</strong> This property is deprecated and will be
|
||||
* removed.
|
||||
*/
|
||||
|
||||
|
||||
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
|
||||
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
|
||||
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
|
||||
|
@ -69,45 +78,91 @@ public:
|
|||
|
||||
/**jsdoc
|
||||
* @function Audio.setInputDevice
|
||||
* @param {} device
|
||||
* @param {object} device
|
||||
* @param {boolean} isHMD
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.setOutputDevice
|
||||
* @param {} device
|
||||
* @param {object} device
|
||||
* @param {boolean} isHMD
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
|
||||
/**jsdoc
|
||||
* Enable or disable reverberation. Reverberation is done by the client, on the post-mix audio. The reverberation options
|
||||
* come from either the domain's audio zone if used — configured on the server — or as scripted by
|
||||
* {@link Audio.setReverbOptions|setReverbOptions}.
|
||||
* @function Audio.setReverb
|
||||
* @param {boolean} enable
|
||||
*/
|
||||
* @param {boolean} enable - <code>true</code> to enable reverberation, <code>false</code> to disable.
|
||||
* @example <caption>Enable reverberation for a short while.</caption>
|
||||
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
|
||||
* var injector;
|
||||
* var injectorOptions = {
|
||||
* position: MyAvatar.position
|
||||
* };
|
||||
*
|
||||
* Script.setTimeout(function () {
|
||||
* print("Reverb OFF");
|
||||
* Audio.setReverb(false);
|
||||
* injector = Audio.playSound(sound, injectorOptions);
|
||||
* }, 1000);
|
||||
*
|
||||
* Script.setTimeout(function () {
|
||||
* var reverbOptions = new AudioEffectOptions();
|
||||
* reverbOptions.roomSize = 100;
|
||||
* Audio.setReverbOptions(reverbOptions);
|
||||
* print("Reverb ON");
|
||||
* Audio.setReverb(true);
|
||||
* }, 4000);
|
||||
*
|
||||
* Script.setTimeout(function () {
|
||||
* print("Reverb OFF");
|
||||
* Audio.setReverb(false);
|
||||
* }, 8000); */
|
||||
Q_INVOKABLE void setReverb(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* Configure reverberation options. Use {@link Audio.setReverb|setReverb} to enable or disable reverberation.
|
||||
* @function Audio.setReverbOptions
|
||||
* @param {AudioEffectOptions} options
|
||||
* @param {AudioEffectOptions} options - The reverberation options.
|
||||
*/
|
||||
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
|
||||
|
||||
/**jsdoc
|
||||
* Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format.
|
||||
* @function Audio.startRecording
|
||||
* @param {string} filename
|
||||
* @returns {boolean}
|
||||
* @param {string} filename - The path and name of the file to make the recording in. Should have a <code>.wav</code>
|
||||
* extension. The file is overwritten if it already exists.
|
||||
* @returns {boolean} <code>true</code> if the specified file could be opened and audio recording has started, otherwise
|
||||
* <code>false</code>.
|
||||
* @example <caption>Make a 10 second audio recording.</caption>
|
||||
* var filename = File.getTempDir() + "/audio.wav";
|
||||
* if (Audio.startRecording(filename)) {
|
||||
* Script.setTimeout(function () {
|
||||
* Audio.stopRecording();
|
||||
* print("Audio recording made in: " + filename);
|
||||
* }, 10000);
|
||||
*
|
||||
* } else {
|
||||
* print("Could not make an audio recording in: " + filename);
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE bool startRecording(const QString& filename);
|
||||
|
||||
/**jsdoc
|
||||
* Finish making an audio recording started with {@link Audio.startRecording|startRecording}.
|
||||
* @function Audio.stopRecording
|
||||
*/
|
||||
Q_INVOKABLE void stopRecording();
|
||||
|
||||
/**jsdoc
|
||||
* Check whether an audio recording is currently being made.
|
||||
* @function Audio.getRecording
|
||||
* @returns {boolean}
|
||||
* @returns {boolean} <code>true</code> if an audio recording is currently being made, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE bool getRecording();
|
||||
|
||||
|
@ -116,40 +171,54 @@ signals:
|
|||
/**jsdoc
|
||||
* @function Audio.nop
|
||||
* @returns {Signal}
|
||||
* @deprecated This signal is deprecated and will be removed.
|
||||
*/
|
||||
void nop();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the audio input is muted or unmuted.
|
||||
* @function Audio.mutedChanged
|
||||
* @param {boolean} isMuted
|
||||
* @param {boolean} isMuted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report when audio input is muted or unmuted</caption>
|
||||
* Audio.mutedChanged.connect(function (isMuted) {
|
||||
* print("Audio muted: " + isMuted);
|
||||
* });
|
||||
*/
|
||||
void mutedChanged(bool isMuted);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the audio input noise reduction is enabled or disabled.
|
||||
* @function Audio.noiseReductionChanged
|
||||
* @param {boolean} isEnabled
|
||||
* @param {boolean} isEnabled - <code>true</code> if audio input noise reduction is enabled, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void noiseReductionChanged(bool isEnabled);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the input audio volume changes.
|
||||
* @function Audio.inputVolumeChanged
|
||||
* @param {number} volume
|
||||
* @param {number} volume - The requested volume to be applied to the audio input, range <code>0.0</code> –
|
||||
* <code>1.0</code>. The resulting value of <code>Audio.inputVolume</code> depends on the capabilities of the device:
|
||||
* for example, the volume can't be changed on some devices, and others might only support values of <code>0.0</code>
|
||||
* and <code>1.0</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void inputVolumeChanged(float volume);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the input audio level changes.
|
||||
* @function Audio.inputLevelChanged
|
||||
* @param {number} level
|
||||
* @param {number} level - The loudness of the input audio, range <code>0.0</code> (no sound) – <code>1.0</code> (the
|
||||
* onset of clipping).
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void inputLevelChanged(float level);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the current context of the audio changes.
|
||||
* @function Audio.contextChanged
|
||||
* @param {string} context
|
||||
* @param {string} context - The current context of the audio: either <code>"Desktop"</code> or <code>"HMD"</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void contextChanged(const QString& context);
|
||||
|
@ -158,7 +227,7 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* @function Audio.onContextChanged
|
||||
* @returns {Signal}
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
void onContextChanged();
|
||||
|
||||
|
|
|
@ -56,8 +56,7 @@ class QScriptEngine;
|
|||
* @property {Uuid} tabletID - The UUID of the tablet body model overlay.
|
||||
* @property {Uuid} tabletScreenID - The UUID of the tablet's screen overlay.
|
||||
* @property {Uuid} homeButtonID - The UUID of the tablet's "home" button overlay.
|
||||
* @property {Uuid} homeButtonHighlightMaterialID - The UUID of the material entity used to highlight tablet button
|
||||
* @property {Uuid} homeButtonUnhighlightMaterialID - The UUID of the material entity use to unhighlight the entity
|
||||
* @property {Uuid} homeButtonHighlightID - The UUID of the tablet's "home" button highlight overlay.
|
||||
*/
|
||||
class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -69,11 +68,10 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
|
|||
Q_PROPERTY(QUuid tabletID READ getCurrentTabletFrameID WRITE setCurrentTabletFrameID)
|
||||
Q_PROPERTY(QUuid homeButtonID READ getCurrentHomeButtonID WRITE setCurrentHomeButtonID)
|
||||
Q_PROPERTY(QUuid tabletScreenID READ getCurrentTabletScreenID WRITE setCurrentTabletScreenID)
|
||||
Q_PROPERTY(QUuid homeButtonHighlightMaterialID READ getCurrentHomeButtonHighlightMaterialID WRITE setCurrentHomeButtonHighlightMaterialID)
|
||||
Q_PROPERTY(QUuid homeButtonUnhighlightMaterialID READ getCurrentHomeButtonUnhighlightMaterialID WRITE setCurrentHomeButtonUnhighlightMaterialID)
|
||||
Q_PROPERTY(QUuid homeButtonHighlightID READ getCurrentHomeButtonHighlightID WRITE setCurrentHomeButtonHighlightID)
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Calculate the intersection of a ray with the HUD overlay.
|
||||
* @function HMD.calculateRayUICollisionPoint
|
||||
|
@ -347,17 +345,6 @@ signals:
|
|||
*/
|
||||
bool shouldShowHandControllersChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the <code>HMD.mounted</code> property value changes.
|
||||
* @function HMD.mountedChanged
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report when there's a change in the HMD being worn.</caption>
|
||||
* HMD.mountedChanged.connect(function () {
|
||||
* print("Mounted changed. HMD is mounted: " + HMD.mounted);
|
||||
* });
|
||||
*/
|
||||
void mountedChanged();
|
||||
|
||||
public:
|
||||
HMDScriptingInterface();
|
||||
static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine);
|
||||
|
@ -376,15 +363,12 @@ public:
|
|||
void setCurrentHomeButtonID(QUuid homeButtonID) { _homeButtonID = homeButtonID; }
|
||||
QUuid getCurrentHomeButtonID() const { return _homeButtonID; }
|
||||
|
||||
void setCurrentHomeButtonHighlightID(QUuid homeButtonHighlightID) { _homeButtonHighlightID = homeButtonHighlightID; }
|
||||
QUuid getCurrentHomeButtonHighlightID() const { return _homeButtonHighlightID; }
|
||||
|
||||
void setCurrentTabletScreenID(QUuid tabletID) { _tabletScreenID = tabletID; }
|
||||
QUuid getCurrentTabletScreenID() const { return _tabletScreenID; }
|
||||
|
||||
void setCurrentHomeButtonHighlightMaterialID(QUuid homeButtonHighlightMaterialID) { _homeButtonHighlightMaterialID = homeButtonHighlightMaterialID; }
|
||||
QUuid getCurrentHomeButtonHighlightMaterialID() { return _homeButtonHighlightMaterialID; }
|
||||
|
||||
void setCurrentHomeButtonUnhighlightMaterialID(QUuid homeButtonUnhighlightMaterialID) { _homeButtonUnhighlightMaterialID = homeButtonUnhighlightMaterialID; }
|
||||
QUuid getCurrentHomeButtonUnhighlightMaterialID() { return _homeButtonUnhighlightMaterialID; }
|
||||
|
||||
private:
|
||||
bool _showTablet { false };
|
||||
bool _tabletContextualMode { false };
|
||||
|
@ -392,8 +376,7 @@ private:
|
|||
QUuid _tabletScreenID; // this is the overlayID which is part of (a child of) the tablet-ui.
|
||||
QUuid _homeButtonID;
|
||||
QUuid _tabletEntityID;
|
||||
QUuid _homeButtonHighlightMaterialID;
|
||||
QUuid _homeButtonUnhighlightMaterialID;
|
||||
QUuid _homeButtonHighlightID;
|
||||
|
||||
// Get the position of the HMD
|
||||
glm::vec3 getPosition() const;
|
||||
|
|
|
@ -266,42 +266,6 @@ void setupPreferences() {
|
|||
preferences->addPreference(new SliderPreference(FACE_TRACKING, "Eye Deflection", getter, setter));
|
||||
}
|
||||
|
||||
static const QString MOVEMENT{ "Movement" };
|
||||
{
|
||||
|
||||
static const QString movementsControlChannel = QStringLiteral("Hifi-Advanced-Movement-Disabler");
|
||||
auto getter = [=]()->bool { return myAvatar->useAdvancedMovementControls(); };
|
||||
auto setter = [=](bool value) { myAvatar->setUseAdvancedMovementControls(value); };
|
||||
preferences->addPreference(new CheckPreference(MOVEMENT,
|
||||
QStringLiteral("Advanced movement for hand controllers"),
|
||||
getter, setter));
|
||||
}
|
||||
{
|
||||
auto getter = [=]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
|
||||
auto setter = [=](int value) { myAvatar->setSnapTurn(value == 0); };
|
||||
auto preference = new RadioButtonsPreference(MOVEMENT, "Snap turn / Smooth turn", getter, setter);
|
||||
QStringList items;
|
||||
items << "Snap turn" << "Smooth turn";
|
||||
preference->setItems(items);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [=]()->float { return myAvatar->getUserHeight(); };
|
||||
auto setter = [=](float value) { myAvatar->setUserHeight(value); };
|
||||
auto preference = new SpinnerPreference(MOVEMENT, "User real-world height (meters)", getter, setter);
|
||||
preference->setMin(1.0f);
|
||||
preference->setMax(2.2f);
|
||||
preference->setDecimals(3);
|
||||
preference->setStep(0.001f);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto preference = new ButtonPreference(MOVEMENT, "RESET SENSORS", [] {
|
||||
qApp->resetSensors();
|
||||
});
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
static const QString VR_MOVEMENT{ "VR Movement" };
|
||||
{
|
||||
|
||||
|
@ -315,7 +279,7 @@ void setupPreferences() {
|
|||
{
|
||||
auto getter = [=]()->bool { return myAvatar->getFlyingHMDPref(); };
|
||||
auto setter = [=](bool value) { myAvatar->setFlyingHMDPref(value); };
|
||||
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping", getter, setter));
|
||||
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping (HMD)", getter, setter));
|
||||
}
|
||||
{
|
||||
auto getter = [=]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
#include "scripting/AccountServicesScriptingInterface.h"
|
||||
#include <plugins/InputConfiguration.h>
|
||||
#include "ui/Snapshot.h"
|
||||
#include "SoundCache.h"
|
||||
#include "SoundCacheScriptingInterface.h"
|
||||
#include "raypick/PointerScriptingInterface.h"
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
#include "AboutUtil.h"
|
||||
|
@ -253,7 +253,7 @@ void Web3DOverlay::setupQmlSurface() {
|
|||
_webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get<AvatarManager>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get<InputConfiguration>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
_webSurface->getSurfaceContext()->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
|
||||
|
|
|
@ -14,11 +14,7 @@
|
|||
|
||||
#include <StencilMaskPass.h>
|
||||
#include <GeometryCache.h>
|
||||
|
||||
#include "render-utils/drawWorkloadProxy_vert.h"
|
||||
#include "render-utils/drawWorkloadView_vert.h"
|
||||
#include "render-utils/drawWorkloadProxy_frag.h"
|
||||
#include "render-utils/drawWorkloadView_frag.h"
|
||||
#include <shaders/Shaders.h>
|
||||
|
||||
|
||||
void GameSpaceToRender::configure(const Config& config) {
|
||||
|
@ -149,14 +145,7 @@ void GameWorkloadRenderItem::setAllViews(const workload::Views& views) {
|
|||
|
||||
const gpu::PipelinePointer GameWorkloadRenderItem::getProxiesPipeline() {
|
||||
if (!_drawAllProxiesPipeline) {
|
||||
auto vs = drawWorkloadProxy_vert::getShader();
|
||||
auto ps = drawWorkloadProxy_frag::getShader();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("workloadProxiesBuffer", 0));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::drawWorkloadProxy);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
/* state->setBlendFunction(true,
|
||||
|
@ -173,15 +162,7 @@ const gpu::PipelinePointer GameWorkloadRenderItem::getProxiesPipeline() {
|
|||
|
||||
const gpu::PipelinePointer GameWorkloadRenderItem::getViewsPipeline() {
|
||||
if (!_drawAllViewsPipeline) {
|
||||
auto vs = drawWorkloadView_vert::getShader();
|
||||
auto ps = drawWorkloadView_frag::getShader();
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding("workloadViewsBuffer", 1));
|
||||
slotBindings.insert(gpu::Shader::Binding("drawMeshBuffer", 0));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::drawWorkloadView);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
/* state->setBlendFunction(true,
|
||||
|
@ -192,6 +173,7 @@ const gpu::PipelinePointer GameWorkloadRenderItem::getViewsPipeline() {
|
|||
state->setCullMode(gpu::State::CULL_NONE);
|
||||
_drawAllViewsPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
return _drawAllViewsPipeline;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ AnimBlendLinear::~AnimBlendLinear() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||
|
||||
|
@ -43,6 +43,9 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con
|
|||
|
||||
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt);
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
@ -51,7 +54,7 @@ const AnimPoseVec& AnimBlendLinear::getPosesInternal() const {
|
|||
return _poses;
|
||||
}
|
||||
|
||||
void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
||||
void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||
size_t prevPoseIndex, size_t nextPoseIndex, float dt) {
|
||||
if (prevPoseIndex == nextPoseIndex) {
|
||||
// this can happen if alpha is on an integer boundary
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
AnimBlendLinear(const QString& id, float alpha);
|
||||
virtual ~AnimBlendLinear() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||
|
||||
|
@ -38,7 +38,7 @@ protected:
|
|||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
||||
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
||||
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||
size_t prevPoseIndex, size_t nextPoseIndex, float dt);
|
||||
|
||||
AnimPoseVec _poses;
|
||||
|
|
|
@ -26,7 +26,7 @@ AnimBlendLinearMove::~AnimBlendLinearMove() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
assert(_children.size() == _characteristicSpeeds.size());
|
||||
|
||||
|
@ -54,6 +54,9 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars,
|
|||
setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut);
|
||||
evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime);
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
@ -62,7 +65,7 @@ const AnimPoseVec& AnimBlendLinearMove::getPosesInternal() const {
|
|||
return _poses;
|
||||
}
|
||||
|
||||
void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
||||
void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||
size_t prevPoseIndex, size_t nextPoseIndex,
|
||||
float prevDeltaTime, float nextDeltaTime) {
|
||||
if (prevPoseIndex == nextPoseIndex) {
|
||||
|
@ -82,7 +85,7 @@ void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVar
|
|||
}
|
||||
|
||||
void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
|
||||
float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut) {
|
||||
float* prevDeltaTimeOut, float* nextDeltaTimeOut, AnimVariantMap& triggersOut) {
|
||||
|
||||
const float FRAMES_PER_SECOND = 30.0f;
|
||||
auto prevClipNode = std::dynamic_pointer_cast<AnimClip>(_children[prevPoseIndex]);
|
||||
|
@ -109,7 +112,7 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn
|
|||
|
||||
// detect loop trigger events
|
||||
if (_phase >= 1.0f) {
|
||||
triggersOut.push_back(_id + "Loop");
|
||||
triggersOut.setTrigger(_id + "Loop");
|
||||
_phase = glm::fract(_phase);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ public:
|
|||
AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector<float>& characteristicSpeeds);
|
||||
virtual ~AnimBlendLinearMove() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||
void setDesiredSpeedVar(const QString& desiredSpeedVar) { _desiredSpeedVar = desiredSpeedVar; }
|
||||
|
@ -48,12 +48,12 @@ protected:
|
|||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
||||
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha,
|
||||
void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, AnimVariantMap& triggersOut, float alpha,
|
||||
size_t prevPoseIndex, size_t nextPoseIndex,
|
||||
float prevDeltaTime, float nextDeltaTime);
|
||||
|
||||
void setFrameAndPhase(float dt, float alpha, int prevPoseIndex, int nextPoseIndex,
|
||||
float* prevDeltaTimeOut, float* nextDeltaTimeOut, Triggers& triggersOut);
|
||||
float* prevDeltaTimeOut, float* nextDeltaTimeOut, AnimVariantMap& triggersOut);
|
||||
|
||||
virtual void setCurrentFrameInternal(float frame) override;
|
||||
|
||||
|
|
159
libraries/animation/src/AnimChain.h
Normal file
159
libraries/animation/src/AnimChain.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
//
|
||||
// AnimChain.h
|
||||
//
|
||||
// Created by Anthony J. Thibault on 7/16/2018.
|
||||
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AnimChain
|
||||
#define hifi_AnimChain
|
||||
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <DebugDraw.h>
|
||||
|
||||
template <int N>
|
||||
class AnimChainT {
|
||||
|
||||
public:
|
||||
AnimChainT() {}
|
||||
|
||||
AnimChainT(const AnimChainT& orig) {
|
||||
_top = orig._top;
|
||||
for (int i = 0; i < _top; i++) {
|
||||
_chain[i] = orig._chain[i];
|
||||
}
|
||||
}
|
||||
|
||||
AnimChainT& operator=(const AnimChainT& orig) {
|
||||
_top = orig._top;
|
||||
for (int i = 0; i < _top; i++) {
|
||||
_chain[i] = orig._chain[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool buildFromRelativePoses(const AnimSkeleton::ConstPointer& skeleton, const AnimPoseVec& relativePoses, int tipIndex) {
|
||||
_top = 0;
|
||||
// iterate through the skeleton parents, from the tip to the base, copying over relativePoses into the chain.
|
||||
for (int jointIndex = tipIndex; jointIndex != -1; jointIndex = skeleton->getParentIndex(jointIndex)) {
|
||||
if (_top >= N) {
|
||||
assert(_top < N);
|
||||
// stack overflow
|
||||
return false;
|
||||
}
|
||||
_chain[_top].relativePose = relativePoses[jointIndex];
|
||||
_chain[_top].jointIndex = jointIndex;
|
||||
_chain[_top].dirty = true;
|
||||
_top++;
|
||||
}
|
||||
|
||||
buildDirtyAbsolutePoses();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const AnimPose& getAbsolutePoseFromJointIndex(int jointIndex) const {
|
||||
for (int i = 0; i < _top; i++) {
|
||||
if (_chain[i].jointIndex == jointIndex) {
|
||||
return _chain[i].absolutePose;
|
||||
}
|
||||
}
|
||||
return AnimPose::identity;
|
||||
}
|
||||
|
||||
bool setRelativePoseAtJointIndex(int jointIndex, const AnimPose& relativePose) {
|
||||
bool foundIndex = false;
|
||||
for (int i = _top - 1; i >= 0; i--) {
|
||||
if (_chain[i].jointIndex == jointIndex) {
|
||||
_chain[i].relativePose = relativePose;
|
||||
foundIndex = true;
|
||||
}
|
||||
// all child absolute poses are now dirty
|
||||
if (foundIndex) {
|
||||
_chain[i].dirty = true;
|
||||
}
|
||||
}
|
||||
return foundIndex;
|
||||
}
|
||||
|
||||
void buildDirtyAbsolutePoses() {
|
||||
// the relative and absolute pose is the same for the base of the chain.
|
||||
_chain[_top - 1].absolutePose = _chain[_top - 1].relativePose;
|
||||
_chain[_top - 1].dirty = false;
|
||||
|
||||
// iterate chain from base to tip, concatinating the relative poses to build the absolute poses.
|
||||
for (int i = _top - 1; i > 0; i--) {
|
||||
AnimChainElem& parent = _chain[i];
|
||||
AnimChainElem& child = _chain[i - 1];
|
||||
|
||||
if (child.dirty) {
|
||||
child.absolutePose = parent.absolutePose * child.relativePose;
|
||||
child.dirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void blend(const AnimChainT& srcChain, float alpha) {
|
||||
// make sure chains have same lengths
|
||||
assert(srcChain._top == _top);
|
||||
if (srcChain._top != _top) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only blend the relative poses
|
||||
for (int i = 0; i < _top; i++) {
|
||||
_chain[i].relativePose.blend(srcChain._chain[i].relativePose, alpha);
|
||||
_chain[i].dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
int size() const {
|
||||
return _top;
|
||||
}
|
||||
|
||||
void outputRelativePoses(AnimPoseVec& relativePoses) {
|
||||
for (int i = 0; i < _top; i++) {
|
||||
relativePoses[_chain[i].jointIndex] = _chain[i].relativePose;
|
||||
}
|
||||
}
|
||||
|
||||
void debugDraw(const glm::mat4& geomToWorldMat, const glm::vec4& color) const {
|
||||
for (int i = 1; i < _top; i++) {
|
||||
glm::vec3 start = transformPoint(geomToWorldMat, _chain[i - 1].absolutePose.trans());
|
||||
glm::vec3 end = transformPoint(geomToWorldMat, _chain[i].absolutePose.trans());
|
||||
DebugDraw::getInstance().drawRay(start, end, color);
|
||||
}
|
||||
}
|
||||
|
||||
void dump() const {
|
||||
for (int i = 0; i < _top; i++) {
|
||||
qWarning() << "AJT: AnimPoseElem[" << i << "]";
|
||||
qWarning() << "AJT: relPose =" << _chain[i].relativePose;
|
||||
qWarning() << "AJT: absPose =" << _chain[i].absolutePose;
|
||||
qWarning() << "AJT: jointIndex =" << _chain[i].jointIndex;
|
||||
qWarning() << "AJT: dirty =" << _chain[i].dirty;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
struct AnimChainElem {
|
||||
AnimPose relativePose;
|
||||
AnimPose absolutePose;
|
||||
int jointIndex { -1 };
|
||||
bool dirty { true };
|
||||
};
|
||||
|
||||
AnimChainElem _chain[N];
|
||||
int _top { 0 };
|
||||
};
|
||||
|
||||
using AnimChain = AnimChainT<10>;
|
||||
|
||||
#endif
|
|
@ -30,7 +30,7 @@ AnimClip::~AnimClip() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
// lookup parameters from animVars, using current instance variables as defaults.
|
||||
_startFrame = animVars.lookup(_startFrameVar, _startFrame);
|
||||
|
@ -77,6 +77,8 @@ const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const Anim
|
|||
::blend(_poses.size(), &prevFrame[0], &nextFrame[0], alpha, &_poses[0]);
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
@ -89,7 +91,7 @@ void AnimClip::loadURL(const QString& url) {
|
|||
void AnimClip::setCurrentFrameInternal(float frame) {
|
||||
// because dt is 0, we should not encounter any triggers
|
||||
const float dt = 0.0f;
|
||||
Triggers triggers;
|
||||
AnimVariantMap triggers;
|
||||
_frame = ::accumulateTime(_startFrame, _endFrame, _timeScale, frame + _startFrame, dt, _loopFlag, _id, triggers);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag);
|
||||
virtual ~AnimClip() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setStartFrameVar(const QString& startFrameVar) { _startFrameVar = startFrameVar; }
|
||||
void setEndFrameVar(const QString& endFrameVar) { _endFrameVar = endFrameVar; }
|
||||
|
|
|
@ -20,12 +20,15 @@ AnimDefaultPose::~AnimDefaultPose() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimDefaultPose::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimDefaultPose::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
if (_skeleton) {
|
||||
_poses = _skeleton->getRelativeDefaultPoses();
|
||||
} else {
|
||||
_poses.clear();
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
AnimDefaultPose(const QString& id);
|
||||
virtual ~AnimDefaultPose() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
protected:
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
|
|
@ -259,14 +259,6 @@ void AnimInverseKinematics::solve(const AnimContext& context, const std::vector<
|
|||
jointChainInfoVec[i].jointInfoVec[j].rot = safeMix(_prevJointChainInfoVec[i].jointInfoVec[j].rot, jointChainInfoVec[i].jointInfoVec[j].rot, alpha);
|
||||
jointChainInfoVec[i].jointInfoVec[j].trans = lerp(_prevJointChainInfoVec[i].jointInfoVec[j].trans, jointChainInfoVec[i].jointInfoVec[j].trans, alpha);
|
||||
}
|
||||
|
||||
// if joint chain was just disabled, ramp the weight toward zero.
|
||||
if (_prevJointChainInfoVec[i].target.getType() != IKTarget::Type::Unknown &&
|
||||
jointChainInfoVec[i].target.getType() == IKTarget::Type::Unknown) {
|
||||
IKTarget newTarget = _prevJointChainInfoVec[i].target;
|
||||
newTarget.setWeight((1.0f - alpha) * _prevJointChainInfoVec[i].target.getWeight());
|
||||
jointChainInfoVec[i].target = newTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -489,29 +481,6 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
|||
// reduce angle by a flexCoefficient
|
||||
angle *= target.getFlexCoefficient(chainDepth);
|
||||
deltaRotation = glm::angleAxis(angle, axis);
|
||||
|
||||
// The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's
|
||||
// new orientation and its target. This is the final parent-relative orientation that the tip joint have
|
||||
// make to achieve its target orientation.
|
||||
glm::quat tipRelativeRotation = glm::inverse(deltaRotation * tipParentOrientation) * target.getRotation();
|
||||
|
||||
// enforce tip's constraint
|
||||
RotationConstraint* constraint = getConstraint(tipIndex);
|
||||
if (constraint) {
|
||||
bool constrained = constraint->apply(tipRelativeRotation);
|
||||
if (constrained) {
|
||||
// The tip's final parent-relative rotation would violate its constraint
|
||||
// so we try to pre-twist this pivot to compensate.
|
||||
glm::quat constrainedTipRotation = deltaRotation * tipParentOrientation * tipRelativeRotation;
|
||||
glm::quat missingRotation = target.getRotation() * glm::inverse(constrainedTipRotation);
|
||||
glm::quat swingPart;
|
||||
glm::quat twistPart;
|
||||
glm::vec3 axis = glm::normalize(deltaRotation * leverArm);
|
||||
swingTwistDecomposition(missingRotation, axis, swingPart, twistPart);
|
||||
const float LIMIT_LEAK_FRACTION = 0.1f;
|
||||
deltaRotation = safeLerp(glm::quat(), twistPart, LIMIT_LEAK_FRACTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (targetType == IKTarget::Type::HmdHead) {
|
||||
|
@ -874,14 +843,14 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
|
|||
}
|
||||
|
||||
//virtual
|
||||
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
// don't call this function, call overlay() instead
|
||||
assert(false);
|
||||
return _relativePoses;
|
||||
}
|
||||
|
||||
//virtual
|
||||
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
||||
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) {
|
||||
#ifdef Q_OS_ANDROID
|
||||
// disable IK on android
|
||||
return underPoses;
|
||||
|
@ -961,6 +930,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
PROFILE_RANGE_EX(simulation_animation, "ik/shiftHips", 0xffff00ff, 0);
|
||||
|
||||
if (_hipsTargetIndex >= 0) {
|
||||
|
||||
assert(_hipsTargetIndex < (int)targets.size());
|
||||
|
||||
// slam the hips to match the _hipsTarget
|
||||
|
@ -1045,6 +1015,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
PROFILE_RANGE_EX(simulation_animation, "ik/ccd", 0xffff00ff, 0);
|
||||
|
||||
setSecondaryTargets(context);
|
||||
|
||||
preconditionRelativePosesToAvoidLimbLock(context, targets);
|
||||
|
||||
solve(context, targets, dt, jointChainInfoVec);
|
||||
|
@ -1056,6 +1027,8 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
}
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _relativePoses;
|
||||
}
|
||||
|
||||
|
@ -1259,48 +1232,7 @@ void AnimInverseKinematics::initConstraints() {
|
|||
|
||||
constraint = static_cast<RotationConstraint*>(stConstraint);
|
||||
} else if (0 == baseName.compare("Hand", Qt::CaseSensitive)) {
|
||||
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
|
||||
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot());
|
||||
stConstraint->setTwistLimits(0.0f, 0.0f); // max == min, disables twist limits
|
||||
|
||||
/* KEEP THIS CODE for future experimentation -- twist limits for hands
|
||||
const float MAX_HAND_TWIST = 3.0f * PI / 5.0f;
|
||||
const float MIN_HAND_TWIST = -PI / 2.0f;
|
||||
if (isLeft) {
|
||||
stConstraint->setTwistLimits(-MAX_HAND_TWIST, -MIN_HAND_TWIST);
|
||||
} else {
|
||||
stConstraint->setTwistLimits(MIN_HAND_TWIST, MAX_HAND_TWIST);
|
||||
}
|
||||
*/
|
||||
|
||||
/* KEEP THIS CODE for future experimentation -- non-symmetrical swing limits for wrist
|
||||
* a more complicated wrist with asymmetric cone
|
||||
// these directions are approximate swing limits in parent-frame
|
||||
// NOTE: they don't need to be normalized
|
||||
std::vector<glm::vec3> swungDirections;
|
||||
swungDirections.push_back(glm::vec3(1.0f, 1.0f, 0.0f));
|
||||
swungDirections.push_back(glm::vec3(0.75f, 1.0f, -1.0f));
|
||||
swungDirections.push_back(glm::vec3(-0.75f, 1.0f, -1.0f));
|
||||
swungDirections.push_back(glm::vec3(-1.0f, 1.0f, 0.0f));
|
||||
swungDirections.push_back(glm::vec3(-0.75f, 1.0f, 1.0f));
|
||||
swungDirections.push_back(glm::vec3(0.75f, 1.0f, 1.0f));
|
||||
|
||||
// rotate directions into joint-frame
|
||||
glm::quat invRelativeRotation = glm::inverse(_defaultRelativePoses[i].rot);
|
||||
int numDirections = (int)swungDirections.size();
|
||||
for (int j = 0; j < numDirections; ++j) {
|
||||
swungDirections[j] = invRelativeRotation * swungDirections[j];
|
||||
}
|
||||
stConstraint->setSwingLimits(swungDirections);
|
||||
*/
|
||||
|
||||
// simple cone
|
||||
std::vector<float> minDots;
|
||||
const float MAX_HAND_SWING = PI / 2.0f;
|
||||
minDots.push_back(cosf(MAX_HAND_SWING));
|
||||
stConstraint->setSwingLimits(minDots);
|
||||
|
||||
constraint = static_cast<RotationConstraint*>(stConstraint);
|
||||
// hand/wrist constraints have been disabled.
|
||||
} else if (baseName.startsWith("Shoulder", Qt::CaseSensitive)) {
|
||||
SwingTwistConstraint* stConstraint = new SwingTwistConstraint();
|
||||
stConstraint->setReferenceRotation(_defaultRelativePoses[i].rot());
|
||||
|
@ -1750,7 +1682,7 @@ void AnimInverseKinematics::preconditionRelativePosesToAvoidLimbLock(const AnimC
|
|||
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
||||
|
||||
for (auto& target : targets) {
|
||||
if (target.getIndex() != -1) {
|
||||
if (target.getIndex() != -1 && target.getType() == IKTarget::Type::RotationAndPosition) {
|
||||
for (int i = 0; i < NUM_LIMBS; i++) {
|
||||
if (limbs[i].first == target.getIndex()) {
|
||||
int tipIndex = limbs[i].first;
|
||||
|
@ -1843,6 +1775,10 @@ void AnimInverseKinematics::initRelativePosesFromSolutionSource(SolutionSource s
|
|||
default:
|
||||
case SolutionSource::RelaxToUnderPoses:
|
||||
blendToPoses(underPoses, underPoses, RELAX_BLEND_FACTOR);
|
||||
// special case for hips: don't dampen hip motion from underposes
|
||||
if (_hipsIndex >= 0 && _hipsIndex < (int)_relativePoses.size()) {
|
||||
_relativePoses[_hipsIndex] = underPoses[_hipsIndex];
|
||||
}
|
||||
break;
|
||||
case SolutionSource::RelaxToLimitCenterPoses:
|
||||
blendToPoses(_limitCenterPoses, underPoses, RELAX_BLEND_FACTOR);
|
||||
|
|
|
@ -52,8 +52,8 @@ public:
|
|||
const QString& typeVar, const QString& weightVar, float weight, const std::vector<float>& flexCoefficients,
|
||||
const QString& poleVectorEnabledVar, const QString& poleReferenceVectorVar, const QString& poleVectorVar);
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) override;
|
||||
|
||||
void clearIKJointLimitHistory();
|
||||
|
||||
|
|
|
@ -32,11 +32,11 @@ AnimManipulator::~AnimManipulator() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
return overlay(animVars, context, dt, triggersOut, _skeleton->getRelativeDefaultPoses());
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
||||
const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) {
|
||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||
|
||||
_poses = underPoses;
|
||||
|
@ -74,6 +74,8 @@ const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, cons
|
|||
}
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ public:
|
|||
AnimManipulator(const QString& id, float alpha);
|
||||
virtual ~AnimManipulator() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut, const AnimPoseVec& underPoses) override;
|
||||
|
||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||
|
||||
|
|
|
@ -59,3 +59,19 @@ void AnimNode::setCurrentFrame(float frame) {
|
|||
child->setCurrentFrameInternal(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimNode::processOutputJoints(AnimVariantMap& triggersOut) const {
|
||||
if (!_skeleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto&& jointName : _outputJointNames) {
|
||||
// TODO: cache the jointIndices
|
||||
int jointIndex = _skeleton->nameToJointIndex(jointName);
|
||||
if (jointIndex >= 0) {
|
||||
AnimPose pose = _skeleton->getAbsolutePose(jointIndex, getPosesInternal());
|
||||
triggersOut.set(_id + jointName + "Rotation", pose.rot());
|
||||
triggersOut.set(_id + jointName + "Position", pose.trans());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,11 +45,12 @@ public:
|
|||
Manipulator,
|
||||
InverseKinematics,
|
||||
DefaultPose,
|
||||
TwoBoneIK,
|
||||
PoleVectorConstraint,
|
||||
NumTypes
|
||||
};
|
||||
using Pointer = std::shared_ptr<AnimNode>;
|
||||
using ConstPointer = std::shared_ptr<const AnimNode>;
|
||||
using Triggers = std::vector<QString>;
|
||||
|
||||
friend class AnimDebugDraw;
|
||||
friend void buildChildMap(std::map<QString, Pointer>& map, Pointer node);
|
||||
|
@ -61,6 +62,8 @@ public:
|
|||
const QString& getID() const { return _id; }
|
||||
Type getType() const { return _type; }
|
||||
|
||||
void addOutputJoint(const QString& outputJointName) { _outputJointNames.push_back(outputJointName); }
|
||||
|
||||
// hierarchy accessors
|
||||
Pointer getParent();
|
||||
void addChild(Pointer child);
|
||||
|
@ -74,8 +77,8 @@ public:
|
|||
|
||||
AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; }
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) = 0;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut,
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) = 0;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut,
|
||||
const AnimPoseVec& underPoses) {
|
||||
return evaluate(animVars, context, dt, triggersOut);
|
||||
}
|
||||
|
@ -114,11 +117,14 @@ protected:
|
|||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const = 0;
|
||||
|
||||
void processOutputJoints(AnimVariantMap& triggersOut) const;
|
||||
|
||||
Type _type;
|
||||
QString _id;
|
||||
std::vector<AnimNode::Pointer> _children;
|
||||
AnimSkeleton::ConstPointer _skeleton;
|
||||
std::weak_ptr<AnimNode> _parent;
|
||||
std::vector<QString> _outputJointNames;
|
||||
|
||||
// no copies
|
||||
AnimNode(const AnimNode&) = delete;
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include "AnimManipulator.h"
|
||||
#include "AnimInverseKinematics.h"
|
||||
#include "AnimDefaultPose.h"
|
||||
#include "AnimTwoBoneIK.h"
|
||||
#include "AnimPoleVectorConstraint.h"
|
||||
|
||||
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
@ -38,6 +40,8 @@ static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const
|
|||
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadTwoBoneIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
||||
static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
|
||||
|
||||
|
@ -56,6 +60,8 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
|
|||
case AnimNode::Type::Manipulator: return "manipulator";
|
||||
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
||||
case AnimNode::Type::DefaultPose: return "defaultPose";
|
||||
case AnimNode::Type::TwoBoneIK: return "twoBoneIK";
|
||||
case AnimNode::Type::PoleVectorConstraint: return "poleVectorConstraint";
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -116,6 +122,8 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
|||
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
||||
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
||||
case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;
|
||||
case AnimNode::Type::TwoBoneIK: return loadTwoBoneIKNode;
|
||||
case AnimNode::Type::PoleVectorConstraint: return loadPoleVectorConstraintNode;
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -131,6 +139,8 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
|||
case AnimNode::Type::Manipulator: return processDoNothing;
|
||||
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
||||
case AnimNode::Type::DefaultPose: return processDoNothing;
|
||||
case AnimNode::Type::TwoBoneIK: return processDoNothing;
|
||||
case AnimNode::Type::PoleVectorConstraint: return processDoNothing;
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -189,6 +199,25 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
|||
} \
|
||||
do {} while (0)
|
||||
|
||||
#define READ_VEC3(NAME, JSON_OBJ, ID, URL, ERROR_RETURN) \
|
||||
auto NAME##_VAL = JSON_OBJ.value(#NAME); \
|
||||
if (!NAME##_VAL.isArray()) { \
|
||||
qCCritical(animation) << "AnimNodeLoader, error reading vector" \
|
||||
<< #NAME << "id =" << ID \
|
||||
<< ", url =" << URL.toDisplayString(); \
|
||||
return ERROR_RETURN; \
|
||||
} \
|
||||
QJsonArray NAME##_ARRAY = NAME##_VAL.toArray(); \
|
||||
if (NAME##_ARRAY.size() != 3) { \
|
||||
qCCritical(animation) << "AnimNodeLoader, vector size != 3" \
|
||||
<< #NAME << "id =" << ID \
|
||||
<< ", url =" << URL.toDisplayString(); \
|
||||
return ERROR_RETURN; \
|
||||
} \
|
||||
glm::vec3 NAME((float)NAME##_ARRAY.at(0).toDouble(), \
|
||||
(float)NAME##_ARRAY.at(1).toDouble(), \
|
||||
(float)NAME##_ARRAY.at(2).toDouble())
|
||||
|
||||
static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) {
|
||||
auto idVal = jsonObj.value("id");
|
||||
if (!idVal.isString()) {
|
||||
|
@ -216,6 +245,16 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr
|
|||
}
|
||||
auto dataObj = dataValue.toObject();
|
||||
|
||||
std::vector<QString> outputJoints;
|
||||
|
||||
auto outputJoints_VAL = dataObj.value("outputJoints");
|
||||
if (outputJoints_VAL.isArray()) {
|
||||
QJsonArray outputJoints_ARRAY = outputJoints_VAL.toArray();
|
||||
for (int i = 0; i < outputJoints_ARRAY.size(); i++) {
|
||||
outputJoints.push_back(outputJoints_ARRAY.at(i).toString());
|
||||
}
|
||||
}
|
||||
|
||||
assert((int)type >= 0 && type < AnimNode::Type::NumTypes);
|
||||
auto node = (animNodeTypeToLoaderFunc(type))(dataObj, id, jsonUrl);
|
||||
if (!node) {
|
||||
|
@ -242,6 +281,9 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr
|
|||
}
|
||||
|
||||
if ((animNodeTypeToProcessFunc(type))(node, dataObj, id, jsonUrl)) {
|
||||
for (auto&& outputJoint : outputJoints) {
|
||||
node->addOutputJoint(outputJoint);
|
||||
}
|
||||
return node;
|
||||
} else {
|
||||
return nullptr;
|
||||
|
@ -531,6 +573,41 @@ static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const Q
|
|||
return node;
|
||||
}
|
||||
|
||||
static AnimNode::Pointer loadTwoBoneIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_BOOL(enabled, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_FLOAT(interpDuration, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(baseJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(midJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(tipJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_VEC3(midHingeAxis, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(alphaVar, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(enabledVar, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(endEffectorRotationVarVar, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(endEffectorPositionVarVar, jsonObj, id, jsonUrl, nullptr);
|
||||
|
||||
auto node = std::make_shared<AnimTwoBoneIK>(id, alpha, enabled, interpDuration,
|
||||
baseJointName, midJointName, tipJointName, midHingeAxis,
|
||||
alphaVar, enabledVar,
|
||||
endEffectorRotationVarVar, endEffectorPositionVarVar);
|
||||
return node;
|
||||
}
|
||||
|
||||
static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||
READ_VEC3(referenceVector, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_BOOL(enabled, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(baseJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(midJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(tipJointName, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(enabledVar, jsonObj, id, jsonUrl, nullptr);
|
||||
READ_STRING(poleVectorVar, jsonObj, id, jsonUrl, nullptr);
|
||||
|
||||
auto node = std::make_shared<AnimPoleVectorConstraint>(id, enabled, referenceVector,
|
||||
baseJointName, midJointName, tipJointName,
|
||||
enabledVar, poleVectorVar);
|
||||
return node;
|
||||
}
|
||||
|
||||
void buildChildMap(std::map<QString, int>& map, AnimNode::Pointer node) {
|
||||
for (int i = 0; i < (int)node->getChildCount(); ++i) {
|
||||
map.insert(std::pair<QString, int>(node->getChild(i)->getID(), i));
|
||||
|
@ -682,7 +759,8 @@ AnimNode::Pointer AnimNodeLoader::load(const QByteArray& contents, const QUrl& j
|
|||
QString version = versionVal.toString();
|
||||
|
||||
// check version
|
||||
if (version != "1.0") {
|
||||
// AJT: TODO version check
|
||||
if (version != "1.0" && version != "1.1") {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad version number" << version << "expected \"1.0\", url =" << jsonUrl.toDisplayString();
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ void AnimOverlay::buildBoneSet(BoneSet boneSet) {
|
|||
}
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
// lookup parameters from animVars, using current instance variables as defaults.
|
||||
// NOTE: switching bonesets can be an expensive operation, let's try to avoid it.
|
||||
|
@ -66,6 +66,9 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const A
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
AnimOverlay(const QString& id, BoneSet boneSet, float alpha);
|
||||
virtual ~AnimOverlay() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setBoneSetVar(const QString& boneSetVar) { _boneSetVar = boneSetVar; }
|
||||
void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; }
|
||||
|
|
244
libraries/animation/src/AnimPoleVectorConstraint.cpp
Normal file
244
libraries/animation/src/AnimPoleVectorConstraint.cpp
Normal file
|
@ -0,0 +1,244 @@
|
|||
//
|
||||
// AnimPoleVectorConstraint.cpp
|
||||
//
|
||||
// Created by Anthony J. Thibault on 5/12/18.
|
||||
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimPoleVectorConstraint.h"
|
||||
#include "AnimationLogging.h"
|
||||
#include "AnimUtil.h"
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
const float FRAMES_PER_SECOND = 30.0f;
|
||||
const float INTERP_DURATION = 6.0f;
|
||||
|
||||
AnimPoleVectorConstraint::AnimPoleVectorConstraint(const QString& id, bool enabled, glm::vec3 referenceVector,
|
||||
const QString& baseJointName, const QString& midJointName, const QString& tipJointName,
|
||||
const QString& enabledVar, const QString& poleVectorVar) :
|
||||
AnimNode(AnimNode::Type::PoleVectorConstraint, id),
|
||||
_enabled(enabled),
|
||||
_referenceVector(referenceVector),
|
||||
_baseJointName(baseJointName),
|
||||
_midJointName(midJointName),
|
||||
_tipJointName(tipJointName),
|
||||
_enabledVar(enabledVar),
|
||||
_poleVectorVar(poleVectorVar) {
|
||||
|
||||
}
|
||||
|
||||
AnimPoleVectorConstraint::~AnimPoleVectorConstraint() {
|
||||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimPoleVectorConstraint::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
assert(_children.size() == 1);
|
||||
if (_children.size() != 1) {
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// evalute underPoses
|
||||
AnimPoseVec underPoses = _children[0]->evaluate(animVars, context, dt, triggersOut);
|
||||
|
||||
// if we don't have a skeleton, or jointName lookup failed.
|
||||
if (!_skeleton || _baseJointIndex == -1 || _midJointIndex == -1 || _tipJointIndex == -1 || underPoses.size() == 0) {
|
||||
// pass underPoses through unmodified.
|
||||
_poses = underPoses;
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// guard against size changes
|
||||
if (underPoses.size() != _poses.size()) {
|
||||
_poses = underPoses;
|
||||
}
|
||||
|
||||
// Look up poleVector from animVars, make sure to convert into geom space.
|
||||
glm::vec3 poleVector = animVars.lookupRigToGeometryVector(_poleVectorVar, Vectors::UNIT_Z);
|
||||
|
||||
// determine if we should interpolate
|
||||
bool enabled = animVars.lookup(_enabledVar, _enabled);
|
||||
|
||||
const float MIN_LENGTH = 1.0e-4f;
|
||||
if (glm::length(poleVector) < MIN_LENGTH) {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
if (enabled != _enabled) {
|
||||
AnimChain poseChain;
|
||||
poseChain.buildFromRelativePoses(_skeleton, _poses, _tipJointIndex);
|
||||
if (enabled) {
|
||||
beginInterp(InterpType::SnapshotToSolve, poseChain);
|
||||
} else {
|
||||
beginInterp(InterpType::SnapshotToUnderPoses, poseChain);
|
||||
}
|
||||
}
|
||||
_enabled = enabled;
|
||||
|
||||
// don't build chains or do IK if we are disbled & not interping.
|
||||
if (_interpType == InterpType::None && !enabled) {
|
||||
_poses = underPoses;
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// compute chain
|
||||
AnimChain underChain;
|
||||
underChain.buildFromRelativePoses(_skeleton, underPoses, _tipJointIndex);
|
||||
AnimChain ikChain = underChain;
|
||||
|
||||
AnimPose baseParentPose = ikChain.getAbsolutePoseFromJointIndex(_baseParentJointIndex);
|
||||
AnimPose basePose = ikChain.getAbsolutePoseFromJointIndex(_baseJointIndex);
|
||||
AnimPose midPose = ikChain.getAbsolutePoseFromJointIndex(_midJointIndex);
|
||||
AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||
|
||||
// Look up refVector from animVars, make sure to convert into geom space.
|
||||
glm::vec3 refVector = midPose.xformVectorFast(_referenceVector);
|
||||
float refVectorLength = glm::length(refVector);
|
||||
|
||||
glm::vec3 axis = basePose.trans() - tipPose.trans();
|
||||
float axisLength = glm::length(axis);
|
||||
glm::vec3 unitAxis = axis / axisLength;
|
||||
|
||||
glm::vec3 sideVector = glm::cross(unitAxis, refVector);
|
||||
float sideVectorLength = glm::length(sideVector);
|
||||
|
||||
// project refVector onto axis plane
|
||||
glm::vec3 refVectorProj = refVector - glm::dot(refVector, unitAxis) * unitAxis;
|
||||
float refVectorProjLength = glm::length(refVectorProj);
|
||||
|
||||
// project poleVector on plane formed by axis.
|
||||
glm::vec3 poleVectorProj = poleVector - glm::dot(poleVector, unitAxis) * unitAxis;
|
||||
float poleVectorProjLength = glm::length(poleVectorProj);
|
||||
|
||||
// double check for zero length vectors or vectors parallel to rotaiton axis.
|
||||
if (axisLength > MIN_LENGTH && refVectorLength > MIN_LENGTH && sideVectorLength > MIN_LENGTH &&
|
||||
refVectorProjLength > MIN_LENGTH && poleVectorProjLength > MIN_LENGTH) {
|
||||
|
||||
float dot = glm::clamp(glm::dot(refVectorProj / refVectorProjLength, poleVectorProj / poleVectorProjLength), 0.0f, 1.0f);
|
||||
float sideDot = glm::dot(poleVector, sideVector);
|
||||
float theta = copysignf(1.0f, sideDot) * acosf(dot);
|
||||
|
||||
glm::quat deltaRot = glm::angleAxis(theta, unitAxis);
|
||||
|
||||
// transform result back into parent relative frame.
|
||||
glm::quat relBaseRot = glm::inverse(baseParentPose.rot()) * deltaRot * basePose.rot();
|
||||
ikChain.setRelativePoseAtJointIndex(_baseJointIndex, AnimPose(relBaseRot, underPoses[_baseJointIndex].trans()));
|
||||
|
||||
glm::quat relTipRot = glm::inverse(midPose.rot()) * glm::inverse(deltaRot) * tipPose.rot();
|
||||
ikChain.setRelativePoseAtJointIndex(_tipJointIndex, AnimPose(relTipRot, underPoses[_tipJointIndex].trans()));
|
||||
}
|
||||
|
||||
// start off by initializing output poses with the underPoses
|
||||
_poses = underPoses;
|
||||
|
||||
// apply smooth interpolation
|
||||
if (_interpType != InterpType::None) {
|
||||
_interpAlpha += _interpAlphaVel * dt;
|
||||
|
||||
if (_interpAlpha < 1.0f) {
|
||||
AnimChain interpChain;
|
||||
if (_interpType == InterpType::SnapshotToUnderPoses) {
|
||||
interpChain = underChain;
|
||||
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||
} else if (_interpType == InterpType::SnapshotToSolve) {
|
||||
interpChain = ikChain;
|
||||
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||
}
|
||||
// copy interpChain into _poses
|
||||
interpChain.outputRelativePoses(_poses);
|
||||
} else {
|
||||
// interpolation complete
|
||||
_interpType = InterpType::None;
|
||||
}
|
||||
}
|
||||
|
||||
if (_interpType == InterpType::None) {
|
||||
if (enabled) {
|
||||
// copy chain into _poses
|
||||
ikChain.outputRelativePoses(_poses);
|
||||
} else {
|
||||
// copy under chain into _poses
|
||||
underChain.outputRelativePoses(_poses);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.getEnableDebugDrawIKChains()) {
|
||||
if (_interpType == InterpType::None && enabled) {
|
||||
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
ikChain.debugDraw(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix(), BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.getEnableDebugDrawIKChains()) {
|
||||
if (enabled) {
|
||||
const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
const glm::vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
const glm::vec4 CYAN(0.0f, 1.0f, 1.0f, 1.0f);
|
||||
const glm::vec4 YELLOW(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
const float VECTOR_LENGTH = 0.5f;
|
||||
|
||||
glm::mat4 geomToWorld = context.getRigToWorldMatrix() * context.getGeometryToRigMatrix();
|
||||
|
||||
// draw the pole
|
||||
glm::vec3 start = transformPoint(geomToWorld, basePose.trans());
|
||||
glm::vec3 end = transformPoint(geomToWorld, tipPose.trans());
|
||||
DebugDraw::getInstance().drawRay(start, end, CYAN);
|
||||
|
||||
// draw the poleVector
|
||||
glm::vec3 midPoint = 0.5f * (start + end);
|
||||
glm::vec3 poleVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, poleVector));
|
||||
DebugDraw::getInstance().drawRay(midPoint, poleVectorEnd, GREEN);
|
||||
|
||||
// draw the refVector
|
||||
glm::vec3 refVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, refVector));
|
||||
DebugDraw::getInstance().drawRay(midPoint, refVectorEnd, RED);
|
||||
|
||||
// draw the sideVector
|
||||
glm::vec3 sideVector = glm::cross(poleVector, refVector);
|
||||
glm::vec3 sideVectorEnd = midPoint + VECTOR_LENGTH * glm::normalize(transformVectorFast(geomToWorld, sideVector));
|
||||
DebugDraw::getInstance().drawRay(midPoint, sideVectorEnd, YELLOW);
|
||||
}
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
const AnimPoseVec& AnimPoleVectorConstraint::getPosesInternal() const {
|
||||
return _poses;
|
||||
}
|
||||
|
||||
void AnimPoleVectorConstraint::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
|
||||
AnimNode::setSkeletonInternal(skeleton);
|
||||
lookUpIndices();
|
||||
}
|
||||
|
||||
void AnimPoleVectorConstraint::lookUpIndices() {
|
||||
assert(_skeleton);
|
||||
|
||||
// look up bone indices by name
|
||||
std::vector<int> indices = _skeleton->lookUpJointIndices({_baseJointName, _midJointName, _tipJointName});
|
||||
|
||||
// cache the results
|
||||
_baseJointIndex = indices[0];
|
||||
_midJointIndex = indices[1];
|
||||
_tipJointIndex = indices[2];
|
||||
|
||||
if (_baseJointIndex != -1) {
|
||||
_baseParentJointIndex = _skeleton->getParentIndex(_baseJointIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimPoleVectorConstraint::beginInterp(InterpType interpType, const AnimChain& chain) {
|
||||
// capture the current poses in a snapshot.
|
||||
_snapshotChain = chain;
|
||||
|
||||
_interpType = interpType;
|
||||
_interpAlphaVel = FRAMES_PER_SECOND / INTERP_DURATION;
|
||||
_interpAlpha = 0.0f;
|
||||
}
|
74
libraries/animation/src/AnimPoleVectorConstraint.h
Normal file
74
libraries/animation/src/AnimPoleVectorConstraint.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// AnimPoleVectorConstraint.h
|
||||
//
|
||||
// Created by Anthony J. Thibault on 5/25/18.
|
||||
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AnimPoleVectorConstraint_h
|
||||
#define hifi_AnimPoleVectorConstraint_h
|
||||
|
||||
#include "AnimNode.h"
|
||||
#include "AnimChain.h"
|
||||
|
||||
// Three bone IK chain
|
||||
|
||||
class AnimPoleVectorConstraint : public AnimNode {
|
||||
public:
|
||||
friend class AnimTests;
|
||||
|
||||
AnimPoleVectorConstraint(const QString& id, bool enabled, glm::vec3 referenceVector,
|
||||
const QString& baseJointName, const QString& midJointName, const QString& tipJointName,
|
||||
const QString& enabledVar, const QString& poleVectorVar);
|
||||
virtual ~AnimPoleVectorConstraint() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
protected:
|
||||
|
||||
enum class InterpType {
|
||||
None = 0,
|
||||
SnapshotToUnderPoses,
|
||||
SnapshotToSolve,
|
||||
NumTypes
|
||||
};
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
|
||||
|
||||
void lookUpIndices();
|
||||
void beginInterp(InterpType interpType, const AnimChain& chain);
|
||||
|
||||
AnimPoseVec _poses;
|
||||
|
||||
bool _enabled;
|
||||
glm::vec3 _referenceVector;
|
||||
|
||||
QString _baseJointName;
|
||||
QString _midJointName;
|
||||
QString _tipJointName;
|
||||
|
||||
QString _enabledVar;
|
||||
QString _poleVectorVar;
|
||||
|
||||
int _baseParentJointIndex { -1 };
|
||||
int _baseJointIndex { -1 };
|
||||
int _midJointIndex { -1 };
|
||||
int _tipJointIndex { -1 };
|
||||
|
||||
InterpType _interpType { InterpType::None };
|
||||
float _interpAlphaVel { 0.0f };
|
||||
float _interpAlpha { 0.0f };
|
||||
|
||||
AnimChain _snapshotChain;
|
||||
|
||||
// no copies
|
||||
AnimPoleVectorConstraint(const AnimPoleVectorConstraint&) = delete;
|
||||
AnimPoleVectorConstraint& operator=(const AnimPoleVectorConstraint&) = delete;
|
||||
};
|
||||
|
||||
#endif // hifi_AnimPoleVectorConstraint_h
|
|
@ -12,6 +12,7 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "AnimUtil.h"
|
||||
|
||||
const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
|
||||
glm::quat(),
|
||||
|
@ -77,4 +78,8 @@ AnimPose::operator glm::mat4() const {
|
|||
glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f));
|
||||
}
|
||||
|
||||
|
||||
void AnimPose::blend(const AnimPose& srcPose, float alpha) {
|
||||
_scale = lerp(srcPose._scale, _scale, alpha);
|
||||
_rot = safeLerp(srcPose._rot, _rot, alpha);
|
||||
_trans = lerp(srcPose._trans, _trans, alpha);
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ public:
|
|||
const glm::vec3& trans() const { return _trans; }
|
||||
glm::vec3& trans() { return _trans; }
|
||||
|
||||
void blend(const AnimPose& srcPose, float alpha);
|
||||
|
||||
private:
|
||||
friend QDebug operator<<(QDebug debug, const AnimPose& pose);
|
||||
glm::vec3 _scale { 1.0f };
|
||||
|
|
|
@ -282,3 +282,17 @@ void AnimSkeleton::dump(const AnimPoseVec& poses) const {
|
|||
qCDebug(animation) << "]";
|
||||
}
|
||||
|
||||
std::vector<int> AnimSkeleton::lookUpJointIndices(const std::vector<QString>& jointNames) const {
|
||||
std::vector<int> result;
|
||||
result.reserve(jointNames.size());
|
||||
for (auto& name : jointNames) {
|
||||
int index = nameToJointIndex(name);
|
||||
if (index == -1) {
|
||||
qWarning(animation) << "AnimSkeleton::lookUpJointIndices(): could not find bone with named " << name;
|
||||
}
|
||||
result.push_back(index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -61,6 +61,8 @@ public:
|
|||
void dump(bool verbose) const;
|
||||
void dump(const AnimPoseVec& poses) const;
|
||||
|
||||
std::vector<int> lookUpJointIndices(const std::vector<QString>& jointNames) const;
|
||||
|
||||
protected:
|
||||
void buildSkeletonFromJoints(const std::vector<FBXJoint>& joints);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ AnimStateMachine::~AnimStateMachine() {
|
|||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) {
|
||||
const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID());
|
||||
if (_currentState->getID() != desiredStateID) {
|
||||
|
@ -81,6 +81,9 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co
|
|||
if (!_duringInterp) {
|
||||
_poses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
|
@ -107,7 +110,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon
|
|||
|
||||
// because dt is 0, we should not encounter any triggers
|
||||
const float dt = 0.0f;
|
||||
Triggers triggers;
|
||||
AnimVariantMap triggers;
|
||||
|
||||
if (_interpType == InterpType::SnapshotBoth) {
|
||||
// snapshot previous pose.
|
||||
|
|
|
@ -113,7 +113,7 @@ public:
|
|||
explicit AnimStateMachine(const QString& id);
|
||||
virtual ~AnimStateMachine() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; }
|
||||
|
||||
|
|
293
libraries/animation/src/AnimTwoBoneIK.cpp
Normal file
293
libraries/animation/src/AnimTwoBoneIK.cpp
Normal file
|
@ -0,0 +1,293 @@
|
|||
//
|
||||
// AnimTwoBoneIK.cpp
|
||||
//
|
||||
// Created by Anthony J. Thibault on 5/12/18.
|
||||
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimTwoBoneIK.h"
|
||||
|
||||
#include <DebugDraw.h>
|
||||
|
||||
#include "AnimationLogging.h"
|
||||
#include "AnimUtil.h"
|
||||
|
||||
const float FRAMES_PER_SECOND = 30.0f;
|
||||
|
||||
AnimTwoBoneIK::AnimTwoBoneIK(const QString& id, float alpha, bool enabled, float interpDuration,
|
||||
const QString& baseJointName, const QString& midJointName,
|
||||
const QString& tipJointName, const glm::vec3& midHingeAxis,
|
||||
const QString& alphaVar, const QString& enabledVar,
|
||||
const QString& endEffectorRotationVarVar, const QString& endEffectorPositionVarVar) :
|
||||
AnimNode(AnimNode::Type::TwoBoneIK, id),
|
||||
_alpha(alpha),
|
||||
_enabled(enabled),
|
||||
_interpDuration(interpDuration),
|
||||
_baseJointName(baseJointName),
|
||||
_midJointName(midJointName),
|
||||
_tipJointName(tipJointName),
|
||||
_midHingeAxis(glm::normalize(midHingeAxis)),
|
||||
_alphaVar(alphaVar),
|
||||
_enabledVar(enabledVar),
|
||||
_endEffectorRotationVarVar(endEffectorRotationVarVar),
|
||||
_endEffectorPositionVarVar(endEffectorPositionVarVar),
|
||||
_prevEndEffectorRotationVar(),
|
||||
_prevEndEffectorPositionVar()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AnimTwoBoneIK::~AnimTwoBoneIK() {
|
||||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimTwoBoneIK::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
|
||||
assert(_children.size() == 1);
|
||||
if (_children.size() != 1) {
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// evalute underPoses
|
||||
AnimPoseVec underPoses = _children[0]->evaluate(animVars, context, dt, triggersOut);
|
||||
|
||||
// if we don't have a skeleton, or jointName lookup failed.
|
||||
if (!_skeleton || _baseJointIndex == -1 || _midJointIndex == -1 || _tipJointIndex == -1 || underPoses.size() == 0) {
|
||||
// pass underPoses through unmodified.
|
||||
_poses = underPoses;
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// guard against size changes
|
||||
if (underPoses.size() != _poses.size()) {
|
||||
_poses = underPoses;
|
||||
}
|
||||
|
||||
const float MIN_ALPHA = 0.0f;
|
||||
const float MAX_ALPHA = 1.0f;
|
||||
float alpha = glm::clamp(animVars.lookup(_alphaVar, _alpha), MIN_ALPHA, MAX_ALPHA);
|
||||
|
||||
// don't perform IK if we have bad indices, or alpha is zero
|
||||
if (_tipJointIndex == -1 || _midJointIndex == -1 || _baseJointIndex == -1 || alpha == 0.0f) {
|
||||
_poses = underPoses;
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// determine if we should interpolate
|
||||
bool enabled = animVars.lookup(_enabledVar, _enabled);
|
||||
if (enabled != _enabled) {
|
||||
AnimChain poseChain;
|
||||
poseChain.buildFromRelativePoses(_skeleton, _poses, _tipJointIndex);
|
||||
if (enabled) {
|
||||
beginInterp(InterpType::SnapshotToSolve, poseChain);
|
||||
} else {
|
||||
beginInterp(InterpType::SnapshotToUnderPoses, poseChain);
|
||||
}
|
||||
}
|
||||
_enabled = enabled;
|
||||
|
||||
// don't build chains or do IK if we are disbled & not interping.
|
||||
if (_interpType == InterpType::None && !enabled) {
|
||||
_poses = underPoses;
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// compute chain
|
||||
AnimChain underChain;
|
||||
underChain.buildFromRelativePoses(_skeleton, underPoses, _tipJointIndex);
|
||||
AnimChain ikChain = underChain;
|
||||
|
||||
AnimPose baseParentPose = ikChain.getAbsolutePoseFromJointIndex(_baseParentJointIndex);
|
||||
AnimPose basePose = ikChain.getAbsolutePoseFromJointIndex(_baseJointIndex);
|
||||
AnimPose midPose = ikChain.getAbsolutePoseFromJointIndex(_midJointIndex);
|
||||
AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||
|
||||
QString endEffectorRotationVar = animVars.lookup(_endEffectorRotationVarVar, QString(""));
|
||||
QString endEffectorPositionVar = animVars.lookup(_endEffectorPositionVarVar, QString(""));
|
||||
|
||||
// if either of the endEffectorVars have changed
|
||||
if ((!_prevEndEffectorRotationVar.isEmpty() && (_prevEndEffectorRotationVar != endEffectorRotationVar)) ||
|
||||
(!_prevEndEffectorPositionVar.isEmpty() && (_prevEndEffectorPositionVar != endEffectorPositionVar))) {
|
||||
// begin interp to smooth out transition between prev and new end effector.
|
||||
AnimChain poseChain;
|
||||
poseChain.buildFromRelativePoses(_skeleton, _poses, _tipJointIndex);
|
||||
beginInterp(InterpType::SnapshotToSolve, poseChain);
|
||||
}
|
||||
|
||||
// Look up end effector from animVars, make sure to convert into geom space.
|
||||
// First look in the triggers then look in the animVars, so we can follow output joints underneath us in the anim graph
|
||||
AnimPose targetPose(tipPose);
|
||||
if (triggersOut.hasKey(endEffectorRotationVar)) {
|
||||
targetPose.rot() = triggersOut.lookupRigToGeometry(endEffectorRotationVar, tipPose.rot());
|
||||
} else if (animVars.hasKey(endEffectorRotationVar)) {
|
||||
targetPose.rot() = animVars.lookupRigToGeometry(endEffectorRotationVar, tipPose.rot());
|
||||
}
|
||||
|
||||
if (triggersOut.hasKey(endEffectorPositionVar)) {
|
||||
targetPose.trans() = triggersOut.lookupRigToGeometry(endEffectorPositionVar, tipPose.trans());
|
||||
} else if (animVars.hasKey(endEffectorRotationVar)) {
|
||||
targetPose.trans() = animVars.lookupRigToGeometry(endEffectorPositionVar, tipPose.trans());
|
||||
}
|
||||
|
||||
_prevEndEffectorRotationVar = endEffectorRotationVar;
|
||||
_prevEndEffectorPositionVar = endEffectorPositionVar;
|
||||
|
||||
glm::vec3 bicepVector = midPose.trans() - basePose.trans();
|
||||
float r0 = glm::length(bicepVector);
|
||||
bicepVector = bicepVector / r0;
|
||||
|
||||
glm::vec3 forearmVector = tipPose.trans() - midPose.trans();
|
||||
float r1 = glm::length(forearmVector);
|
||||
forearmVector = forearmVector / r1;
|
||||
|
||||
float d = glm::length(targetPose.trans() - basePose.trans());
|
||||
|
||||
// http://mathworld.wolfram.com/Circle-CircleIntersection.html
|
||||
float midAngle = 0.0f;
|
||||
if (d < r0 + r1) {
|
||||
float y = sqrtf((-d + r1 - r0) * (-d - r1 + r0) * (-d + r1 + r0) * (d + r1 + r0)) / (2.0f * d);
|
||||
midAngle = PI - (acosf(y / r0) + acosf(y / r1));
|
||||
}
|
||||
|
||||
// compute midJoint rotation
|
||||
glm::quat relMidRot = glm::angleAxis(midAngle, _midHingeAxis);
|
||||
|
||||
// insert new relative pose into the chain and rebuild it.
|
||||
ikChain.setRelativePoseAtJointIndex(_midJointIndex, AnimPose(relMidRot, underPoses[_midJointIndex].trans()));
|
||||
ikChain.buildDirtyAbsolutePoses();
|
||||
|
||||
// recompute tip pose after mid joint has been rotated
|
||||
AnimPose newTipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||
|
||||
glm::vec3 leverArm = newTipPose.trans() - basePose.trans();
|
||||
glm::vec3 targetLine = targetPose.trans() - basePose.trans();
|
||||
|
||||
// compute delta rotation that brings leverArm parallel to targetLine
|
||||
glm::vec3 axis = glm::cross(leverArm, targetLine);
|
||||
float axisLength = glm::length(axis);
|
||||
const float MIN_AXIS_LENGTH = 1.0e-4f;
|
||||
if (axisLength > MIN_AXIS_LENGTH) {
|
||||
axis /= axisLength;
|
||||
float cosAngle = glm::clamp(glm::dot(leverArm, targetLine) / (glm::length(leverArm) * glm::length(targetLine)), -1.0f, 1.0f);
|
||||
float angle = acosf(cosAngle);
|
||||
glm::quat deltaRot = glm::angleAxis(angle, axis);
|
||||
|
||||
// combine deltaRot with basePose.
|
||||
glm::quat absRot = deltaRot * basePose.rot();
|
||||
|
||||
// transform result back into parent relative frame.
|
||||
glm::quat relBaseRot = glm::inverse(baseParentPose.rot()) * absRot;
|
||||
ikChain.setRelativePoseAtJointIndex(_baseJointIndex, AnimPose(relBaseRot, underPoses[_baseJointIndex].trans()));
|
||||
}
|
||||
|
||||
// recompute midJoint pose after base has been rotated.
|
||||
ikChain.buildDirtyAbsolutePoses();
|
||||
AnimPose midJointPose = ikChain.getAbsolutePoseFromJointIndex(_midJointIndex);
|
||||
|
||||
// transform target rotation in to parent relative frame.
|
||||
glm::quat relTipRot = glm::inverse(midJointPose.rot()) * targetPose.rot();
|
||||
ikChain.setRelativePoseAtJointIndex(_tipJointIndex, AnimPose(relTipRot, underPoses[_tipJointIndex].trans()));
|
||||
|
||||
// blend with the underChain
|
||||
ikChain.blend(underChain, alpha);
|
||||
|
||||
// start off by initializing output poses with the underPoses
|
||||
_poses = underPoses;
|
||||
|
||||
// apply smooth interpolation
|
||||
if (_interpType != InterpType::None) {
|
||||
_interpAlpha += _interpAlphaVel * dt;
|
||||
|
||||
if (_interpAlpha < 1.0f) {
|
||||
AnimChain interpChain;
|
||||
if (_interpType == InterpType::SnapshotToUnderPoses) {
|
||||
interpChain = underChain;
|
||||
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||
} else if (_interpType == InterpType::SnapshotToSolve) {
|
||||
interpChain = ikChain;
|
||||
interpChain.blend(_snapshotChain, _interpAlpha);
|
||||
}
|
||||
// copy interpChain into _poses
|
||||
interpChain.outputRelativePoses(_poses);
|
||||
} else {
|
||||
// interpolation complete
|
||||
_interpType = InterpType::None;
|
||||
}
|
||||
}
|
||||
|
||||
if (_interpType == InterpType::None) {
|
||||
if (enabled) {
|
||||
// copy chain into _poses
|
||||
ikChain.outputRelativePoses(_poses);
|
||||
} else {
|
||||
// copy under chain into _poses
|
||||
underChain.outputRelativePoses(_poses);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.getEnableDebugDrawIKTargets()) {
|
||||
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
glm::mat4 rigToAvatarMat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3());
|
||||
|
||||
glm::mat4 geomTargetMat = createMatFromQuatAndPos(targetPose.rot(), targetPose.trans());
|
||||
glm::mat4 avatarTargetMat = rigToAvatarMat * context.getGeometryToRigMatrix() * geomTargetMat;
|
||||
|
||||
QString name = QString("%1_target").arg(_id);
|
||||
DebugDraw::getInstance().addMyAvatarMarker(name, glmExtractRotation(avatarTargetMat),
|
||||
extractTranslation(avatarTargetMat), _enabled ? GREEN : RED);
|
||||
} else if (_lastEnableDebugDrawIKTargets) {
|
||||
QString name = QString("%1_target").arg(_id);
|
||||
DebugDraw::getInstance().removeMyAvatarMarker(name);
|
||||
}
|
||||
_lastEnableDebugDrawIKTargets = context.getEnableDebugDrawIKTargets();
|
||||
|
||||
if (context.getEnableDebugDrawIKChains()) {
|
||||
if (_interpType == InterpType::None && enabled) {
|
||||
const vec4 CYAN(0.0f, 1.0f, 1.0f, 1.0f);
|
||||
ikChain.debugDraw(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix(), CYAN);
|
||||
}
|
||||
}
|
||||
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
const AnimPoseVec& AnimTwoBoneIK::getPosesInternal() const {
|
||||
return _poses;
|
||||
}
|
||||
|
||||
void AnimTwoBoneIK::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
|
||||
AnimNode::setSkeletonInternal(skeleton);
|
||||
lookUpIndices();
|
||||
}
|
||||
|
||||
void AnimTwoBoneIK::lookUpIndices() {
|
||||
assert(_skeleton);
|
||||
|
||||
// look up bone indices by name
|
||||
std::vector<int> indices = _skeleton->lookUpJointIndices({_baseJointName, _midJointName, _tipJointName});
|
||||
|
||||
// cache the results
|
||||
_baseJointIndex = indices[0];
|
||||
_midJointIndex = indices[1];
|
||||
_tipJointIndex = indices[2];
|
||||
|
||||
if (_baseJointIndex != -1) {
|
||||
_baseParentJointIndex = _skeleton->getParentIndex(_baseJointIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimTwoBoneIK::beginInterp(InterpType interpType, const AnimChain& chain) {
|
||||
// capture the current poses in a snapshot.
|
||||
_snapshotChain = chain;
|
||||
|
||||
_interpType = interpType;
|
||||
_interpAlphaVel = FRAMES_PER_SECOND / _interpDuration;
|
||||
_interpAlpha = 0.0f;
|
||||
}
|
83
libraries/animation/src/AnimTwoBoneIK.h
Normal file
83
libraries/animation/src/AnimTwoBoneIK.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// AnimTwoBoneIK.h
|
||||
//
|
||||
// Created by Anthony J. Thibault on 5/12/18.
|
||||
// Copyright (c) 2018 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AnimTwoBoneIK_h
|
||||
#define hifi_AnimTwoBoneIK_h
|
||||
|
||||
#include "AnimNode.h"
|
||||
#include "AnimChain.h"
|
||||
|
||||
// Simple two bone IK chain
|
||||
class AnimTwoBoneIK : public AnimNode {
|
||||
public:
|
||||
friend class AnimTests;
|
||||
|
||||
AnimTwoBoneIK(const QString& id, float alpha, bool enabled, float interpDuration,
|
||||
const QString& baseJointName, const QString& midJointName,
|
||||
const QString& tipJointName, const glm::vec3& midHingeAxis,
|
||||
const QString& alphaVar, const QString& enabledVar,
|
||||
const QString& endEffectorRotationVarVar, const QString& endEffectorPositionVarVar);
|
||||
virtual ~AnimTwoBoneIK() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
protected:
|
||||
|
||||
enum class InterpType {
|
||||
None = 0,
|
||||
SnapshotToUnderPoses,
|
||||
SnapshotToSolve,
|
||||
NumTypes
|
||||
};
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override;
|
||||
|
||||
void lookUpIndices();
|
||||
void beginInterp(InterpType interpType, const AnimChain& chain);
|
||||
|
||||
AnimPoseVec _poses;
|
||||
|
||||
float _alpha;
|
||||
bool _enabled;
|
||||
float _interpDuration; // in frames (1/30 sec)
|
||||
QString _baseJointName;
|
||||
QString _midJointName;
|
||||
QString _tipJointName;
|
||||
glm::vec3 _midHingeAxis; // in baseJoint relative frame, should be normalized
|
||||
|
||||
int _baseParentJointIndex { -1 };
|
||||
int _baseJointIndex { -1 };
|
||||
int _midJointIndex { -1 };
|
||||
int _tipJointIndex { -1 };
|
||||
|
||||
QString _alphaVar; // float - (0, 1) 0 means underPoses only, 1 means IK only.
|
||||
QString _enabledVar; // bool
|
||||
QString _endEffectorRotationVarVar; // string
|
||||
QString _endEffectorPositionVarVar; // string
|
||||
|
||||
QString _prevEndEffectorRotationVar;
|
||||
QString _prevEndEffectorPositionVar;
|
||||
|
||||
InterpType _interpType { InterpType::None };
|
||||
float _interpAlphaVel { 0.0f };
|
||||
float _interpAlpha { 0.0f };
|
||||
|
||||
AnimChain _snapshotChain;
|
||||
|
||||
bool _lastEnableDebugDrawIKTargets { false };
|
||||
|
||||
// no copies
|
||||
AnimTwoBoneIK(const AnimTwoBoneIK&) = delete;
|
||||
AnimTwoBoneIK& operator=(const AnimTwoBoneIK&) = delete;
|
||||
};
|
||||
|
||||
#endif // hifi_AnimTwoBoneIK_h
|
|
@ -21,14 +21,6 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A
|
|||
const AnimPose& aPose = a[i];
|
||||
const AnimPose& bPose = b[i];
|
||||
|
||||
// adjust signs if necessary
|
||||
const glm::quat& q1 = aPose.rot();
|
||||
glm::quat q2 = bPose.rot();
|
||||
float dot = glm::dot(q1, q2);
|
||||
if (dot < 0.0f) {
|
||||
q2 = -q2;
|
||||
}
|
||||
|
||||
result[i].scale() = lerp(aPose.scale(), bPose.scale(), alpha);
|
||||
result[i].rot() = safeLerp(aPose.rot(), bPose.rot(), alpha);
|
||||
result[i].trans() = lerp(aPose.trans(), bPose.trans(), alpha);
|
||||
|
@ -53,7 +45,7 @@ glm::quat averageQuats(size_t numQuats, const glm::quat* quats) {
|
|||
}
|
||||
|
||||
float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag,
|
||||
const QString& id, AnimNode::Triggers& triggersOut) {
|
||||
const QString& id, AnimVariantMap& triggersOut) {
|
||||
|
||||
const float EPSILON = 0.0001f;
|
||||
float frame = currentFrame;
|
||||
|
@ -79,12 +71,12 @@ float accumulateTime(float startFrame, float endFrame, float timeScale, float cu
|
|||
if (framesRemaining >= framesTillEnd) {
|
||||
if (loopFlag) {
|
||||
// anim loop
|
||||
triggersOut.push_back(id + "OnLoop");
|
||||
triggersOut.setTrigger(id + "OnLoop");
|
||||
framesRemaining -= framesTillEnd;
|
||||
frame = clampedStartFrame;
|
||||
} else {
|
||||
// anim end
|
||||
triggersOut.push_back(id + "OnDone");
|
||||
triggersOut.setTrigger(id + "OnDone");
|
||||
frame = endFrame;
|
||||
framesRemaining = 0.0f;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A
|
|||
glm::quat averageQuats(size_t numQuats, const glm::quat* quats);
|
||||
|
||||
float accumulateTime(float startFrame, float endFrame, float timeScale, float currentFrame, float dt, bool loopFlag,
|
||||
const QString& id, AnimNode::Triggers& triggersOut);
|
||||
const QString& id, AnimVariantMap& triggersOut);
|
||||
|
||||
inline glm::quat safeLerp(const glm::quat& a, const glm::quat& b, float alpha) {
|
||||
// adjust signs if necessary
|
||||
|
|
|
@ -24,71 +24,12 @@ class Animation;
|
|||
|
||||
typedef QSharedPointer<Animation> AnimationPointer;
|
||||
|
||||
/// Scriptable interface for FBX animation loading.
|
||||
class AnimationCache : public ResourceCache, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
|
||||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* API to manage animation cache resources.
|
||||
* @namespace AnimationCache
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
// Functions are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* Get the list of all resource URLs.
|
||||
* @function AnimationCache.getResourceList
|
||||
* @returns {string[]}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function AnimationCache.dirty
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* @function AnimationCache.updateTotalSize
|
||||
* @param {number} deltaSize
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Prefetches a resource.
|
||||
* @function AnimationCache.prefetch
|
||||
* @param {string} url - URL of the resource to prefetch.
|
||||
* @param {object} [extra=null]
|
||||
* @returns {ResourceObject}
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* Asynchronously loads a resource from the specified URL and returns it.
|
||||
* @function AnimationCache.getResource
|
||||
* @param {string} url - URL of the resource to load.
|
||||
* @param {string} [fallback=""] - Fallback URL if load of the desired URL fails.
|
||||
* @param {} [extra=null]
|
||||
* @returns {object}
|
||||
*/
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Returns animation resource for particular animation.
|
||||
* @function AnimationCache.getAnimation
|
||||
* @param {string} url - URL to load.
|
||||
* @returns {AnimationObject} animation
|
||||
*/
|
||||
Q_INVOKABLE AnimationPointer getAnimation(const QString& url) { return getAnimation(QUrl(url)); }
|
||||
Q_INVOKABLE AnimationPointer getAnimation(const QUrl& url);
|
||||
|
||||
|
|
20
libraries/animation/src/AnimationCacheScriptingInterface.cpp
Normal file
20
libraries/animation/src/AnimationCacheScriptingInterface.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// AnimationCacheScriptingInterface.cpp
|
||||
// libraries/animation/src
|
||||
//
|
||||
// Created by David Rowe on 25 Jul 2018.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimationCacheScriptingInterface.h"
|
||||
|
||||
AnimationCacheScriptingInterface::AnimationCacheScriptingInterface() :
|
||||
ScriptableResourceCache::ScriptableResourceCache(DependencyManager::get<AnimationCache>())
|
||||
{ }
|
||||
|
||||
AnimationPointer AnimationCacheScriptingInterface::getAnimation(const QString& url) {
|
||||
return DependencyManager::get<AnimationCache>()->getAnimation(QUrl(url));
|
||||
}
|
58
libraries/animation/src/AnimationCacheScriptingInterface.h
Normal file
58
libraries/animation/src/AnimationCacheScriptingInterface.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// AnimationCacheScriptingInterface.h
|
||||
// libraries/animation/src
|
||||
//
|
||||
// Created by David Rowe on 25 Jul 2018.
|
||||
// Copyright 2018 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
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#ifndef hifi_AnimationCacheScriptingInterface_h
|
||||
#define hifi_AnimationCacheScriptingInterface_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <ResourceCache.h>
|
||||
|
||||
#include "AnimationCache.h"
|
||||
|
||||
class AnimationCacheScriptingInterface : public ScriptableResourceCache, public Dependency {
|
||||
Q_OBJECT
|
||||
|
||||
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
|
||||
|
||||
/**jsdoc
|
||||
* API to manage animation cache resources.
|
||||
* @namespace AnimationCache
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
|
||||
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
|
||||
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
|
||||
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
|
||||
*
|
||||
* @borrows ResourceCache.getResourceList as getResourceList
|
||||
* @borrows ResourceCache.updateTotalSize as updateTotalSize
|
||||
* @borrows ResourceCache.prefetch as prefetch
|
||||
* @borrows ResourceCache.dirty as dirty
|
||||
*/
|
||||
|
||||
public:
|
||||
AnimationCacheScriptingInterface();
|
||||
|
||||
/**jsdoc
|
||||
* Returns animation resource for particular animation.
|
||||
* @function AnimationCache.getAnimation
|
||||
* @param {string} url - URL to load.
|
||||
* @returns {AnimationObject} animation
|
||||
*/
|
||||
Q_INVOKABLE AnimationPointer getAnimation(const QString& url);
|
||||
};
|
||||
|
||||
#endif // hifi_AnimationCacheScriptingInterface_h
|
|
@ -59,6 +59,21 @@ const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 0.9f, 0.0f);
|
|||
const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 0.9f, 0.0f);
|
||||
const glm::vec3 DEFAULT_HEAD_POS(0.0f, 0.75f, 0.0f);
|
||||
|
||||
static const QString LEFT_FOOT_POSITION("leftFootPosition");
|
||||
static const QString LEFT_FOOT_ROTATION("leftFootRotation");
|
||||
static const QString LEFT_FOOT_IK_POSITION_VAR("leftFootIKPositionVar");
|
||||
static const QString LEFT_FOOT_IK_ROTATION_VAR("leftFootIKRotationVar");
|
||||
static const QString MAIN_STATE_MACHINE_LEFT_FOOT_POSITION("mainStateMachineLeftFootPosition");
|
||||
static const QString MAIN_STATE_MACHINE_LEFT_FOOT_ROTATION("mainStateMachineLeftFootRotation");
|
||||
|
||||
static const QString RIGHT_FOOT_POSITION("rightFootPosition");
|
||||
static const QString RIGHT_FOOT_ROTATION("rightFootRotation");
|
||||
static const QString RIGHT_FOOT_IK_POSITION_VAR("rightFootIKPositionVar");
|
||||
static const QString RIGHT_FOOT_IK_ROTATION_VAR("rightFootIKRotationVar");
|
||||
static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_ROTATION("mainStateMachineRightFootRotation");
|
||||
static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_POSITION("mainStateMachineRightFootPosition");
|
||||
|
||||
|
||||
Rig::Rig() {
|
||||
// Ensure thread-safe access to the rigRegistry.
|
||||
std::lock_guard<std::mutex> guard(rigRegistryMutex);
|
||||
|
@ -1049,7 +1064,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
|||
getGeometryToRigTransform(), rigToWorldTransform);
|
||||
|
||||
// evaluate the animation
|
||||
AnimNode::Triggers triggersOut;
|
||||
AnimVariantMap triggersOut;
|
||||
|
||||
_internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut);
|
||||
if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) {
|
||||
|
@ -1057,9 +1072,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
|||
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
|
||||
}
|
||||
_animVars.clearTriggers();
|
||||
for (auto& trigger : triggersOut) {
|
||||
_animVars.setTrigger(trigger);
|
||||
}
|
||||
_animVars = triggersOut;
|
||||
}
|
||||
applyOverridePoses();
|
||||
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
|
||||
|
@ -1241,7 +1254,7 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJoin
|
|||
}
|
||||
|
||||
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
|
||||
bool leftArmEnabled, bool rightArmEnabled, float dt,
|
||||
bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
|
||||
|
@ -1305,7 +1318,13 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
|
||||
_animVars.unset("leftHandPosition");
|
||||
_animVars.unset("leftHandRotation");
|
||||
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||
|
||||
if (headEnabled) {
|
||||
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||
} else {
|
||||
// disable hand IK for desktop mode
|
||||
_animVars.set("leftHandType", (int)IKTarget::Type::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
if (rightHandEnabled) {
|
||||
|
@ -1364,21 +1383,41 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
|
|||
|
||||
_animVars.unset("rightHandPosition");
|
||||
_animVars.unset("rightHandRotation");
|
||||
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||
|
||||
if (headEnabled) {
|
||||
_animVars.set("rightHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||
} else {
|
||||
// disable hand IK for desktop mode
|
||||
_animVars.set("rightHandType", (int)IKTarget::Type::Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||
void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled,
|
||||
const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) {
|
||||
|
||||
const float KNEE_POLE_VECTOR_BLEND_FACTOR = 0.95f;
|
||||
|
||||
int hipsIndex = indexOfJoint("Hips");
|
||||
const float KNEE_POLE_VECTOR_BLEND_FACTOR = 0.85f;
|
||||
|
||||
if (headEnabled) {
|
||||
// always do IK if head is enabled
|
||||
_animVars.set("leftFootIKEnabled", true);
|
||||
_animVars.set("rightFootIKEnabled", true);
|
||||
} else {
|
||||
// only do IK if we have a valid foot.
|
||||
_animVars.set("leftFootIKEnabled", leftFootEnabled);
|
||||
_animVars.set("rightFootIKEnabled", rightFootEnabled);
|
||||
}
|
||||
|
||||
if (leftFootEnabled) {
|
||||
_animVars.set("leftFootPosition", leftFootPose.trans());
|
||||
_animVars.set("leftFootRotation", leftFootPose.rot());
|
||||
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||
|
||||
_animVars.set(LEFT_FOOT_POSITION, leftFootPose.trans());
|
||||
_animVars.set(LEFT_FOOT_ROTATION, leftFootPose.rot());
|
||||
|
||||
// We want to drive the IK directly from the trackers.
|
||||
_animVars.set(LEFT_FOOT_IK_POSITION_VAR, LEFT_FOOT_POSITION);
|
||||
_animVars.set(LEFT_FOOT_IK_ROTATION_VAR, LEFT_FOOT_ROTATION);
|
||||
|
||||
int footJointIndex = _animSkeleton->nameToJointIndex("LeftFoot");
|
||||
int kneeJointIndex = _animSkeleton->nameToJointIndex("LeftLeg");
|
||||
|
@ -1396,20 +1435,25 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose
|
|||
_prevLeftFootPoleVector = smoothDeltaRot * _prevLeftFootPoleVector;
|
||||
|
||||
_animVars.set("leftFootPoleVectorEnabled", true);
|
||||
_animVars.set("leftFootPoleReferenceVector", Vectors::UNIT_Z);
|
||||
_animVars.set("leftFootPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftFootPoleVector));
|
||||
} else {
|
||||
_animVars.unset("leftFootPosition");
|
||||
_animVars.unset("leftFootRotation");
|
||||
_animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||
// We want to drive the IK from the underlying animation.
|
||||
// This gives us the ability to squat while in the HMD, without the feet from dipping under the floor.
|
||||
_animVars.set(LEFT_FOOT_IK_POSITION_VAR, MAIN_STATE_MACHINE_LEFT_FOOT_POSITION);
|
||||
_animVars.set(LEFT_FOOT_IK_ROTATION_VAR, MAIN_STATE_MACHINE_LEFT_FOOT_ROTATION);
|
||||
|
||||
// We want to match the animated knee pose as close as possible, so don't use poleVectors
|
||||
_animVars.set("leftFootPoleVectorEnabled", false);
|
||||
_prevLeftFootPoleVectorValid = false;
|
||||
}
|
||||
|
||||
if (rightFootEnabled) {
|
||||
_animVars.set("rightFootPosition", rightFootPose.trans());
|
||||
_animVars.set("rightFootRotation", rightFootPose.rot());
|
||||
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||
_animVars.set(RIGHT_FOOT_POSITION, rightFootPose.trans());
|
||||
_animVars.set(RIGHT_FOOT_ROTATION, rightFootPose.rot());
|
||||
|
||||
// We want to drive the IK directly from the trackers.
|
||||
_animVars.set(RIGHT_FOOT_IK_POSITION_VAR, RIGHT_FOOT_POSITION);
|
||||
_animVars.set(RIGHT_FOOT_IK_ROTATION_VAR, RIGHT_FOOT_ROTATION);
|
||||
|
||||
int footJointIndex = _animSkeleton->nameToJointIndex("RightFoot");
|
||||
int kneeJointIndex = _animSkeleton->nameToJointIndex("RightLeg");
|
||||
|
@ -1427,13 +1471,16 @@ void Rig::updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose
|
|||
_prevRightFootPoleVector = smoothDeltaRot * _prevRightFootPoleVector;
|
||||
|
||||
_animVars.set("rightFootPoleVectorEnabled", true);
|
||||
_animVars.set("rightFootPoleReferenceVector", Vectors::UNIT_Z);
|
||||
_animVars.set("rightFootPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightFootPoleVector));
|
||||
} else {
|
||||
_animVars.unset("rightFootPosition");
|
||||
_animVars.unset("rightFootRotation");
|
||||
// We want to drive the IK from the underlying animation.
|
||||
// This gives us the ability to squat while in the HMD, without the feet from dipping under the floor.
|
||||
_animVars.set(RIGHT_FOOT_IK_POSITION_VAR, MAIN_STATE_MACHINE_RIGHT_FOOT_POSITION);
|
||||
_animVars.set(RIGHT_FOOT_IK_ROTATION_VAR, MAIN_STATE_MACHINE_RIGHT_FOOT_ROTATION);
|
||||
|
||||
// We want to match the animated knee pose as close as possible, so don't use poleVectors
|
||||
_animVars.set("rightFootPoleVectorEnabled", false);
|
||||
_animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition);
|
||||
_prevRightFootPoleVectorValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1475,23 +1522,12 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm
|
|||
for (int i = 0; i < (int)children.size(); i++) {
|
||||
int jointIndex = children[i];
|
||||
int parentIndex = _animSkeleton->getParentIndex(jointIndex);
|
||||
_internalPoseSet._absolutePoses[jointIndex] =
|
||||
_internalPoseSet._absolutePoses[jointIndex] =
|
||||
_internalPoseSet._absolutePoses[parentIndex] * _internalPoseSet._relativePoses[jointIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static glm::quat quatLerp(const glm::quat& q1, const glm::quat& q2, float alpha) {
|
||||
float dot = glm::dot(q1, q2);
|
||||
glm::quat temp;
|
||||
if (dot < 0.0f) {
|
||||
temp = -q2;
|
||||
} else {
|
||||
temp = q2;
|
||||
}
|
||||
return glm::normalize(glm::lerp(q1, temp, alpha));
|
||||
}
|
||||
|
||||
bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex, int oppositeArmIndex, glm::vec3& poleVector) const {
|
||||
// The resulting Pole Vector is calculated as the sum of a three vectors.
|
||||
// The first is the vector with direction shoulder-hand. The module of this vector is inversely proportional to the strength of the resulting Pole Vector.
|
||||
|
@ -1510,19 +1546,21 @@ bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex,
|
|||
|
||||
glm::vec3 backVector = oppositeArmPose.trans() - armPose.trans();
|
||||
glm::vec3 backCenter = armPose.trans() + 0.5f * backVector;
|
||||
|
||||
const float OVER_BACK_HEAD_PERCENTAGE = 0.2f;
|
||||
|
||||
glm::vec3 headCenter = backCenter + glm::vec3(0, OVER_BACK_HEAD_PERCENTAGE * backVector.length(), 0);
|
||||
glm::vec3 frontVector = glm::normalize(glm::cross(backVector, glm::vec3(0, 1, 0)));
|
||||
glm::vec3 frontVector = glm::normalize(glm::cross(backVector, Vectors::UNIT_Y));
|
||||
glm::vec3 topVector = glm::normalize(glm::cross(frontVector, backVector));
|
||||
|
||||
glm::vec3 centerToHand = handPose.trans() - backCenter;
|
||||
glm::vec3 headCenter = backCenter + glm::length(backVector) * topVector;
|
||||
|
||||
// Make sure is pointing forward
|
||||
frontVector = frontVector.z < 0 ? -frontVector : frontVector;
|
||||
|
||||
float horizontalModule = glm::dot(armToHand, glm::vec3(0, -1, 0));
|
||||
glm::vec3 headForward = headCenter + horizontalModule * frontVector;
|
||||
float horizontalModule = glm::dot(centerToHand, -topVector);
|
||||
|
||||
glm::vec3 headForward = headCenter + glm::max(0.0f, horizontalModule) * frontVector;
|
||||
glm::vec3 armToHead = headForward - armPose.trans();
|
||||
|
||||
|
||||
float armToHandDistance = glm::length(armToHand);
|
||||
float armToElbowDistance = glm::length(armToElbow);
|
||||
float elbowToHandDistance = glm::length(elbowToHand);
|
||||
|
@ -1533,9 +1571,11 @@ bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex,
|
|||
|
||||
// How much the hand is reaching for the opposite side
|
||||
float oppositeProjection = glm::dot(armToHandDir, glm::normalize(backVector));
|
||||
|
||||
// Don't use pole vector when the hands are behind
|
||||
if (glm::dot(frontVector, armToHand) < 0 && oppositeProjection < 0.5f * armTotalDistance) {
|
||||
|
||||
bool isCrossed = glm::dot(centerToHand, backVector) > 0;
|
||||
bool isBehind = glm::dot(frontVector, armToHand) < 0;
|
||||
// Don't use pole vector when the hands are behind the back and the arms are not crossed
|
||||
if (isBehind && !isCrossed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1549,10 +1589,10 @@ bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex,
|
|||
glm::vec3 correctionVector = glm::vec3(0, 0, 0);
|
||||
|
||||
const float FORWARD_TRIGGER_PERCENTAGE = 0.2f;
|
||||
const float FORWARD_CORRECTOR_WEIGHT = 3.0f;
|
||||
const float FORWARD_CORRECTOR_WEIGHT = 2.3f;
|
||||
|
||||
float elbowForwardTrigger = FORWARD_TRIGGER_PERCENTAGE * armToHandDistance;
|
||||
|
||||
|
||||
if (oppositeProjection > -elbowForwardTrigger) {
|
||||
float forwardAmount = FORWARD_CORRECTOR_WEIGHT * (elbowForwardTrigger + oppositeProjection);
|
||||
correctionVector = forwardAmount * frontVector;
|
||||
|
@ -1561,31 +1601,18 @@ bool Rig::calculateElbowPoleVector(int handIndex, int elbowIndex, int armIndex,
|
|||
return true;
|
||||
}
|
||||
|
||||
// returns a poleVector for the knees that is a blend of the foot and the hips.
|
||||
// targetFootPose is in rig space
|
||||
// result poleVector is also in rig space.
|
||||
glm::vec3 Rig::calculateKneePoleVector(int footJointIndex, int kneeIndex, int upLegIndex, int hipsIndex, const AnimPose& targetFootPose) const {
|
||||
const float FOOT_THETA = 0.8969f; // 51.39 degrees
|
||||
const glm::vec3 localFootForward(0.0f, cosf(FOOT_THETA), sinf(FOOT_THETA));
|
||||
|
||||
glm::vec3 footForward = targetFootPose.rot() * localFootForward;
|
||||
AnimPose hipsPose = _externalPoseSet._absolutePoses[hipsIndex];
|
||||
AnimPose footPose = targetFootPose;
|
||||
AnimPose kneePose = _externalPoseSet._absolutePoses[kneeIndex];
|
||||
AnimPose upLegPose = _externalPoseSet._absolutePoses[upLegIndex];
|
||||
glm::vec3 hipsForward = hipsPose.rot() * Vectors::UNIT_Z;
|
||||
|
||||
// ray from foot to upLeg
|
||||
glm::vec3 d = glm::normalize(footPose.trans() - upLegPose.trans());
|
||||
|
||||
// form a plane normal to the hips x-axis
|
||||
glm::vec3 n = hipsPose.rot() * Vectors::UNIT_X;
|
||||
|
||||
// project d onto this plane
|
||||
glm::vec3 dProj = d - glm::dot(d, n) * n;
|
||||
|
||||
// rotate dProj by 90 degrees to get the poleVector.
|
||||
glm::vec3 poleVector = glm::angleAxis(-PI / 2.0f, n) * dProj;
|
||||
|
||||
// blend the foot oreintation into the pole vector
|
||||
glm::quat kneeToFootDelta = footPose.rot() * glm::inverse(kneePose.rot());
|
||||
const float WRIST_POLE_ADJUST_FACTOR = 0.5f;
|
||||
glm::quat poleAdjust = quatLerp(Quaternions::IDENTITY, kneeToFootDelta, WRIST_POLE_ADJUST_FACTOR);
|
||||
|
||||
return glm::normalize(poleAdjust * poleVector);
|
||||
return glm::normalize(lerp(hipsForward, footForward, 0.75f));
|
||||
}
|
||||
|
||||
void Rig::updateFromControllerParameters(const ControllerParameters& params, float dt) {
|
||||
|
@ -1610,12 +1637,12 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
|||
|
||||
updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]);
|
||||
|
||||
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, dt,
|
||||
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, headEnabled, dt,
|
||||
params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand],
|
||||
params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo,
|
||||
params.rigToSensorMatrix, sensorToRigMatrix);
|
||||
|
||||
updateFeet(leftFootEnabled, rightFootEnabled,
|
||||
updateFeet(leftFootEnabled, rightFootEnabled, headEnabled,
|
||||
params.primaryControllerPoses[PrimaryControllerType_LeftFoot], params.primaryControllerPoses[PrimaryControllerType_RightFoot],
|
||||
params.rigToSensorMatrix, sensorToRigMatrix);
|
||||
|
||||
|
|
|
@ -75,6 +75,10 @@ public:
|
|||
};
|
||||
|
||||
struct ControllerParameters {
|
||||
ControllerParameters() {
|
||||
memset(primaryControllerFlags, 0, NumPrimaryControllerTypes);
|
||||
memset(secondaryControllerFlags, 0, NumPrimaryControllerTypes);
|
||||
}
|
||||
glm::mat4 rigToSensorMatrix;
|
||||
AnimPose primaryControllerPoses[NumPrimaryControllerTypes]; // rig space
|
||||
uint8_t primaryControllerFlags[NumPrimaryControllerTypes];
|
||||
|
@ -229,12 +233,13 @@ protected:
|
|||
|
||||
void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix);
|
||||
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
|
||||
bool leftArmEnabled, bool rightArmEnabled, float dt,
|
||||
bool leftArmEnabled, bool rightArmEnabled, bool headEnabled, float dt,
|
||||
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
|
||||
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
|
||||
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo,
|
||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
|
||||
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||
void updateFeet(bool leftFootEnabled, bool rightFootEnabled, bool headEnabled,
|
||||
const AnimPose& leftFootPose, const AnimPose& rightFootPose,
|
||||
const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix);
|
||||
|
||||
void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
||||
|
|
|
@ -59,28 +59,29 @@ static void setOption(QScriptValue arguments, const QString name, float defaultV
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* Reverberation options that can be used to initialize an {@link AudioEffectOptions} object when created.
|
||||
* @typedef {object} AudioEffectOptions.ReverbOptions
|
||||
* @property {number} bandwidth
|
||||
* @property {number} preDelay
|
||||
* @property {number} lateDelay
|
||||
* @property {number} reverbTime
|
||||
* @property {number} earlyDiffusion
|
||||
* @property {number} lateDiffusion
|
||||
* @property {number} roomSize
|
||||
* @property {number} density
|
||||
* @property {number} bassMult
|
||||
* @property {number} bassFreq
|
||||
* @property {number} highGain
|
||||
* @property {number} highFreq
|
||||
* @property {number} modRate
|
||||
* @property {number} modDepth
|
||||
* @property {number} earlyGain
|
||||
* @property {number} lateGain
|
||||
* @property {number} earlyMixLeft
|
||||
* @property {number} earlyMixRight
|
||||
* @property {number} lateMixLeft
|
||||
* @property {number} lateMixRight
|
||||
* @property {number} wetDryMix
|
||||
* @property {number} bandwidth=10000 - The corner frequency (Hz) of the low-pass filter at reverb input.
|
||||
* @property {number} preDelay=20 - The delay (milliseconds) between dry signal and the onset of early reflections.
|
||||
* @property {number} lateDelay=0 - The delay (milliseconds) between early reflections and the onset of reverb tail.
|
||||
* @property {number} reverbTime=2 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60.
|
||||
* @property {number} earlyDiffusion=100 - Adjusts the buildup of echo density in the early reflections, normally 100%.
|
||||
* @property {number} lateDiffusion=100 - Adjusts the buildup of echo density in the reverb tail, normally 100%.
|
||||
* @property {number} roomSize=50 - The apparent room size, from small (0%) to large (100%).
|
||||
* @property {number} density=100 - Adjusts the echo density in the reverb tail, normally 100%.
|
||||
* @property {number} bassMult=1.5 - Adjusts the bass-frequency reverb time, as multiple of reverbTime.
|
||||
* @property {number} bassFreq=250 - The crossover frequency (Hz) for the onset of bassMult.
|
||||
* @property {number} highGain=-6 - Reduces the high-frequency reverb time, as attenuation (dB).
|
||||
* @property {number} highFreq=3000 - The crossover frequency (Hz) for the onset of highGain.
|
||||
* @property {number} modRate=2.3 - The rate of modulation (Hz) of the LFO-modulated delay lines.
|
||||
* @property {number} modDepth=50 - The depth of modulation (percent) of the LFO-modulated delay lines.
|
||||
* @property {number} earlyGain=0 - Adjusts the relative level (dB) of the early reflections.
|
||||
* @property {number} lateGain=0 - Adjusts the relative level (dB) of the reverb tail.
|
||||
* @property {number} earlyMixLeft=20 - The apparent distance of the source (percent) in the early reflections.
|
||||
* @property {number} earlyMixRight=20 - The apparent distance of the source (percent) in the early reflections.
|
||||
* @property {number} lateMixLeft=90 - The apparent distance of the source (percent) in the reverb tail.
|
||||
* @property {number} lateMixRight=90 - The apparent distance of the source (percent) in the reverb tail.
|
||||
* @property {number} wetDryMix=50 - Adjusts the wet/dry ratio, from completely dry (0%) to completely wet (100%).
|
||||
*/
|
||||
AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) {
|
||||
setOption(arguments, BANDWIDTH_HANDLE, BANDWIDTH_DEFAULT, _bandwidth);
|
||||
|
|
|
@ -16,35 +16,39 @@
|
|||
#include <QtScript/QScriptEngine>
|
||||
|
||||
/**jsdoc
|
||||
* Audio effect options used by the {@link Audio} API.
|
||||
*
|
||||
* <p>Create using <code>new AudioEffectOptions(reverbOptions)</code>.</p>
|
||||
*
|
||||
* @class AudioEffectOptions
|
||||
* @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null]
|
||||
* @param {AudioEffectOptions.ReverbOptions} [reverbOptions=null] - Reverberation options.
|
||||
*
|
||||
* @hifi-interface
|
||||
* @hifi-client-entity
|
||||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {number} bandwidth=10000
|
||||
* @property {number} preDelay=20
|
||||
* @property {number} lateDelay=0
|
||||
* @property {number} reverbTime=2
|
||||
* @property {number} earlyDiffusion=100
|
||||
* @property {number} lateDiffusion=100
|
||||
* @property {number} roomSize=50
|
||||
* @property {number} density=100
|
||||
* @property {number} bassMult=1.5
|
||||
* @property {number} bassFreq=250
|
||||
* @property {number} highGain=-6
|
||||
* @property {number} highFreq=3000
|
||||
* @property {number} modRate=2.3
|
||||
* @property {number} modDepth=50
|
||||
* @property {number} earlyGain=0
|
||||
* @property {number} lateGain=0
|
||||
* @property {number} earlyMixLeft=20
|
||||
* @property {number} earlyMixRight=20
|
||||
* @property {number} lateMixLeft=90
|
||||
* @property {number} lateMixRight=90
|
||||
* @property {number} wetDryMix=50
|
||||
* @property {number} bandwidth=10000 - The corner frequency (Hz) of the low-pass filter at reverb input.
|
||||
* @property {number} preDelay=20 - The delay (milliseconds) between dry signal and the onset of early reflections.
|
||||
* @property {number} lateDelay=0 - The delay (milliseconds) between early reflections and the onset of reverb tail.
|
||||
* @property {number} reverbTime=2 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60.
|
||||
* @property {number} earlyDiffusion=100 - Adjusts the buildup of echo density in the early reflections, normally 100%.
|
||||
* @property {number} lateDiffusion=100 - Adjusts the buildup of echo density in the reverb tail, normally 100%.
|
||||
* @property {number} roomSize=50 - The apparent room size, from small (0%) to large (100%).
|
||||
* @property {number} density=100 - Adjusts the echo density in the reverb tail, normally 100%.
|
||||
* @property {number} bassMult=1.5 - Adjusts the bass-frequency reverb time, as multiple of reverbTime.
|
||||
* @property {number} bassFreq=250 - The crossover frequency (Hz) for the onset of bassMult.
|
||||
* @property {number} highGain=-6 - Reduces the high-frequency reverb time, as attenuation (dB).
|
||||
* @property {number} highFreq=3000 - The crossover frequency (Hz) for the onset of highGain.
|
||||
* @property {number} modRate=2.3 - The rate of modulation (Hz) of the LFO-modulated delay lines.
|
||||
* @property {number} modDepth=50 - The depth of modulation (percent) of the LFO-modulated delay lines.
|
||||
* @property {number} earlyGain=0 - Adjusts the relative level (dB) of the early reflections.
|
||||
* @property {number} lateGain=0 - Adjusts the relative level (dB) of the reverb tail.
|
||||
* @property {number} earlyMixLeft=20 - The apparent distance of the source (percent) in the early reflections.
|
||||
* @property {number} earlyMixRight=20 - The apparent distance of the source (percent) in the early reflections.
|
||||
* @property {number} lateMixLeft=90 - The apparent distance of the source (percent) in the reverb tail.
|
||||
* @property {number} lateMixRight=90 - The apparent distance of the source (percent) in the reverb tail.
|
||||
* @property {number} wetDryMix=50 - Adjusts the wet/dry ratio, from completely dry (0%) to completely wet (100%).
|
||||
*/
|
||||
|
||||
class AudioEffectOptions : public QObject {
|
||||
|
|
|
@ -840,7 +840,7 @@ static void nearFieldGainCorrection(float azimuth, float distance, float& gainL,
|
|||
// normalized distance factor = [0,1] as distance = [HRTF_NEARFIELD_MAX,HRTF_HEAD_RADIUS]
|
||||
assert(distance < HRTF_NEARFIELD_MAX);
|
||||
assert(distance > HRTF_HEAD_RADIUS);
|
||||
float d = (HRTF_NEARFIELD_MAX - distance) * ( 1.0f / (HRTF_NEARFIELD_MAX - HRTF_HEAD_RADIUS));
|
||||
float d = (HRTF_NEARFIELD_MAX - distance) * (1.0f / (HRTF_NEARFIELD_MAX - HRTF_HEAD_RADIUS));
|
||||
|
||||
// angle of incidence at each ear
|
||||
float angleL = azimuth + HALFPI;
|
||||
|
@ -919,6 +919,9 @@ static void azimuthToIndex(float azimuth, int& index0, int& index1, float& frac)
|
|||
index1 = index0 + 1;
|
||||
frac = azimuth - (float)index0;
|
||||
|
||||
if (index0 >= HRTF_AZIMUTHS) {
|
||||
index0 -= HRTF_AZIMUTHS;
|
||||
}
|
||||
if (index1 >= HRTF_AZIMUTHS) {
|
||||
index1 -= HRTF_AZIMUTHS;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,23 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje
|
|||
return obj;
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Configures how an audio injector plays its audio.
|
||||
* @typedef {object} AudioInjector.AudioInjectorOptions
|
||||
* @property {Vec3} position=Vec3.ZERO - The position in the domain to play the sound.
|
||||
* @property {Quat} orientation=Quat.IDENTITY - The orientation in the domain to play the sound in.
|
||||
* @property {number} volume=1.0 - Playback volume, between <code>0.0</code> and <code>1.0</code>.
|
||||
* @property {number} pitch=1.0 - Alter the pitch of the sound, within +/- 2 octaves. The value is the relative sample rate to
|
||||
* resample the sound at, range <code>0.0625</code> – <code>16.0</code>. A value of <code>0.0625</code> lowers the
|
||||
* pitch by 2 octaves; <code>1.0</code> is no change in pitch; <code>16.0</code> raises the pitch by 2 octaves.
|
||||
* @property {boolean} loop=false - If <code>true</code>, the sound is played repeatedly until playback is stopped.
|
||||
* @property {number} secondOffset=0 - Starts playback from a specified time (seconds) within the sound file, ≥
|
||||
* <code>0</code>.
|
||||
* @property {boolean} localOnly=false - IF <code>true</code>, the sound is played back locally on the client rather than to
|
||||
* others via the audio mixer.
|
||||
* @property {boolean} ignorePenumbra=false - <strong>Deprecated:</strong> This property is deprecated and will be
|
||||
* removed.
|
||||
*/
|
||||
void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions) {
|
||||
if (!object.isObject()) {
|
||||
qWarning() << "Audio injector options is not an object.";
|
||||
|
|
|
@ -79,6 +79,14 @@ private:
|
|||
typedef QSharedPointer<Sound> SharedSoundPointer;
|
||||
|
||||
/**jsdoc
|
||||
* An audio resource, created by {@link SoundCache.getSound}, to be played back using {@link Audio.playSound}.
|
||||
* <p>Supported formats:</p>
|
||||
* <ul>
|
||||
* <li>WAV: 16-bit uncompressed WAV at any sample rate, with 1 (mono), 2(stereo), or 4 (ambisonic) channels.</li>
|
||||
* <li>MP3: Mono or stereo, at any sample rate.</li>
|
||||
* <li>RAW: 48khz 16-bit mono or stereo. Filename must include <code>".stereo"</code> to be interpreted as stereo.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @class SoundObject
|
||||
*
|
||||
* @hifi-interface
|
||||
|
@ -86,8 +94,9 @@ typedef QSharedPointer<Sound> SharedSoundPointer;
|
|||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {boolean} downloaded
|
||||
* @property {number} duration
|
||||
* @property {boolean} downloaded - <code>true</code> if the sound has been downloaded and is ready to be played, otherwise
|
||||
* <code>false</code>.
|
||||
* @property {number} duration - The duration of the sound, in seconds.
|
||||
*/
|
||||
class SoundScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -103,6 +112,7 @@ public:
|
|||
float getDuration() { return _sound->getDuration(); }
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the sound has been downloaded and is ready to be played.
|
||||
* @function SoundObject.ready
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue