mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-09 19:12:36 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into baseball
This commit is contained in:
commit
1d498af76e
276 changed files with 6560 additions and 4785 deletions
|
@ -157,7 +157,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
++_sumListeners;
|
||||
|
||||
AvatarData& avatar = nodeData->getAvatar();
|
||||
glm::vec3 myPosition = avatar.getPosition();
|
||||
glm::vec3 myPosition = avatar.getClientGlobalPosition();
|
||||
|
||||
// reset the internal state for correct random number distribution
|
||||
distribution.reset();
|
||||
|
@ -290,7 +290,7 @@ void AvatarMixer::broadcastAvatarData() {
|
|||
|
||||
// The full rate distance is the distance at which EVERY update will be sent for this avatar
|
||||
// at twice the full rate distance, there will be a 50% chance of sending this avatar's update
|
||||
glm::vec3 otherPosition = otherAvatar.getPosition();
|
||||
glm::vec3 otherPosition = otherAvatar.getClientGlobalPosition();
|
||||
float distanceToAvatar = glm::length(myPosition - otherPosition);
|
||||
|
||||
// potentially update the max full rate distance for this frame
|
||||
|
|
19
assignment-client/src/entities/AssignmentParentFinder.cpp
Normal file
19
assignment-client/src/entities/AssignmentParentFinder.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// AssignmentParentFinder.cpp
|
||||
// assignment-client/src/entities
|
||||
//
|
||||
// Created by Seth Alves on 2015-10-22
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AssignmentParentFinder.h"
|
||||
|
||||
SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID) const {
|
||||
SpatiallyNestableWeakPointer parent;
|
||||
// search entities
|
||||
parent = _tree->findEntityByEntityItemID(parentID);
|
||||
return parent;
|
||||
}
|
34
assignment-client/src/entities/AssignmentParentFinder.h
Normal file
34
assignment-client/src/entities/AssignmentParentFinder.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// AssignmentParentFinder.h
|
||||
// interface/src/entities
|
||||
//
|
||||
// Created by Seth Alves on 2015-10-21
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AssignmentParentFinder_h
|
||||
#define hifi_AssignmentParentFinder_h
|
||||
|
||||
#include <memory>
|
||||
#include <QUuid>
|
||||
|
||||
#include <EntityTree.h>
|
||||
#include <SpatialParentFinder.h>
|
||||
|
||||
// This interface is used to turn a QUuid into a pointer to a "parent" -- something that children can
|
||||
// be spatially relative to. At this point, this means either an EntityItem or an Avatar.
|
||||
|
||||
class AssignmentParentFinder : public SpatialParentFinder {
|
||||
public:
|
||||
AssignmentParentFinder(EntityTreePointer tree) : _tree(tree) { }
|
||||
virtual ~AssignmentParentFinder() { }
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID) const;
|
||||
|
||||
protected:
|
||||
EntityTreePointer _tree;
|
||||
};
|
||||
|
||||
#endif // hifi_AssignmentParentFinder_h
|
|
@ -16,6 +16,7 @@
|
|||
#include "EntityServer.h"
|
||||
#include "EntityServerConsts.h"
|
||||
#include "EntityNodeData.h"
|
||||
#include "AssignmentParentFinder.h"
|
||||
|
||||
const char* MODEL_SERVER_NAME = "Entity";
|
||||
const char* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server";
|
||||
|
@ -60,6 +61,10 @@ OctreePointer EntityServer::createTree() {
|
|||
tree->setSimulation(simpleSimulation);
|
||||
_entitySimulation = simpleSimulation;
|
||||
}
|
||||
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||
DependencyManager::set<AssignmentParentFinder>(tree);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
@ -266,3 +271,72 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio
|
|||
tree->setWantEditLogging(wantEditLogging);
|
||||
tree->setWantTerseEditLogging(wantTerseEditLogging);
|
||||
}
|
||||
|
||||
|
||||
// FIXME - this stats tracking is somewhat temporary to debug the Whiteboard issues. It's not a bad
|
||||
// set of stats to have, but we'd probably want a different data structure if we keep it very long.
|
||||
// Since this version uses a single shared QMap for all senders, there could be some lock contention
|
||||
// on this QWriteLocker
|
||||
void EntityServer::trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode) {
|
||||
QWriteLocker locker(&_viewerSendingStatsLock);
|
||||
_viewerSendingStats[viewerNode][dataID] = { usecTimestampNow(), dataLastEdited };
|
||||
}
|
||||
|
||||
void EntityServer::trackViewerGone(const QUuid& viewerNode) {
|
||||
QWriteLocker locker(&_viewerSendingStatsLock);
|
||||
_viewerSendingStats.remove(viewerNode);
|
||||
}
|
||||
|
||||
QString EntityServer::serverSubclassStats() {
|
||||
QLocale locale(QLocale::English);
|
||||
QString statsString;
|
||||
|
||||
// display memory usage stats
|
||||
statsString += "<b>Entity Server Memory Statistics</b>\r\n";
|
||||
statsString += QString().sprintf("EntityTreeElement size... %ld bytes\r\n", sizeof(EntityTreeElement));
|
||||
statsString += QString().sprintf(" EntityItem size... %ld bytes\r\n", sizeof(EntityItem));
|
||||
statsString += "\r\n\r\n";
|
||||
|
||||
statsString += "<b>Entity Server Sending to Viewer Statistics</b>\r\n";
|
||||
statsString += "----- Viewer Node ID ----------------- ----- Entity ID ---------------------- "
|
||||
"---------- Last Sent To ---------- ---------- Last Edited -----------\r\n";
|
||||
|
||||
int viewers = 0;
|
||||
const int COLUMN_WIDTH = 24;
|
||||
|
||||
{
|
||||
QReadLocker locker(&_viewerSendingStatsLock);
|
||||
quint64 now = usecTimestampNow();
|
||||
|
||||
for (auto viewerID : _viewerSendingStats.keys()) {
|
||||
statsString += viewerID.toString() + "\r\n";
|
||||
|
||||
auto viewerData = _viewerSendingStats[viewerID];
|
||||
for (auto entityID : viewerData.keys()) {
|
||||
ViewerSendingStats stats = viewerData[entityID];
|
||||
|
||||
quint64 elapsedSinceSent = now - stats.lastSent;
|
||||
double sentMsecsAgo = (double)(elapsedSinceSent / USECS_PER_MSEC);
|
||||
|
||||
quint64 elapsedSinceEdit = now - stats.lastEdited;
|
||||
double editMsecsAgo = (double)(elapsedSinceEdit / USECS_PER_MSEC);
|
||||
|
||||
statsString += " "; // the viewerID spacing
|
||||
statsString += entityID.toString();
|
||||
statsString += " ";
|
||||
statsString += QString("%1 msecs ago")
|
||||
.arg(locale.toString((double)sentMsecsAgo).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += QString("%1 msecs ago")
|
||||
.arg(locale.toString((double)editMsecsAgo).rightJustified(COLUMN_WIDTH, ' '));
|
||||
statsString += "\r\n";
|
||||
}
|
||||
viewers++;
|
||||
}
|
||||
}
|
||||
if (viewers < 1) {
|
||||
statsString += " no viewers... \r\n";
|
||||
}
|
||||
statsString += "\r\n\r\n";
|
||||
|
||||
return statsString;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
#include "EntityTree.h"
|
||||
|
||||
/// Handles assignments of type EntityServer - sending entities to various clients.
|
||||
|
||||
struct ViewerSendingStats {
|
||||
quint64 lastSent;
|
||||
quint64 lastEdited;
|
||||
};
|
||||
|
||||
class EntityServer : public OctreeServer, public NewlyCreatedEntityHook {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -44,6 +50,10 @@ public:
|
|||
|
||||
virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) override;
|
||||
virtual void readAdditionalConfiguration(const QJsonObject& settingsSectionObject) override;
|
||||
virtual QString serverSubclassStats() override;
|
||||
|
||||
virtual void trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode) override;
|
||||
virtual void trackViewerGone(const QUuid& viewerNode) override;
|
||||
|
||||
public slots:
|
||||
void pruneDeletedEntities();
|
||||
|
@ -57,6 +67,9 @@ private slots:
|
|||
private:
|
||||
EntitySimulation* _entitySimulation;
|
||||
QTimer* _pruneDeletedEntitiesTimer = nullptr;
|
||||
|
||||
QReadWriteLock _viewerSendingStatsLock;
|
||||
QMap<QUuid, QMap<QUuid, ViewerSendingStats>> _viewerSendingStats;
|
||||
};
|
||||
|
||||
#endif // hifi_EntityServer_h
|
||||
|
|
|
@ -43,6 +43,7 @@ void OctreeInboundPacketProcessor::resetStats() {
|
|||
_totalPackets = 0;
|
||||
_lastNackTime = usecTimestampNow();
|
||||
|
||||
QWriteLocker locker(&_senderStatsLock);
|
||||
_singleSenderStats.clear();
|
||||
}
|
||||
|
||||
|
@ -220,6 +221,8 @@ void OctreeInboundPacketProcessor::trackInboundPacket(const QUuid& nodeUUID, uns
|
|||
_totalElementsInPacket += editsInPacket;
|
||||
_totalPackets++;
|
||||
|
||||
QWriteLocker locker(&_senderStatsLock);
|
||||
|
||||
// find the individual senders stats and track them there too...
|
||||
// see if this is the first we've heard of this node...
|
||||
if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) {
|
||||
|
@ -242,6 +245,8 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
int packetsSent = 0;
|
||||
int totalBytesSent = 0;
|
||||
|
||||
QWriteLocker locker(&_senderStatsLock);
|
||||
|
||||
NodeToSenderStatsMapIterator i = _singleSenderStats.begin();
|
||||
while (i != _singleSenderStats.end()) {
|
||||
|
||||
|
@ -262,10 +267,9 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
|
|||
}
|
||||
|
||||
const SharedNodePointer& destinationNode = DependencyManager::get<NodeList>()->nodeWithUUID(nodeUUID);
|
||||
// If the node no longer exists, wait until the ReceivedPacketProcessor has cleaned up the node
|
||||
// to remove it from our stats list.
|
||||
// FIXME Is it safe to clean it up here before ReceivedPacketProcess has?
|
||||
// if the node no longer exists, remove its stats
|
||||
if (!destinationNode) {
|
||||
i = _singleSenderStats.erase(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ public:
|
|||
|
||||
void resetStats();
|
||||
|
||||
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; }
|
||||
NodeToSenderStatsMap getSingleSenderStats() { QReadLocker locker(&_senderStatsLock); return _singleSenderStats; }
|
||||
|
||||
virtual void terminating() { _shuttingDown = true; ReceivedPacketProcessor::terminating(); }
|
||||
|
||||
|
@ -94,15 +94,16 @@ private:
|
|||
OctreeServer* _myServer;
|
||||
int _receivedPacketCount;
|
||||
|
||||
quint64 _totalTransitTime;
|
||||
quint64 _totalProcessTime;
|
||||
quint64 _totalLockWaitTime;
|
||||
quint64 _totalElementsInPacket;
|
||||
quint64 _totalPackets;
|
||||
std::atomic<uint64_t> _totalTransitTime;
|
||||
std::atomic<uint64_t> _totalProcessTime;
|
||||
std::atomic<uint64_t> _totalLockWaitTime;
|
||||
std::atomic<uint64_t> _totalElementsInPacket;
|
||||
std::atomic<uint64_t> _totalPackets;
|
||||
|
||||
NodeToSenderStatsMap _singleSenderStats;
|
||||
QReadWriteLock _senderStatsLock;
|
||||
|
||||
quint64 _lastNackTime;
|
||||
std::atomic<uint64_t> _lastNackTime;
|
||||
bool _shuttingDown;
|
||||
};
|
||||
#endif // hifi_OctreeInboundPacketProcessor_h
|
||||
|
|
|
@ -59,7 +59,6 @@ OctreeQueryNode::~OctreeQueryNode() {
|
|||
|
||||
void OctreeQueryNode::nodeKilled() {
|
||||
_isShuttingDown = true;
|
||||
elementBag.unhookNotifications(); // if our node is shutting down, then we no longer need octree element notifications
|
||||
if (_octreeSendThread) {
|
||||
// just tell our thread we want to shutdown, this is asynchronous, and fast, we don't need or want it to block
|
||||
// while the thread actually shuts down
|
||||
|
@ -69,7 +68,6 @@ void OctreeQueryNode::nodeKilled() {
|
|||
|
||||
void OctreeQueryNode::forceNodeShutdown() {
|
||||
_isShuttingDown = true;
|
||||
elementBag.unhookNotifications(); // if our node is shutting down, then we no longer need octree element notifications
|
||||
if (_octreeSendThread) {
|
||||
// we really need to force our thread to shutdown, this is synchronous, we will block while the thread actually
|
||||
// shuts down because we really need it to shutdown, and it's ok if we wait for it to complete
|
||||
|
@ -181,7 +179,6 @@ void OctreeQueryNode::resetOctreePacket() {
|
|||
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
_currentPacketIsColor = getWantColor();
|
||||
_currentPacketIsCompressed = getWantCompression();
|
||||
OCTREE_PACKET_FLAGS flags = 0;
|
||||
if (_currentPacketIsColor) {
|
||||
|
@ -338,8 +335,7 @@ void OctreeQueryNode::dumpOutOfView() {
|
|||
int stillInView = 0;
|
||||
int outOfView = 0;
|
||||
OctreeElementBag tempBag;
|
||||
while (!elementBag.isEmpty()) {
|
||||
OctreeElementPointer elementToCheck = elementBag.extract();
|
||||
while (OctreeElementPointer elementToCheck = elementBag.extract()) {
|
||||
if (elementToCheck->isInView(_currentViewFrustum)) {
|
||||
tempBag.insert(elementToCheck);
|
||||
stillInView++;
|
||||
|
@ -348,8 +344,7 @@ void OctreeQueryNode::dumpOutOfView() {
|
|||
}
|
||||
}
|
||||
if (stillInView > 0) {
|
||||
while (!tempBag.isEmpty()) {
|
||||
OctreeElementPointer elementToKeepInBag = tempBag.extract();
|
||||
while (OctreeElementPointer elementToKeepInBag = tempBag.extract()) {
|
||||
if (elementToKeepInBag->isInView(_currentViewFrustum)) {
|
||||
elementBag.insert(elementToKeepInBag);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
#include <CoverageMap.h>
|
||||
#include <NodeData.h>
|
||||
#include <OctreeConstants.h>
|
||||
#include <OctreeElementBag.h>
|
||||
|
@ -55,7 +54,6 @@ public:
|
|||
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
|
||||
|
||||
OctreeElementBag elementBag;
|
||||
CoverageMap map;
|
||||
OctreeElementExtraEncodeData extraEncodeData;
|
||||
|
||||
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }
|
||||
|
@ -80,7 +78,7 @@ public:
|
|||
bool getCurrentPacketIsColor() const { return _currentPacketIsColor; }
|
||||
bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; }
|
||||
bool getCurrentPacketFormatMatches() {
|
||||
return (getCurrentPacketIsColor() == getWantColor() && getCurrentPacketIsCompressed() == getWantCompression());
|
||||
return (getCurrentPacketIsCompressed() == getWantCompression());
|
||||
}
|
||||
|
||||
bool hasLodChanged() const { return _lodChanged; }
|
||||
|
|
|
@ -321,7 +321,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
// If we're starting a fresh packet, then...
|
||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||
// the clients requested color state.
|
||||
bool wantColor = nodeData->getWantColor();
|
||||
bool wantCompression = nodeData->getWantCompression();
|
||||
|
||||
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
|
||||
|
@ -350,7 +349,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) {
|
||||
nodeData->dumpOutOfView();
|
||||
}
|
||||
nodeData->map.erase();
|
||||
}
|
||||
|
||||
if (!viewFrustumChanged && !nodeData->getWantDelta()) {
|
||||
|
@ -424,8 +422,11 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
quint64 lockWaitEnd = usecTimestampNow();
|
||||
lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
|
||||
quint64 encodeStart = usecTimestampNow();
|
||||
|
||||
|
||||
OctreeElementPointer subTree = nodeData->elementBag.extract();
|
||||
if (!subTree) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Looking for a way to prevent locking and encoding a tree that is not
|
||||
// going to result in any packets being sent...
|
||||
|
@ -448,22 +449,25 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
}
|
||||
*/
|
||||
|
||||
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
|
||||
CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP;
|
||||
|
||||
float octreeSizeScale = nodeData->getOctreeSizeScale();
|
||||
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
|
||||
|
||||
int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving()
|
||||
? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||
|
||||
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
|
||||
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(),
|
||||
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
|
||||
wantOcclusionCulling, coverageMap, boundaryLevelAdjust, octreeSizeScale,
|
||||
boundaryLevelAdjust, octreeSizeScale,
|
||||
nodeData->getLastTimeBagEmpty(),
|
||||
isFullScene, &nodeData->stats, _myServer->getJurisdiction(),
|
||||
&nodeData->extraEncodeData);
|
||||
|
||||
// Our trackSend() function is implemented by the server subclass, and will be called back
|
||||
// during the encodeTreeBitstream() as new entities/data elements are sent
|
||||
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
|
||||
_myServer->trackSend(dataID, dataEdited, _nodeUUID);
|
||||
};
|
||||
|
||||
// TODO: should this include the lock time or not? This stat is sent down to the client,
|
||||
// it seems like it may be a good idea to include the lock time as part of the encode time
|
||||
// are reported to client. Since you can encode without the lock
|
||||
|
@ -625,7 +629,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus
|
|||
if (nodeData->elementBag.isEmpty()) {
|
||||
nodeData->updateLastKnownViewFrustum();
|
||||
nodeData->setViewSent(true);
|
||||
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
|
||||
}
|
||||
|
||||
} // end if bag wasn't empty, and so we sent stuff...
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <atomic>
|
||||
|
||||
#include <GenericThread.h>
|
||||
#include <OctreeElementBag.h>
|
||||
|
||||
#include "OctreeQueryNode.h"
|
||||
|
||||
|
|
|
@ -711,7 +711,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
|
||||
|
||||
int senderNumber = 0;
|
||||
NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
|
||||
NodeToSenderStatsMap allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
|
||||
for (NodeToSenderStatsMapConstIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
|
||||
senderNumber++;
|
||||
QUuid senderID = i.key();
|
||||
|
@ -821,6 +821,11 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
.arg(locale.toString((uint)checkSum).rightJustified(16, ' '));
|
||||
|
||||
statsString += "\r\n\r\n";
|
||||
|
||||
statsString += serverSubclassStats();
|
||||
|
||||
statsString += "\r\n\r\n";
|
||||
|
||||
statsString += "</pre>\r\n";
|
||||
statsString += "</doc></html>";
|
||||
|
||||
|
@ -1179,6 +1184,8 @@ void OctreeServer::nodeKilled(SharedNodePointer node) {
|
|||
if (usecsElapsed > 1000) {
|
||||
qDebug() << qPrintable(_safeServerName) << "server nodeKilled() took: " << usecsElapsed << " usecs for node:" << *node;
|
||||
}
|
||||
|
||||
trackViewerGone(node->getUUID());
|
||||
}
|
||||
|
||||
void OctreeServer::forceNodeShutdown(SharedNodePointer node) {
|
||||
|
|
|
@ -79,6 +79,9 @@ public:
|
|||
virtual void beforeRun() { }
|
||||
virtual bool hasSpecialPacketsToSend(const SharedNodePointer& node) { return false; }
|
||||
virtual int sendSpecialPackets(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) { return 0; }
|
||||
virtual QString serverSubclassStats() { return QString(); }
|
||||
virtual void trackSend(const QUuid& dataID, quint64 dataLastEdited, const QUuid& viewerNode) { }
|
||||
virtual void trackViewerGone(const QUuid& viewerNode) { }
|
||||
|
||||
static float SKIP_TIME; // use this for trackXXXTime() calls for non-times
|
||||
|
||||
|
|
31
cmake/externals/gverb/CMakeLists.txt
vendored
31
cmake/externals/gverb/CMakeLists.txt
vendored
|
@ -1,31 +0,0 @@
|
|||
set(EXTERNAL_NAME gverb)
|
||||
|
||||
if (ANDROID)
|
||||
set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19")
|
||||
endif ()
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://hifi-public.s3.amazonaws.com/dependencies/gverb-master.zip
|
||||
URL_MD5 8b16d586390a2102804e46b87820dfc6
|
||||
CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to gverb include directory")
|
||||
|
||||
if (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/gverb.lib CACHE FILEPATH "List of gverb libraries")
|
||||
else ()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${INSTALL_DIR}/lib/libgverb.a CACHE FILEPATH "List of gverb libraries")
|
||||
endif ()
|
|
@ -18,10 +18,16 @@ hifi_library_search_hints("leapmotion")
|
|||
find_path(LEAPMOTION_INCLUDE_DIRS Leap.h PATH_SUFFIXES include HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
|
||||
if (WIN32)
|
||||
find_library(LEAPMOTION_LIBRARY_DEBUG Leapd PATH_SUFFIXES lib/x86 HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
find_library(LEAPMOTION_LIBRARY_RELEASE Leap PATH_SUFFIXES lib/x86 HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
|
||||
find_path(LEAPMOTION_DLL_PATH Leap.dll PATH_SUFFIXES lib/x86 HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(ARCH_DIR "x64")
|
||||
else()
|
||||
set(ARCH_DIR "x86")
|
||||
endif()
|
||||
|
||||
find_library(LEAPMOTION_LIBRARY_DEBUG Leapd PATH_SUFFIXES "lib/${ARCH_DIR}" HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
find_library(LEAPMOTION_LIBRARY_RELEASE Leap PATH_SUFFIXES "lib/${ARCH_DIR}" HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
find_path(LEAPMOTION_DLL_PATH Leap.dll PATH_SUFFIXES "lib/${ARCH_DIR}" HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
elseif (APPLE)
|
||||
find_library(LEAPMOTION_LIBRARY_RELEASE Leap PATH_SUFFIXES lib HINTS ${LEAPMOTION_SEARCH_DIRS})
|
||||
endif ()
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
# Try to find the RSSDK library
|
||||
#
|
||||
# You must provide a RSSDK_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# RSSDK_FOUND - system found RSSDK
|
||||
# RSSDK_INCLUDE_DIRS - the RSSDK include directory
|
||||
# RSSDK_LIBRARIES - Link this to use RSSDK
|
||||
#
|
||||
# Created on 12/7/2014 by Thijs Wenker
|
||||
# Copyright (c) 2014 High Fidelity
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("rssdk")
|
||||
|
||||
find_path(RSSDK_INCLUDE_DIRS pxcbase.h PATH_SUFFIXES include HINTS ${RSSDK_SEARCH_DIRS})
|
||||
|
||||
if (WIN32)
|
||||
find_library(RSSDK_LIBRARY_DEBUG libpxc_d PATH_SUFFIXES lib/Win32 HINTS ${RSSDK_SEARCH_DIRS})
|
||||
find_library(RSSDK_LIBRARY_RELEASE libpxc PATH_SUFFIXES lib/Win32 HINTS ${RSSDK_SEARCH_DIRS})
|
||||
endif ()
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(RSSDK)
|
||||
|
||||
set(RSSDK_LIBRARIES "${RSSDK_LIBRARY}")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(RSSDK DEFAULT_MSG RSSDK_INCLUDE_DIRS RSSDK_LIBRARIES)
|
||||
|
||||
mark_as_advanced(RSSDK_INCLUDE_DIRS RSSDK_LIBRARIES RSSDK_SEARCH_DIRS)
|
|
@ -1,30 +0,0 @@
|
|||
#
|
||||
# FindRtMidi.cmake
|
||||
#
|
||||
# Try to find the RtMidi library
|
||||
#
|
||||
# You can provide a RTMIDI_ROOT_DIR which contains lib and include directories
|
||||
#
|
||||
# Once done this will define
|
||||
#
|
||||
# RTMIDI_FOUND - system found RtMidi
|
||||
# RTMIDI_INCLUDE_DIRS - the RtMidi include directory
|
||||
# RTMIDI_LIBRARIES - link to this to use RtMidi
|
||||
#
|
||||
# Created on 6/30/2014 by Stephen Birarda
|
||||
# Copyright 2014 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("rtmidi")
|
||||
|
||||
find_path(RTMIDI_INCLUDE_DIRS RtMidi.h PATH_SUFFIXES include HINTS ${RTMIDI_SEARCH_DIRS})
|
||||
find_library(RTMIDI_LIBRARIES NAMES rtmidi PATH_SUFFIXES lib HINTS ${RTMIDI_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(RtMidi DEFAULT_MSG RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES)
|
||||
|
||||
mark_as_advanced(RTMIDI_INCLUDE_DIRS RTMIDI_LIBRARIES RTMIDI_SEARCH_DIRS)
|
416
examples/acScripts/AgentPoolController.js
Normal file
416
examples/acScripts/AgentPoolController.js
Normal file
|
@ -0,0 +1,416 @@
|
|||
//
|
||||
// AgentPoolController.js
|
||||
// acScripts
|
||||
//
|
||||
// Created by Sam Gateau on 11/23/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
function printDebug(message) {
|
||||
print(message);
|
||||
}
|
||||
|
||||
(function() {
|
||||
var SERVICE_CHANNEL = "com.highfidelity.playback.service";
|
||||
var COMMAND_CHANNEL = "com.highfidelity.playback.command";
|
||||
|
||||
// The time between alive messages on the command channel
|
||||
var ALIVE_PERIOD = 3;
|
||||
var NUM_CYCLES_BEFORE_RESET = 8;
|
||||
|
||||
// Service Actions
|
||||
var MASTER_ID = -1;
|
||||
var INVALID_AGENT = -2;
|
||||
|
||||
var BROADCAST_AGENTS = -3;
|
||||
|
||||
var MASTER_ALIVE = "MASTER_ALIVE";
|
||||
var AGENT_ALIVE = "AGENT_ALIVE";
|
||||
var AGENT_READY = "READY";
|
||||
var MASTER_HIRE_AGENT = "HIRE"
|
||||
var MASTER_FIRE_AGENT = "FIRE";
|
||||
|
||||
var makeUniqueUUID = function(SEUUID) {
|
||||
//return SEUUID + Math.random();
|
||||
// forget complexity, just give me a four digit pin
|
||||
return (Math.random() * 10000).toFixed(0);
|
||||
}
|
||||
|
||||
var packServiceMessage = function(dest, command, src) {
|
||||
var message = {
|
||||
dest: dest,
|
||||
command: command,
|
||||
src: src
|
||||
};
|
||||
return JSON.stringify(message);
|
||||
};
|
||||
|
||||
var unpackServiceMessage = function(message) {
|
||||
return JSON.parse(message);
|
||||
};
|
||||
|
||||
var packCommandMessage = function(dest, action, argument) {
|
||||
var message = {
|
||||
dest_key: dest,
|
||||
action_key: action,
|
||||
argument_key: argument
|
||||
};
|
||||
return JSON.stringify(message);
|
||||
};
|
||||
|
||||
var unpackCommandMessage = function(message) {
|
||||
return JSON.parse(message);
|
||||
};
|
||||
|
||||
// Actor
|
||||
//---------------------------------
|
||||
var Actor = function() {
|
||||
this.agentID = INVALID_AGENT;
|
||||
this.lastAliveCycle = 0;
|
||||
this.onHired = function(actor) {};
|
||||
this.onFired = function(actor) {};
|
||||
};
|
||||
|
||||
Actor.prototype.isConnected = function () {
|
||||
return (this.agentID != INVALID_AGENT);
|
||||
}
|
||||
|
||||
Actor.prototype.alive = function () {
|
||||
printDebug("Agent UUID =" + this.agentID + " Alive was " + this.lastAliveCycle);
|
||||
this.lastAliveCycle = 0;
|
||||
}
|
||||
Actor.prototype.incrementAliveCycle = function () {
|
||||
printDebug("Actor.prototype.incrementAliveCycle UUID =" + this.agentID + " Alive pre increment " + this.lastAliveCycle);
|
||||
if (this.isConnected()) {
|
||||
this.lastAliveCycle++;
|
||||
printDebug("Agent UUID =" + this.agentID + " Alive incremented " + this.lastAliveCycle);
|
||||
}
|
||||
return this.lastAliveCycle;
|
||||
}
|
||||
|
||||
this.Actor = Actor;
|
||||
|
||||
// master side
|
||||
//---------------------------------
|
||||
var MasterController = function() {
|
||||
this.timeSinceLastAlive = 0;
|
||||
this.knownAgents = new Array;
|
||||
this.hiredActors = new Array;
|
||||
this.hiringAgentsQueue = new Array;
|
||||
this.subscribed = false;
|
||||
};
|
||||
|
||||
MasterController.prototype.destroy = function() {
|
||||
if (this.subscribed) {
|
||||
Messages.unsubscribe(SERVICE_CHANNEL);
|
||||
Messages.unsubscribe(COMMAND_CHANNEL);
|
||||
this.subscribed = true;
|
||||
}
|
||||
};
|
||||
|
||||
MasterController.prototype.reset = function() {
|
||||
this.timeSinceLastAlive = 0;
|
||||
|
||||
if (!this.subscribed) {
|
||||
Messages.subscribe(COMMAND_CHANNEL);
|
||||
Messages.subscribe(SERVICE_CHANNEL);
|
||||
var localThis = this;
|
||||
Messages.messageReceived.connect(function (channel, message, senderID) {
|
||||
if (channel == SERVICE_CHANNEL) {
|
||||
localThis._processServiceMessage(message, senderID);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
// ready to roll, enable
|
||||
this.subscribed = true;
|
||||
printDebug("Master Started");
|
||||
};
|
||||
|
||||
MasterController.prototype._processServiceMessage = function(message, senderID) {
|
||||
var service = unpackServiceMessage(message);
|
||||
if (service.dest == MASTER_ID) {
|
||||
if (service.command == AGENT_READY) {
|
||||
// check to see if we know about this agent
|
||||
var agentIndex = this.knownAgents.indexOf(service.src);
|
||||
if (agentIndex < 0) {
|
||||
this._onAgentAvailableForHiring(service.src);
|
||||
} else {
|
||||
// Master think the agent is hired but not the other way around, forget about it
|
||||
printDebug("New agent still sending ready ? " + service.src + " " + agentIndex + " Forgeting about it");
|
||||
// this._removeHiredAgent(agentIndex);
|
||||
}
|
||||
} else if (service.command == AGENT_ALIVE) {
|
||||
// check to see if we know about this agent
|
||||
var agentIndex = this.knownAgents.indexOf(service.src);
|
||||
if (agentIndex >= 0) {
|
||||
// yes so reset its alive beat
|
||||
this.hiredActors[agentIndex].alive();
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MasterController.prototype._onAgentAvailableForHiring = function(agentID) {
|
||||
if (this.hiringAgentsQueue.length == 0) {
|
||||
printDebug("No Actor on the hiring queue");
|
||||
return;
|
||||
}
|
||||
|
||||
printDebug("MasterController.prototype._onAgentAvailableForHiring " + agentID);
|
||||
var newActor = this.hiringAgentsQueue.pop();
|
||||
|
||||
var indexOfNewAgent = this.knownAgents.push(agentID);
|
||||
newActor.alive();
|
||||
newActor.agentID = agentID;
|
||||
this.hiredActors.push(newActor);
|
||||
|
||||
printDebug("New agent available to be hired " + agentID + " " + indexOfNewAgent);
|
||||
var serviceMessage = packServiceMessage(agentID, MASTER_HIRE_AGENT, MASTER_ID);
|
||||
printDebug("serviceMessage = " + serviceMessage);
|
||||
Messages.sendMessage(SERVICE_CHANNEL, serviceMessage);
|
||||
printDebug("message sent calling the actor" + JSON.stringify(newActor) );
|
||||
|
||||
newActor.onHired(newActor);
|
||||
}
|
||||
|
||||
MasterController.prototype.sendCommand = function(target, action, argument) {
|
||||
if (this.subscribed) {
|
||||
var command = packCommandMessage(target, action, argument);
|
||||
printDebug(command);
|
||||
Messages.sendMessage(COMMAND_CHANNEL, command);
|
||||
}
|
||||
};
|
||||
|
||||
MasterController.prototype.update = function(deltaTime) {
|
||||
this.timeSinceLastAlive += deltaTime;
|
||||
if (this.timeSinceLastAlive > ALIVE_PERIOD) {
|
||||
this.timeSinceLastAlive = 0;
|
||||
Messages.sendMessage(SERVICE_CHANNEL, packServiceMessage(BROADCAST_AGENTS, MASTER_ALIVE, MASTER_ID));
|
||||
|
||||
{
|
||||
// Check for alive connected agents
|
||||
var lostAgents = new Array();
|
||||
for (var i = 0; i < this.hiredActors.length; i++) {
|
||||
var actor = this.hiredActors[i];
|
||||
var lastAlive = actor.incrementAliveCycle()
|
||||
if (lastAlive > NUM_CYCLES_BEFORE_RESET) {
|
||||
printDebug("Agent Lost, firing Agent #" + i + " ID " + actor.agentID);
|
||||
lostAgents.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
// now fire gathered lost agents from end to begin
|
||||
while (lostAgents.length > 0) {
|
||||
printDebug("Firing " + lostAgents.length + " agents" + JSON.stringify(lostAgents));
|
||||
this.fireAgent(this.hiredActors[lostAgents.pop()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
MasterController.prototype.hireAgent = function(actor) {
|
||||
if (actor == null) {
|
||||
printDebug("trying to hire an agent with a null actor, abort");
|
||||
return;
|
||||
}
|
||||
if (actor.isConnected()) {
|
||||
printDebug("trying to hire an agent already connected, abort");
|
||||
return;
|
||||
}
|
||||
this.hiringAgentsQueue.unshift(actor);
|
||||
};
|
||||
|
||||
MasterController.prototype.fireAgent = function(actor) {
|
||||
// check to see if we know about this agent
|
||||
printDebug("MasterController.prototype.fireAgent" + actor.agentID);
|
||||
|
||||
// Try the waiting list first
|
||||
var waitingIndex = this.hiringAgentsQueue.indexOf(actor);
|
||||
if (waitingIndex >= 0) {
|
||||
printDebug("fireAgent found actor on waiting queue #" + waitingIndex);
|
||||
var lostActor = this.hiringAgentsQueue.splice(waitingIndex, 1);
|
||||
if (lostActor.length) {
|
||||
lostActor[0].onFired(lostActor[0]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// then the hired agents
|
||||
var actorIndex = this.knownAgents.indexOf(actor.agentID);
|
||||
if (actorIndex >= 0) {
|
||||
printDebug("fired actor found #" + actorIndex);
|
||||
this._removeHiredAgent(actorIndex);
|
||||
}
|
||||
}
|
||||
|
||||
MasterController.prototype._removeHiredAgent = function(actorIndex) {
|
||||
// check to see if we know about this agent
|
||||
if (actorIndex >= 0) {
|
||||
printDebug("MasterController.prototype._removeHiredAgent #" + this.knownAgents[actorIndex])
|
||||
this.knownAgents.splice(actorIndex, 1);
|
||||
|
||||
var lostActor = this.hiredActors[actorIndex];
|
||||
this.hiredActors.splice(actorIndex, 1);
|
||||
|
||||
var lostAgentID = lostActor.agentID;
|
||||
lostActor.agentID = INVALID_AGENT;
|
||||
|
||||
if (lostAgentID != INVALID_AGENT) {
|
||||
printDebug("fired actor is still connected, send fire command");
|
||||
Messages.sendMessage(SERVICE_CHANNEL, packServiceMessage(lostAgentID, MASTER_FIRE_AGENT, MASTER_ID));
|
||||
}
|
||||
|
||||
lostActor.onFired(lostActor);
|
||||
}
|
||||
}
|
||||
|
||||
this.MasterController = MasterController;
|
||||
|
||||
// agent side
|
||||
//---------------------------------
|
||||
var AgentController = function() {
|
||||
this.subscribed = false;
|
||||
this._init();
|
||||
|
||||
this.onHired = function() {};
|
||||
this.onCommand = function(command) {};
|
||||
this.onFired = function() {};
|
||||
};
|
||||
|
||||
AgentController.prototype._init = function() {
|
||||
this.isHired= false;
|
||||
this.timeSinceLastAlive = 0;
|
||||
this.numCyclesWithoutAlive = 0;
|
||||
this.agentUUID = makeUniqueUUID(Agent.sessionUUID);
|
||||
printDebug("this.agentUUID = " + this.agentUUID);
|
||||
}
|
||||
|
||||
AgentController.prototype.destroy = function() {
|
||||
if (this.subscribed) {
|
||||
this.fired();
|
||||
Messages.unsubscribe(SERVICE_CHANNEL);
|
||||
Messages.unsubscribe(COMMAND_CHANNEL);
|
||||
this.subscribed = true;
|
||||
}
|
||||
};
|
||||
|
||||
AgentController.prototype.reset = function() {
|
||||
// If already hired, fire
|
||||
this.fired();
|
||||
|
||||
if (!this.subscribed) {
|
||||
Messages.subscribe(COMMAND_CHANNEL);
|
||||
Messages.subscribe(SERVICE_CHANNEL);
|
||||
var localThis = this;
|
||||
Messages.messageReceived.connect(function (channel, message, senderID) {
|
||||
if (channel == SERVICE_CHANNEL) {
|
||||
localThis._processServiceMessage(message, senderID);
|
||||
return;
|
||||
}
|
||||
if (channel == COMMAND_CHANNEL) {
|
||||
localThis._processCommandMessage(message, senderID);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.subscribed = true;
|
||||
printDebug("Client Started");
|
||||
};
|
||||
|
||||
AgentController.prototype._processServiceMessage = function(message, senderID) {
|
||||
var service = unpackServiceMessage(message);
|
||||
printDebug("Client " + this.agentUUID + " Received message = " + message);
|
||||
if (service.dest == this.agentUUID) {
|
||||
if (service.command != AGENT_READY) {
|
||||
|
||||
// this is potentially a message to hire me if i m not already
|
||||
if (!this.isHired && (service.command == MASTER_HIRE_AGENT)) {
|
||||
printDebug(service.command);
|
||||
this.isHired = true;
|
||||
printDebug("Client Hired by master UUID" + service.src);
|
||||
this.onHired();
|
||||
this.alive();
|
||||
return;
|
||||
}
|
||||
|
||||
// Or maybe a message to fire me if i m not hired
|
||||
if (this.isHired && (service.command == MASTER_FIRE_AGENT)) {
|
||||
printDebug("Client Fired by master UUID" + senderID);
|
||||
this.fired();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if ((service.src == MASTER_ID) && (service.command == MASTER_ALIVE)) {
|
||||
this.numCyclesWithoutAlive = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AgentController.prototype._processCommandMessage = function(message, senderID) {
|
||||
// ONly work if hired
|
||||
if (this.isHired) {
|
||||
var command = unpackCommandMessage(message);
|
||||
if ((command.dest_key == this.agentUUID) || (command.dest_key == BROADCAST_AGENTS)) {
|
||||
printDebug("Command received = " + JSON.stringify(command) + senderID);
|
||||
this.onCommand(command);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
AgentController.prototype.update = function(deltaTime) {
|
||||
this.timeSinceLastAlive += deltaTime;
|
||||
if (this.timeSinceLastAlive > ALIVE_PERIOD) {
|
||||
if (this.subscribed) {
|
||||
if (!this.isHired) {
|
||||
Messages.sendMessage(SERVICE_CHANNEL, packServiceMessage(MASTER_ID, AGENT_READY, this.agentUUID));
|
||||
//printDebug("Client Ready" + SERVICE_CHANNEL + AGENT_READY);
|
||||
} else {
|
||||
// Send alive beat
|
||||
printDebug("beat !");
|
||||
Messages.sendMessage(SERVICE_CHANNEL, packServiceMessage(MASTER_ID, AGENT_ALIVE, this.agentUUID));
|
||||
|
||||
// Listen for master beat
|
||||
this.numCyclesWithoutAlive++;
|
||||
if (this.numCyclesWithoutAlive > NUM_CYCLES_BEFORE_RESET) {
|
||||
printDebug("Master Lost, self firing Agent");
|
||||
this.fired();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.timeSinceLastAlive = 0;
|
||||
}
|
||||
};
|
||||
|
||||
AgentController.prototype.fired = function() {
|
||||
// clear the state first
|
||||
var wasHired = this.isHired;
|
||||
|
||||
// reset
|
||||
this._init();
|
||||
|
||||
// then custom fire if was hired
|
||||
if (wasHired) {
|
||||
this.onFired();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.AgentController = AgentController;
|
||||
|
||||
|
||||
|
||||
this.BROADCAST_AGENTS = BROADCAST_AGENTS;
|
||||
})();
|
||||
|
||||
|
||||
|
|
@ -10,20 +10,17 @@
|
|||
//
|
||||
|
||||
|
||||
var filename = "http://your.recording.url";
|
||||
var recordingFile = "http://your.recording.url";
|
||||
var playFromCurrentLocation = true;
|
||||
var loop = true;
|
||||
|
||||
Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/e21c0b95-e502-4d15-8c41-ea2fc40f1125/3585ddf674869a67d31d5964f7b52de1.fst?1427169998";
|
||||
|
||||
// Set position here if playFromCurrentLocation is true
|
||||
Avatar.position = { x:1, y: 1, z: 1 };
|
||||
Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
||||
Avatar.scale = 1.0;
|
||||
|
||||
Agent.isAvatar = true;
|
||||
|
||||
Avatar.loadRecording(filename);
|
||||
Recording.loadRecording(recordingFile);
|
||||
|
||||
count = 300; // This is necessary to wait for the audio mixer to connect
|
||||
function update(event) {
|
||||
|
@ -32,22 +29,18 @@ function update(event) {
|
|||
return;
|
||||
}
|
||||
if (count == 0) {
|
||||
Avatar.setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||
Avatar.setPlayerLoop(loop);
|
||||
Avatar.setPlayerUseDisplayName(true);
|
||||
Avatar.setPlayerUseAttachments(true);
|
||||
Avatar.setPlayerUseHeadModel(false);
|
||||
Avatar.setPlayerUseSkeletonModel(true);
|
||||
Avatar.startPlaying();
|
||||
Avatar.play();
|
||||
Recording.setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||
Recording.setPlayerLoop(loop);
|
||||
Recording.setPlayerUseDisplayName(true);
|
||||
Recording.setPlayerUseAttachments(true);
|
||||
Recording.setPlayerUseHeadModel(false);
|
||||
Recording.setPlayerUseSkeletonModel(true);
|
||||
Recording.startPlaying();
|
||||
Vec3.print("Playing from ", Avatar.position);
|
||||
|
||||
count--;
|
||||
}
|
||||
|
||||
if (Avatar.isPlaying()) {
|
||||
Avatar.play();
|
||||
} else {
|
||||
if (!Recording.isPlaying()) {
|
||||
Script.update.disconnect(update);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,18 +9,15 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("./AgentPoolController.js");
|
||||
var agentController = new AgentController();
|
||||
|
||||
// Set the following variables to the values needed
|
||||
var commandChannel = "com.highfidelity.PlaybackChannel1";
|
||||
var clip_url = null;
|
||||
var playFromCurrentLocation = true;
|
||||
var useDisplayName = true;
|
||||
var useAttachments = true;
|
||||
var useAvatarModel = true;
|
||||
|
||||
// ID of the agent. Two agents can't have the same ID.
|
||||
var announceIDChannel = "com.highfidelity.playbackAgent.announceID";
|
||||
var UNKNOWN_AGENT_ID = -2;
|
||||
var id = UNKNOWN_AGENT_ID; // unknown until aknowledged
|
||||
|
||||
// Set position/orientation/scale here if playFromCurrentLocation is true
|
||||
Avatar.position = { x:0, y: 0, z: 0 };
|
||||
|
@ -28,16 +25,12 @@ Avatar.orientation = Quat.fromPitchYawRollDegrees(0, 0, 0);
|
|||
Avatar.scale = 1.0;
|
||||
|
||||
var totalTime = 0;
|
||||
var subscribed = false;
|
||||
var WAIT_FOR_AUDIO_MIXER = 1;
|
||||
|
||||
// Script. DO NOT MODIFY BEYOND THIS LINE.
|
||||
var DO_NOTHING = 0;
|
||||
var PLAY = 1;
|
||||
var PLAY_LOOP = 2;
|
||||
var STOP = 3;
|
||||
var SHOW = 4;
|
||||
var HIDE = 5;
|
||||
var LOAD = 6;
|
||||
|
||||
Recording.setPlayFromCurrentLocation(playFromCurrentLocation);
|
||||
|
@ -46,29 +39,15 @@ Recording.setPlayerUseAttachments(useAttachments);
|
|||
Recording.setPlayerUseHeadModel(false);
|
||||
Recording.setPlayerUseSkeletonModel(useAvatarModel);
|
||||
|
||||
function getAction(channel, message, senderID) {
|
||||
if(subscribed) {
|
||||
var command = JSON.parse(message);
|
||||
print("I'm the agent " + id + " and I received this: ID: " + command.id_key + " Action: " + command.action_key + " URL: " + command.clip_url_key);
|
||||
|
||||
if (command.id_key == id || command.id_key == -1) {
|
||||
if (command.action_key === 6) {
|
||||
clip_url = command.clip_url_key;
|
||||
}
|
||||
|
||||
action = command.action_key;
|
||||
print("That command was for me!");
|
||||
print("My clip is: " + clip_url);
|
||||
} else {
|
||||
action = DO_NOTHING;
|
||||
}
|
||||
|
||||
switch(action) {
|
||||
function agentCommand(command) {
|
||||
if(true) {
|
||||
|
||||
// var command = JSON.parse(message);
|
||||
print("I'm the agent " + this.agentUUID + " and I received this: Dest: " + command.dest_key + " Action: " + command.action_key + " URL: " + command.argument_key);
|
||||
|
||||
switch(command.action_key) {
|
||||
case PLAY:
|
||||
print("Play");
|
||||
if (!Agent.isAvatar) {
|
||||
Agent.isAvatar = true;
|
||||
}
|
||||
if (!Recording.isPlaying()) {
|
||||
Recording.startPlaying();
|
||||
}
|
||||
|
@ -76,9 +55,6 @@ function getAction(channel, message, senderID) {
|
|||
break;
|
||||
case PLAY_LOOP:
|
||||
print("Play loop");
|
||||
if (!Agent.isAvatar) {
|
||||
Agent.isAvatar = true;
|
||||
}
|
||||
if (!Recording.isPlaying()) {
|
||||
Recording.startPlaying();
|
||||
}
|
||||
|
@ -90,75 +66,60 @@ function getAction(channel, message, senderID) {
|
|||
Recording.stopPlaying();
|
||||
}
|
||||
break;
|
||||
case SHOW:
|
||||
print("Show");
|
||||
if (!Agent.isAvatar) {
|
||||
Agent.isAvatar = true;
|
||||
}
|
||||
break;
|
||||
case HIDE:
|
||||
print("Hide");
|
||||
if (Recording.isPlaying()) {
|
||||
Recording.stopPlaying();
|
||||
}
|
||||
Agent.isAvatar = false;
|
||||
break;
|
||||
case LOAD:
|
||||
print("Load");
|
||||
if(clip_url !== null) {
|
||||
Recording.loadRecording(clip_url);
|
||||
}
|
||||
break;
|
||||
case DO_NOTHING:
|
||||
{
|
||||
print("Load" + command.argument_key);
|
||||
print("Agent #" + command.dest_key + " loading clip URL: " + command.argument_key);
|
||||
Recording.loadRecording(command.argument_key);
|
||||
print("After Load" + command.argument_key);
|
||||
Recording.setPlayerTime(0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print("Unknown action: " + action);
|
||||
print("Unknown action: " + command.action_key);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (Recording.isPlaying()) {
|
||||
Recording.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function agentHired() {
|
||||
print("Agent Hired from playbackAgents.js");
|
||||
Agent.isAvatar = true;
|
||||
Recording.stopPlaying();
|
||||
Recording.setPlayerLoop(false);
|
||||
Recording.setPlayerTime(0);
|
||||
}
|
||||
|
||||
function agentFired() {
|
||||
print("Agent Fired from playbackAgents.js");
|
||||
Recording.stopPlaying();
|
||||
Recording.setPlayerTime(0);
|
||||
Recording.setPlayerLoop(false);
|
||||
Agent.isAvatar = false;
|
||||
}
|
||||
|
||||
|
||||
function update(deltaTime) {
|
||||
|
||||
totalTime += deltaTime;
|
||||
|
||||
if (totalTime > WAIT_FOR_AUDIO_MIXER) {
|
||||
if (!subscribed) {
|
||||
Messages.subscribe(commandChannel); // command channel
|
||||
Messages.subscribe(announceIDChannel); // id announce channel
|
||||
subscribed = true;
|
||||
print("I'm the agent and I am ready to receive!");
|
||||
}
|
||||
if (subscribed && id == UNKNOWN_AGENT_ID) {
|
||||
print("sending ready, id:" + id);
|
||||
Messages.sendMessage(announceIDChannel, "ready");
|
||||
if (!agentController.subscribed) {
|
||||
agentController.reset();
|
||||
agentController.onCommand = agentCommand;
|
||||
agentController.onHired = agentHired;
|
||||
agentController.onFired = agentFired;
|
||||
}
|
||||
}
|
||||
|
||||
agentController.update(deltaTime);
|
||||
}
|
||||
|
||||
|
||||
function scriptEnding() {
|
||||
|
||||
agentController.destroy();
|
||||
}
|
||||
|
||||
Messages.messageReceived.connect(function (channel, message, senderID) {
|
||||
if (channel == announceIDChannel && message != "ready") {
|
||||
// If I don't yet know if my ID has been recieved, then check to see if the master has acknowledged me
|
||||
if (id == UNKNOWN_AGENT_ID) {
|
||||
var parts = message.split(".");
|
||||
var agentID = parts[0];
|
||||
var agentIndex = parts[1];
|
||||
if (agentID == Agent.sessionUUID) {
|
||||
id = agentIndex;
|
||||
Messages.unsubscribe(announceIDChannel); // id announce channel
|
||||
}
|
||||
}
|
||||
}
|
||||
if (channel == commandChannel) {
|
||||
getAction(channel, message, senderID);
|
||||
}
|
||||
});
|
||||
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
|
@ -8,31 +8,24 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
Script.include("./AgentPoolController.js");
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
|
||||
var masterController = new MasterController();
|
||||
|
||||
var ac_number = 1; // This is the default number of ACs. Their ID need to be unique and between 0 (included) and ac_number (excluded)
|
||||
var names = new Array(); // It is possible to specify the name of the ACs in this array. ACs names ordered by IDs (Default name is "ACx", x = ID + 1))
|
||||
var channel = "com.highfidelity.PlaybackChannel1";
|
||||
var subscribed = false;
|
||||
var clip_url = null;
|
||||
var input_text = null;
|
||||
|
||||
var knownAgents = new Array; // We will add our known agents here when we discover them
|
||||
|
||||
// available playbackAgents will announce their sessionID here.
|
||||
var announceIDChannel = "com.highfidelity.playbackAgent.announceID";
|
||||
|
||||
// Script. DO NOT MODIFY BEYOND THIS LINE.
|
||||
Script.include("../libraries/toolBars.js");
|
||||
//Script.include("../libraries/toolBars.js");
|
||||
Script.include(HIFI_PUBLIC_BUCKET + "scripts/libraries/toolBars.js");
|
||||
// We want small icons
|
||||
Tool.IMAGE_HEIGHT /= 2;
|
||||
Tool.IMAGE_WIDTH /= 2;
|
||||
|
||||
var DO_NOTHING = 0;
|
||||
var PLAY = 1;
|
||||
var PLAY_LOOP = 2;
|
||||
var STOP = 3;
|
||||
var SHOW = 4;
|
||||
var HIDE = 5;
|
||||
var LOAD = 6;
|
||||
|
||||
var windowDimensions = Controller.getViewportDimensions();
|
||||
|
@ -44,241 +37,381 @@ var COLOR_MASTER = { red: 0, green: 0, blue: 0 };
|
|||
var TEXT_HEIGHT = 12;
|
||||
var TEXT_MARGIN = 3;
|
||||
|
||||
var toolBars = new Array();
|
||||
var nameOverlays = new Array();
|
||||
var onOffIcon = new Array();
|
||||
var playIcon = new Array();
|
||||
var playLoopIcon = new Array();
|
||||
var stopIcon = new Array();
|
||||
var loadIcon = new Array();
|
||||
|
||||
setupPlayback();
|
||||
|
||||
function setupPlayback() {
|
||||
ac_number = Window.prompt("Insert number of agents: ","1");
|
||||
if (ac_number === "" || ac_number === null) {
|
||||
ac_number = 1;
|
||||
}
|
||||
Messages.subscribe(channel);
|
||||
subscribed = true;
|
||||
setupToolBars();
|
||||
// Add new features to Actor class:
|
||||
Actor.prototype.destroy = function() {
|
||||
print("Actor.prototype.destroy");
|
||||
print("Need to fire myself" + this.agentID);
|
||||
masterController.fireAgent(this);
|
||||
}
|
||||
|
||||
function setupToolBars() {
|
||||
if (toolBars.length > 0) {
|
||||
print("Multiple calls to Recorder.js:setupToolBars()");
|
||||
return;
|
||||
}
|
||||
Tool.IMAGE_HEIGHT /= 2;
|
||||
Tool.IMAGE_WIDTH /= 2;
|
||||
|
||||
for (i = 0; i <= ac_number; i++) {
|
||||
toolBars.push(new ToolBar(0, 0, ToolBar.HORIZONTAL));
|
||||
toolBars[i].setBack((i == ac_number) ? COLOR_MASTER : COLOR_TOOL_BAR, ALPHA_OFF);
|
||||
|
||||
onOffIcon.push(toolBars[i].addTool({
|
||||
imageURL: TOOL_ICON_URL + "ac-on-off.svg",
|
||||
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
x: 0, y: 0,
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_ON,
|
||||
visible: true
|
||||
}, true, true));
|
||||
|
||||
playIcon[i] = toolBars[i].addTool({
|
||||
imageURL: TOOL_ICON_URL + "play.svg",
|
||||
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
var playLoopWidthFactor = 1.65;
|
||||
playLoopIcon[i] = toolBars[i].addTool({
|
||||
imageURL: TOOL_ICON_URL + "play-and-loop.svg",
|
||||
subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
width: playLoopWidthFactor * Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
stopIcon[i] = toolBars[i].addTool({
|
||||
imageURL: TOOL_ICON_URL + "recording-stop.svg",
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
loadIcon[i] = toolBars[i].addTool({
|
||||
imageURL: TOOL_ICON_URL + "recording-upload.svg",
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
nameOverlays.push(Overlays.addOverlay("text", {
|
||||
backgroundColor: { red: 0, green: 0, blue: 0 },
|
||||
font: { size: TEXT_HEIGHT },
|
||||
text: (i == ac_number) ? "Master" : i + ". " +
|
||||
((i < names.length) ? names[i] :
|
||||
"AC" + i),
|
||||
x: 0, y: 0,
|
||||
width: toolBars[i].width + ToolBar.SPACING,
|
||||
height: TEXT_HEIGHT + TEXT_MARGIN,
|
||||
leftMargin: TEXT_MARGIN,
|
||||
topMargin: TEXT_MARGIN,
|
||||
alpha: ALPHA_OFF,
|
||||
backgroundAlpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}));
|
||||
|
||||
Actor.prototype.resetClip = function(clipURL, onLoadClip) {
|
||||
this.clipURL = clipURL;
|
||||
this.onLoadClip = onLoadClip;
|
||||
if (this.isConnected()) {
|
||||
this.onLoadClip(this);
|
||||
}
|
||||
}
|
||||
|
||||
function sendCommand(id, action) {
|
||||
if (action === SHOW) {
|
||||
toolBars[id].selectTool(onOffIcon[id], false);
|
||||
toolBars[id].setAlpha(ALPHA_ON, playIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_ON, playLoopIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_ON, stopIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_ON, loadIcon[id]);
|
||||
} else if (action === HIDE) {
|
||||
toolBars[id].selectTool(onOffIcon[id], true);
|
||||
toolBars[id].setAlpha(ALPHA_OFF, playIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_OFF, playLoopIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_OFF, stopIcon[id]);
|
||||
toolBars[id].setAlpha(ALPHA_OFF, loadIcon[id]);
|
||||
} else if (toolBars[id].toolSelected(onOffIcon[id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id == (toolBars.length - 1)) {
|
||||
id = -1; // Master command becomes broadcast.
|
||||
}
|
||||
|
||||
var message = {
|
||||
id_key: id,
|
||||
action_key: action,
|
||||
clip_url_key: clip_url
|
||||
};
|
||||
|
||||
if(subscribed){
|
||||
Messages.sendMessage(channel, JSON.stringify(message));
|
||||
print("Message sent!");
|
||||
clip_url = null;
|
||||
|
||||
Actor.prototype.onMousePressEvent = function(clickedOverlay) {
|
||||
if (this.playIcon === this.toolbar.clicked(clickedOverlay, false)) {
|
||||
masterController.sendCommand(this.agentID, PLAY);
|
||||
} else if (this.playLoopIcon === this.toolbar.clicked(clickedOverlay, false)) {
|
||||
masterController.sendCommand(this.agentID, PLAY_LOOP);
|
||||
} else if (this.stopIcon === this.toolbar.clicked(clickedOverlay, false)) {
|
||||
masterController.sendCommand(this.agentID, STOP);
|
||||
} else if (this.nameOverlay === clickedOverlay) {
|
||||
print("Actor: " + JSON.stringify(this));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function mousePressEvent(event) {
|
||||
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
Actor.prototype._buildUI = function() {
|
||||
print("Actor.prototype._buildUI = " + JSON.stringify(this));
|
||||
|
||||
// Check master control
|
||||
var i = toolBars.length - 1;
|
||||
if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
if (toolBars[i].toolSelected(onOffIcon[i])) {
|
||||
sendCommand(i, SHOW);
|
||||
} else {
|
||||
sendCommand(i, HIDE);
|
||||
}
|
||||
} else if (playIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
sendCommand(i, PLAY);
|
||||
} else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
sendCommand(i, PLAY_LOOP);
|
||||
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
sendCommand(i, STOP);
|
||||
} else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
this.toolbar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
|
||||
|
||||
this.toolbar.setBack(COLOR_TOOL_BAR, ALPHA_OFF);
|
||||
|
||||
this.playIcon = this.toolbar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "play.svg",
|
||||
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
var playLoopWidthFthis = 1.65;
|
||||
this.playLoopIcon = this.toolbar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "play-and-loop.svg",
|
||||
subImage: { x: 0, y: 0, width: playLoopWidthFthis * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
width: playLoopWidthFthis * Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
this.stopIcon = this.toolbar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "recording-stop.svg",
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
this.nameOverlay = Overlays.addOverlay("text", {
|
||||
backgroundColor: { red: 0, green: 0, blue: 0 },
|
||||
font: { size: TEXT_HEIGHT },
|
||||
text: "AC offline",
|
||||
x: 0, y: 0,
|
||||
width: this.toolbar.width + ToolBar.SPACING,
|
||||
height: TEXT_HEIGHT + TEXT_MARGIN,
|
||||
leftMargin: TEXT_MARGIN,
|
||||
topMargin: TEXT_MARGIN,
|
||||
alpha: ALPHA_OFF,
|
||||
backgroundAlpha: ALPHA_OFF,
|
||||
visible: true
|
||||
});
|
||||
}
|
||||
|
||||
Actor.prototype._destroyUI = function() {
|
||||
this.toolbar.cleanup();
|
||||
Overlays.deleteOverlay(this.nameOverlay);
|
||||
}
|
||||
|
||||
Actor.prototype.moveUI = function(pos) {
|
||||
var textSize = TEXT_HEIGHT + 2 * TEXT_MARGIN;
|
||||
this.toolbar.move(pos.x, pos.y);
|
||||
|
||||
Overlays.editOverlay(this.nameOverlay, {
|
||||
x: this.toolbar.x - ToolBar.SPACING,
|
||||
y: this.toolbar.y - textSize
|
||||
});
|
||||
}
|
||||
|
||||
Director = function() {
|
||||
this.actors = new Array();
|
||||
this.toolbar = null;
|
||||
this._buildUI();
|
||||
this.requestPerformanceLoad = false;
|
||||
this.performanceURL = "";
|
||||
};
|
||||
|
||||
Director.prototype.destroy = function () {
|
||||
print("Director.prototype.destroy")
|
||||
this.clearActors();
|
||||
this.toolbar.cleanup();
|
||||
Overlays.deleteOverlay(this.nameOverlay);
|
||||
}
|
||||
|
||||
Director.prototype.clearActors = function () {
|
||||
print("Director.prototype.clearActors")
|
||||
while (this.actors.length > 0) {
|
||||
this.actors.pop().destroy();
|
||||
}
|
||||
|
||||
this.actors = new Array();// Brand new actors
|
||||
}
|
||||
|
||||
Director.prototype._buildUI = function () {
|
||||
this.toolbar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
|
||||
|
||||
this.toolbar.setBack(COLOR_MASTER, ALPHA_OFF);
|
||||
|
||||
this.onOffIcon = this.toolbar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "ac-on-off.svg",
|
||||
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
x: 0, y: 0,
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_ON,
|
||||
visible: true
|
||||
}, true, true);
|
||||
|
||||
this.playIcon = this.toolbar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "play.svg",
|
||||
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
var playLoopWidthFthis = 1.65;
|
||||
this.playLoopIcon = this.toolbar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "play-and-loop.svg",
|
||||
subImage: { x: 0, y: 0, width: playLoopWidthFthis * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||
width: playLoopWidthFthis * Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
this.stopIcon = this.toolbar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "recording-stop.svg",
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
this.loadIcon = this.toolbar.addTool({
|
||||
imageURL: TOOL_ICON_URL + "recording-upload.svg",
|
||||
width: Tool.IMAGE_WIDTH,
|
||||
height: Tool.IMAGE_HEIGHT,
|
||||
alpha: ALPHA_OFF,
|
||||
visible: true
|
||||
}, false);
|
||||
|
||||
this.nameOverlay = Overlays.addOverlay("text", {
|
||||
backgroundColor: { red: 0, green: 0, blue: 0 },
|
||||
font: { size: TEXT_HEIGHT },
|
||||
text: "Master",
|
||||
x: 0, y: 0,
|
||||
width: this.toolbar.width + ToolBar.SPACING,
|
||||
height: TEXT_HEIGHT + TEXT_MARGIN,
|
||||
leftMargin: TEXT_MARGIN,
|
||||
topMargin: TEXT_MARGIN,
|
||||
alpha: ALPHA_OFF,
|
||||
backgroundAlpha: ALPHA_OFF,
|
||||
visible: true
|
||||
});
|
||||
}
|
||||
|
||||
Director.prototype.onMousePressEvent = function(clickedOverlay) {
|
||||
if (this.playIcon === this.toolbar.clicked(clickedOverlay, false)) {
|
||||
print("master play");
|
||||
masterController.sendCommand(BROADCAST_AGENTS, PLAY);
|
||||
} else if (this.onOffIcon === this.toolbar.clicked(clickedOverlay, false)) {
|
||||
this.clearActors();
|
||||
return true;
|
||||
} else if (this.playLoopIcon === this.toolbar.clicked(clickedOverlay, false)) {
|
||||
masterController.sendCommand(BROADCAST_AGENTS, PLAY_LOOP);
|
||||
} else if (this.stopIcon === this.toolbar.clicked(clickedOverlay, false)) {
|
||||
masterController.sendCommand(BROADCAST_AGENTS, STOP);
|
||||
} else if (this.loadIcon === this.toolbar.clicked(clickedOverlay, false)) {
|
||||
input_text = Window.prompt("Insert the url of the clip: ","");
|
||||
if (!(input_text === "" || input_text === null)) {
|
||||
clip_url = input_text;
|
||||
sendCommand(i, LOAD);
|
||||
}
|
||||
print("Performance file ready to be loaded url = " + input_text);
|
||||
this.requestPerformanceLoad = true;
|
||||
this.performanceURL = input_text;
|
||||
}
|
||||
} else if (this.nameOverlay === clickedOverlay) {
|
||||
print("Director: " + JSON.stringify(this));
|
||||
} else {
|
||||
// Check individual controls
|
||||
for (i = 0; i < ac_number; i++) {
|
||||
if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
if (toolBars[i].toolSelected(onOffIcon[i], false)) {
|
||||
sendCommand(i, SHOW);
|
||||
} else {
|
||||
sendCommand(i, HIDE);
|
||||
}
|
||||
} else if (playIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
sendCommand(i, PLAY);
|
||||
} else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
sendCommand(i, PLAY_LOOP);
|
||||
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
sendCommand(i, STOP);
|
||||
} else if (loadIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||
input_text = Window.prompt("Insert the url of the clip: ","");
|
||||
if (!(input_text === "" || input_text === null)) {
|
||||
clip_url = input_text;
|
||||
sendCommand(i, LOAD);
|
||||
}
|
||||
} else {
|
||||
|
||||
for (var i = 0; i < this.actors.length; i++) {
|
||||
if (this.actors[i].onMousePressEvent(clickedOverlay)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // nothing clicked from our known overlays
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Director.prototype.moveUI = function(pos) {
|
||||
var textSize = TEXT_HEIGHT + 2 * TEXT_MARGIN;
|
||||
var relative = { x: pos.x, y: pos.y + (this.actors.length + 1) * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize) };
|
||||
|
||||
this.toolbar.move(relative.x, windowDimensions.y - relative.y);
|
||||
Overlays.editOverlay(this.nameOverlay, {
|
||||
x: this.toolbar.x - ToolBar.SPACING,
|
||||
y: this.toolbar.y - textSize
|
||||
});
|
||||
|
||||
for (var i = 0; i < this.actors.length; i++) {
|
||||
this.actors[i].moveUI({x: relative.x, y: windowDimensions.y - relative.y +
|
||||
(i + 1) * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize)});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Director.prototype.reloadPerformance = function() {
|
||||
this.requestPerformanceLoad = false;
|
||||
|
||||
if (this.performanceURL[0] == '{') {
|
||||
var jsonPerformance = JSON.parse(this.performanceURL);
|
||||
this.onPerformanceLoaded(jsonPerformance);
|
||||
} else {
|
||||
|
||||
var urlpartition = this.performanceURL.split(".");
|
||||
print(urlpartition[0]);
|
||||
print(urlpartition[1]);
|
||||
|
||||
if ((urlpartition.length > 1) && (urlpartition[urlpartition.length - 1] === "hfr")) {
|
||||
print("detected a unique clip url");
|
||||
var oneClipPerformance = new Object();
|
||||
oneClipPerformance.avatarClips = new Array();
|
||||
oneClipPerformance.avatarClips[0] = input_text;
|
||||
|
||||
print(JSON.stringify(oneClipPerformance));
|
||||
|
||||
// we make a local simple performance file with a single clip and pipe in directly
|
||||
this.onPerformanceLoaded(oneClipPerformance);
|
||||
return true;
|
||||
} else {
|
||||
// FIXME: I cannot pass directly this.onPerformanceLoaded, is that exepected ?
|
||||
var localThis = this;
|
||||
Assets.downloadData(input_text, function(data) { localThis.onPerformanceLoaded(JSON.parse(data)); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Director.prototype.onPerformanceLoaded = function(performanceJSON) {
|
||||
// First fire all the current actors
|
||||
this.clearActors();
|
||||
|
||||
print("Director.prototype.onPerformanceLoaded = " + JSON.stringify(performanceJSON));
|
||||
if (performanceJSON.avatarClips != null) {
|
||||
var numClips = performanceJSON.avatarClips.length;
|
||||
print("Found " + numClips + "in the performance file, and currently using " + this.actors.length + " actor(s)");
|
||||
|
||||
for (var i = 0; i < numClips; i++) {
|
||||
this.hireActor(performanceJSON.avatarClips[i]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Director.prototype.hireActor = function(clipURL) {
|
||||
print("new actor = " + this.actors.length );
|
||||
var newActor = new Actor();
|
||||
newActor.clipURL = null;
|
||||
newActor.onLoadClip = function(clip) {};
|
||||
|
||||
var localThis = this;
|
||||
newActor.onHired = function(actor) {
|
||||
print("agent hired from Director! " + actor.agentID)
|
||||
Overlays.editOverlay(actor.nameOverlay, {
|
||||
text: "AC " + actor.agentID,
|
||||
backgroundColor: { red: 0, green: 255, blue: 0 }
|
||||
});
|
||||
|
||||
if (actor.clipURL != null) {
|
||||
print("agent hired, calling load clip for url " + actor.clipURL);
|
||||
actor.onLoadClip(actor);
|
||||
}
|
||||
};
|
||||
|
||||
newActor.onFired = function(actor) {
|
||||
print("agent fired from playbackMaster! " + actor.agentID);
|
||||
var index = localThis.actors.indexOf(actor);
|
||||
if (index >= 0) {
|
||||
localThis.actors.splice(index, 1);
|
||||
}
|
||||
|
||||
actor._destroyUI();
|
||||
|
||||
actor.destroy();
|
||||
moveUI();
|
||||
}
|
||||
|
||||
newActor.resetClip(clipURL, function(actor) {
|
||||
print("Load clip for agent" + actor.agentID + " calling load clip for url " + actor.clipURL);
|
||||
masterController.sendCommand(actor.agentID, LOAD, actor.clipURL);
|
||||
});
|
||||
|
||||
|
||||
masterController.hireAgent(newActor);
|
||||
newActor._buildUI();
|
||||
|
||||
this.actors.push(newActor);
|
||||
|
||||
moveUI();
|
||||
}
|
||||
|
||||
|
||||
masterController.reset();
|
||||
var director = new Director();
|
||||
|
||||
moveUI();
|
||||
|
||||
|
||||
function mousePressEvent(event) {
|
||||
print("mousePressEvent");
|
||||
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
|
||||
// Check director and actors
|
||||
director.onMousePressEvent(clickedOverlay);
|
||||
}
|
||||
|
||||
function moveUI() {
|
||||
var textSize = TEXT_HEIGHT + 2 * TEXT_MARGIN;
|
||||
var relative = { x: 70, y: 75 + (ac_number) * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize) };
|
||||
|
||||
for (i = 0; i <= ac_number; i++) {
|
||||
toolBars[i].move(relative.x,
|
||||
windowDimensions.y - relative.y +
|
||||
i * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize));
|
||||
|
||||
Overlays.editOverlay(nameOverlays[i], {
|
||||
x: toolBars[i].x - ToolBar.SPACING,
|
||||
y: toolBars[i].y - textSize
|
||||
});
|
||||
}
|
||||
director.moveUI({ x: 70, y: 75});
|
||||
|
||||
}
|
||||
|
||||
function update() {
|
||||
function update(deltaTime) {
|
||||
var newDimensions = Controller.getViewportDimensions();
|
||||
if (windowDimensions.x != newDimensions.x ||
|
||||
windowDimensions.y != newDimensions.y) {
|
||||
windowDimensions = newDimensions;
|
||||
moveUI();
|
||||
}
|
||||
|
||||
if (director.requestPerformanceLoad) {
|
||||
print("reloadPerformance " + director.performanceURL);
|
||||
director.reloadPerformance();
|
||||
}
|
||||
|
||||
masterController.update(deltaTime);
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
for (i = 0; i <= ac_number; i++) {
|
||||
toolBars[i].cleanup();
|
||||
Overlays.deleteOverlay(nameOverlays[i]);
|
||||
}
|
||||
|
||||
if (subscribed) {
|
||||
Messages.unsubscribe(channel);
|
||||
}
|
||||
Messages.unsubscribe(announceIDChannel);
|
||||
print("cleanup")
|
||||
director.destroy();
|
||||
masterController.destroy();
|
||||
}
|
||||
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
||||
|
||||
|
||||
Messages.subscribe(announceIDChannel);
|
||||
Messages.messageReceived.connect(function (channel, message, senderID) {
|
||||
if (channel == announceIDChannel && message == "ready") {
|
||||
// check to see if we know about this agent
|
||||
if (knownAgents.indexOf(senderID) < 0) {
|
||||
var indexOfNewAgent = knownAgents.length;
|
||||
knownAgents[indexOfNewAgent] = senderID;
|
||||
var acknowledgeMessage = senderID + "." + indexOfNewAgent;
|
||||
Messages.sendMessage(announceIDChannel, acknowledgeMessage);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
moveUI();
|
|
@ -34,9 +34,10 @@ var BUMPER_ON_VALUE = 0.5;
|
|||
// distant manipulation
|
||||
//
|
||||
|
||||
var DISTANCE_HOLDING_RADIUS_FACTOR = 5; // multiplied by distance between hand and object
|
||||
var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object
|
||||
var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position
|
||||
var DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did
|
||||
var MOVE_WITH_HEAD = true; // experimental head-controll of distantly held objects
|
||||
|
||||
var NO_INTERSECT_COLOR = {
|
||||
red: 10,
|
||||
|
@ -676,6 +677,13 @@ function MyController(hand) {
|
|||
this.currentObjectTime = now;
|
||||
this.handRelativePreviousPosition = Vec3.subtract(handControllerPosition, MyAvatar.position);
|
||||
this.handPreviousRotation = handRotation;
|
||||
this.currentCameraOrientation = Camera.orientation;
|
||||
|
||||
// compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object
|
||||
this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, handControllerPosition) + 1.0);
|
||||
if (this.radiusScalar < 1.0) {
|
||||
this.radiusScalar = 1.0;
|
||||
}
|
||||
|
||||
this.actionID = NULL_ACTION_ID;
|
||||
this.actionID = Entities.addAction("spring", this.grabbedEntity, {
|
||||
|
@ -707,8 +715,6 @@ function MyController(hand) {
|
|||
this.currentAvatarOrientation = MyAvatar.orientation;
|
||||
|
||||
this.overlayLineOff();
|
||||
|
||||
|
||||
};
|
||||
|
||||
this.continueDistanceHolding = function() {
|
||||
|
@ -737,8 +743,12 @@ function MyController(hand) {
|
|||
this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
|
||||
|
||||
// the action was set up on a previous call. update the targets.
|
||||
var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) *
|
||||
DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR);
|
||||
var radius = Vec3.distance(this.currentObjectPosition, handControllerPosition) *
|
||||
this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR;
|
||||
if (radius < 1.0) {
|
||||
radius = 1.0;
|
||||
}
|
||||
|
||||
// how far did avatar move this timestep?
|
||||
var currentPosition = MyAvatar.position;
|
||||
var avatarDeltaPosition = Vec3.subtract(currentPosition, this.currentAvatarPosition);
|
||||
|
@ -769,11 +779,11 @@ function MyController(hand) {
|
|||
var handMoved = Vec3.subtract(handToAvatar, this.handRelativePreviousPosition);
|
||||
this.handRelativePreviousPosition = handToAvatar;
|
||||
|
||||
// magnify the hand movement but not the change from avatar movement & rotation
|
||||
// magnify the hand movement but not the change from avatar movement & rotation
|
||||
handMoved = Vec3.subtract(handMoved, handMovementFromTurning);
|
||||
var superHandMoved = Vec3.multiply(handMoved, radius);
|
||||
|
||||
// Move the object by the magnified amount and then by amount from avatar movement & rotation
|
||||
// Move the object by the magnified amount and then by amount from avatar movement & rotation
|
||||
var newObjectPosition = Vec3.sum(this.currentObjectPosition, superHandMoved);
|
||||
newObjectPosition = Vec3.sum(newObjectPosition, avatarDeltaPosition);
|
||||
newObjectPosition = Vec3.sum(newObjectPosition, objectMovementFromTurning);
|
||||
|
@ -795,6 +805,16 @@ function MyController(hand) {
|
|||
|
||||
Entities.callEntityMethod(this.grabbedEntity, "continueDistantGrab");
|
||||
|
||||
// mix in head motion
|
||||
if (MOVE_WITH_HEAD) {
|
||||
var objDistance = Vec3.length(objectToAvatar);
|
||||
var before = Vec3.multiplyQbyV(this.currentCameraOrientation, { x: 0.0, y: 0.0, z: objDistance });
|
||||
var after = Vec3.multiplyQbyV(Camera.orientation, { x: 0.0, y: 0.0, z: objDistance });
|
||||
var change = Vec3.subtract(before, after);
|
||||
this.currentCameraOrientation = Camera.orientation;
|
||||
this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, change);
|
||||
}
|
||||
|
||||
Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
targetPosition: this.currentObjectPosition,
|
||||
linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME,
|
||||
|
@ -1265,10 +1285,10 @@ Controller.enableMapping(MAPPING_NAME);
|
|||
var handToDisable = 'none';
|
||||
|
||||
function update() {
|
||||
if (handToDisable !== LEFT_HAND) {
|
||||
if (handToDisable !== LEFT_HAND && handToDisable!=='both') {
|
||||
leftController.update();
|
||||
}
|
||||
if (handToDisable !== RIGHT_HAND) {
|
||||
if (handToDisable !== RIGHT_HAND && handToDisable!=='both') {
|
||||
rightController.update();
|
||||
}
|
||||
}
|
||||
|
@ -1276,15 +1296,20 @@ function update() {
|
|||
Messages.subscribe('Hifi-Hand-Disabler');
|
||||
|
||||
handleHandDisablerMessages = function(channel, message, sender) {
|
||||
|
||||
|
||||
if (sender === MyAvatar.sessionUUID) {
|
||||
handToDisable = message;
|
||||
if (message === 'left') {
|
||||
handToDisable = LEFT_HAND;
|
||||
}
|
||||
if (message === 'right') {
|
||||
handToDisable = RIGHT_HAND;
|
||||
}
|
||||
if(message==='both'){
|
||||
handToDisable='both';
|
||||
}
|
||||
if(message==='none'){
|
||||
handToDisable='none';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,8 +22,10 @@ var laserPointer = (function () {
|
|||
];
|
||||
|
||||
function isHandPointing(hand) {
|
||||
var MINIMUM_TRIGGER_PULL = 0.9;
|
||||
return Controller.getTriggerValue(hand) > MINIMUM_TRIGGER_PULL;
|
||||
var MINIMUM_TRIGGER_PULL = 0.9,
|
||||
controller;
|
||||
controller = hand === 0 ? Controller.Standard.LT : Controller.Standard.RT;
|
||||
return Controller.getValue(controller) > MINIMUM_TRIGGER_PULL;
|
||||
}
|
||||
|
||||
function isFingerPointing(hand) {
|
||||
|
|
|
@ -193,13 +193,12 @@ var leapHands = (function () {
|
|||
}
|
||||
|
||||
// Set avatar arms vertical, forearms horizontal, as "zero" position for calibration
|
||||
MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, -90.0));
|
||||
MyAvatar.setJointData("LeftForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0));
|
||||
MyAvatar.setJointData("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
|
||||
MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 90.0));
|
||||
MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0));
|
||||
MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
|
||||
|
||||
MyAvatar.setJointRotation("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
|
||||
MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 90.0, 90.0));
|
||||
MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
|
||||
MyAvatar.setJointRotation("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
|
||||
MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, -90.0, -90.0));
|
||||
MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
|
||||
|
||||
// Wait for arms to assume their positions before calculating
|
||||
Script.setTimeout(finishCalibration, CALIBRATION_TIME);
|
||||
|
@ -382,23 +381,14 @@ var leapHands = (function () {
|
|||
|
||||
// Hand rotation in camera coordinates ...
|
||||
handRotation = {
|
||||
x: handRotation.z,
|
||||
y: handRotation.y,
|
||||
z: handRotation.x,
|
||||
x: -handRotation.x,
|
||||
y: -handRotation.z,
|
||||
z: -handRotation.y,
|
||||
w: handRotation.w
|
||||
};
|
||||
|
||||
// Hand rotation in avatar coordinates ...
|
||||
if (h === 0) {
|
||||
handRotation.x = -handRotation.x;
|
||||
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
|
||||
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 }), handRotation);
|
||||
} else {
|
||||
handRotation.z = -handRotation.z;
|
||||
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
|
||||
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 0, z: 1 }), handRotation);
|
||||
}
|
||||
|
||||
handRotation = Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), handRotation);
|
||||
cameraOrientation.x = -cameraOrientation.x;
|
||||
cameraOrientation.z = -cameraOrientation.z;
|
||||
handRotation = Quat.multiply(cameraOrientation, handRotation);
|
||||
|
@ -421,22 +411,14 @@ var leapHands = (function () {
|
|||
|
||||
// Hand rotation in camera coordinates ...
|
||||
handRotation = {
|
||||
x: handRotation.z,
|
||||
y: handRotation.y,
|
||||
z: handRotation.x,
|
||||
x: -handRotation.x,
|
||||
y: -handRotation.z,
|
||||
z: -handRotation.y,
|
||||
w: handRotation.w
|
||||
};
|
||||
|
||||
// Hand rotation in avatar coordinates ...
|
||||
if (h === 0) {
|
||||
handRotation.x = -handRotation.x;
|
||||
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
|
||||
handRotation);
|
||||
} else {
|
||||
handRotation.z = -handRotation.z;
|
||||
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
|
||||
handRotation);
|
||||
}
|
||||
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
|
||||
}
|
||||
|
||||
// Set hand position and orientation ...
|
||||
|
@ -462,7 +444,7 @@ var leapHands = (function () {
|
|||
w: locRotation.w
|
||||
};
|
||||
}
|
||||
MyAvatar.setJointData(fingers[h][i][j].jointName, locRotation);
|
||||
MyAvatar.setJointRotation(fingers[h][i][j].jointName, locRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ function randVector(a, b) {
|
|||
|
||||
var startTimeInSeconds = new Date().getTime() / 1000;
|
||||
|
||||
var NATURAL_SIZE_OF_BUTTERFLY = { x: 1.0, y: 0.4, z: 0.2 };
|
||||
var NATURAL_SIZE_OF_BUTTERFLY = { x:0.5, y: 0.2, z: 0.1 };
|
||||
|
||||
var lifeTime = 3600; // One hour lifespan
|
||||
var range = 7.0; // Over what distance in meters do you want the flock to fly around
|
||||
|
@ -65,8 +65,8 @@ function addButterfly() {
|
|||
var color = { red: 100, green: 100, blue: 100 };
|
||||
var size = 0;
|
||||
|
||||
var MINSIZE = 0.06;
|
||||
var RANGESIZE = 0.2;
|
||||
var MINSIZE = 0.01;
|
||||
var RANGESIZE = 0.05;
|
||||
var maxSize = MINSIZE + RANGESIZE;
|
||||
|
||||
size = MINSIZE + Math.random() * RANGESIZE;
|
||||
|
@ -74,7 +74,7 @@ function addButterfly() {
|
|||
var dimensions = Vec3.multiply(NATURAL_SIZE_OF_BUTTERFLY, (size / maxSize));
|
||||
|
||||
var GRAVITY = -0.2;
|
||||
var newFrameRate = 20 + Math.random() * 30;
|
||||
var newFrameRate = 29 + Math.random() * 30;
|
||||
var properties = {
|
||||
type: "Model",
|
||||
lifetime: lifeTime,
|
||||
|
@ -86,17 +86,13 @@ function addButterfly() {
|
|||
dimensions: dimensions,
|
||||
color: color,
|
||||
animation: {
|
||||
url: "http://public.highfidelity.io/models/content/butterfly/butterfly.fbx",
|
||||
firstFrame: 0,
|
||||
url: "http://hifi-content.s3.amazonaws.com/james/butterfly/butterfly.fbx",
|
||||
fps: newFrameRate,
|
||||
currentFrame: 0,
|
||||
hold: false,
|
||||
lastFrame: 10000,
|
||||
loop: true,
|
||||
running: true,
|
||||
startAutomatically:false
|
||||
},
|
||||
modelURL: "http://public.highfidelity.io/models/content/butterfly/butterfly.fbx"
|
||||
modelURL: "http://hifi-content.s3.amazonaws.com/james/butterfly/butterfly.fbx"
|
||||
};
|
||||
butterflies.push(Entities.addEntity(properties));
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ Script.include("../../../libraries/constants.js");
|
|||
|
||||
var GUN_FORCE =20;
|
||||
|
||||
Messages.sendMessage('Hifi-Hand-Disabler', "both");
|
||||
|
||||
var gameName = "Kill All The Rats!"
|
||||
// var HOST = "localhost:5000"
|
||||
|
@ -194,6 +195,7 @@ function fire(side, value) {
|
|||
|
||||
|
||||
function scriptEnding() {
|
||||
Messages.sendMessage('Hifi-Hand-Disabler', 'none');
|
||||
mapping.disable();
|
||||
for (var i = 0; i < pointers.length; ++i) {
|
||||
Overlays.deleteOverlay(pointers[i]);
|
||||
|
|
|
@ -223,7 +223,9 @@
|
|||
var elDimensionsZ = document.getElementById("property-dim-z");
|
||||
var elResetToNaturalDimensions = document.getElementById("reset-to-natural-dimensions");
|
||||
var elRescaleDimensionsPct = document.getElementById("dimension-rescale-pct");
|
||||
var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button");
|
||||
var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button");
|
||||
|
||||
var elParentID = document.getElementById("property-parent-id");
|
||||
|
||||
var elRegistrationX = document.getElementById("property-reg-x");
|
||||
var elRegistrationY = document.getElementById("property-reg-y");
|
||||
|
@ -453,6 +455,8 @@
|
|||
elDimensionsY.value = properties.dimensions.y.toFixed(2);
|
||||
elDimensionsZ.value = properties.dimensions.z.toFixed(2);
|
||||
|
||||
elParentID.value = properties.parentID;
|
||||
|
||||
elRegistrationX.value = properties.registrationPoint.x.toFixed(2);
|
||||
elRegistrationY.value = properties.registrationPoint.y.toFixed(2);
|
||||
elRegistrationZ.value = properties.registrationPoint.z.toFixed(2);
|
||||
|
@ -666,6 +670,8 @@
|
|||
elDimensionsY.addEventListener('change', dimensionsChangeFunction);
|
||||
elDimensionsZ.addEventListener('change', dimensionsChangeFunction);
|
||||
|
||||
elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID'));
|
||||
|
||||
var registrationChangeFunction = createEmitVec3PropertyUpdateFunction(
|
||||
'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ);
|
||||
elRegistrationX.addEventListener('change', registrationChangeFunction);
|
||||
|
@ -1055,6 +1061,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<span class="label" style="float: left; margin-right: 6px">ParentID</span>
|
||||
<div class="value" style="overflow: hidden;">
|
||||
<input type="text" id="property-parent-id">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="label">Registration</div>
|
||||
<div class="value">
|
||||
|
|
|
@ -47,7 +47,12 @@ function makeBasketball() {
|
|||
modelURL: basketballURL,
|
||||
restitution: 1.0,
|
||||
damping: 0.00001,
|
||||
shapeType: "sphere"
|
||||
shapeType: "sphere",
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
originalPosition = position;
|
||||
}
|
||||
|
|
|
@ -15,12 +15,20 @@ function createDoll() {
|
|||
|
||||
var scriptURL = Script.resolvePath("doll.js");
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 }), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var naturalDimensions = { x: 1.63, y: 1.67, z: 0.26};
|
||||
var naturalDimensions = {
|
||||
x: 1.63,
|
||||
y: 1.67,
|
||||
z: 0.26
|
||||
};
|
||||
|
||||
var desiredDimensions = Vec3.multiply(naturalDimensions, 0.15);
|
||||
|
||||
|
||||
var doll = Entities.addEntity({
|
||||
type: "Model",
|
||||
name: "doll",
|
||||
|
@ -39,7 +47,12 @@ function createDoll() {
|
|||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
collisionsWillMove: true
|
||||
collisionsWillMove: true,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
return doll;
|
||||
}
|
||||
|
|
|
@ -18,14 +18,27 @@ var scriptURL = Script.resolvePath('flashlight.js');
|
|||
|
||||
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
||||
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {x: 0, y: 0.5, z: 0}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
var center = Vec3.sum(Vec3.sum(MyAvatar.position, {
|
||||
x: 0,
|
||||
y: 0.5,
|
||||
z: 0
|
||||
}), Vec3.multiply(0.5, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
var flashlight = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: modelURL,
|
||||
position: center,
|
||||
dimensions: { x: 0.08, y: 0.30, z: 0.08},
|
||||
dimensions: {
|
||||
x: 0.08,
|
||||
y: 0.30,
|
||||
z: 0.08
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
shapeType: 'box',
|
||||
script: scriptURL
|
||||
});
|
||||
script: scriptURL,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
|
@ -35,7 +35,12 @@ var pingPongGun = Entities.addEntity({
|
|||
z: 0.47
|
||||
},
|
||||
collisionsWillMove: true,
|
||||
collisionSoundURL: COLLISION_SOUND_URL
|
||||
collisionSoundURL: COLLISION_SOUND_URL,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
invertSolidWhileHeld: true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function cleanUp() {
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
Controller.Standard.LT,
|
||||
Controller.Standard.RT,
|
||||
];
|
||||
var RELOAD_THRESHOLD = 0.95;
|
||||
|
||||
Pistol = function() {
|
||||
_this = this;
|
||||
this.equipped = false;
|
||||
|
@ -45,6 +47,7 @@
|
|||
};
|
||||
|
||||
Pistol.prototype = {
|
||||
canShoot: false,
|
||||
|
||||
startEquip: function(id, params) {
|
||||
this.equipped = true;
|
||||
|
@ -62,6 +65,17 @@
|
|||
},
|
||||
toggleWithTriggerPressure: function() {
|
||||
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]);
|
||||
|
||||
if (this.triggerValue < RELOAD_THRESHOLD) {
|
||||
// print('RELOAD');
|
||||
this.canShoot = true;
|
||||
}
|
||||
if (this.canShoot === true && this.triggerValue === 1) {
|
||||
// print('SHOOT');
|
||||
this.fire();
|
||||
this.canShoot = false;
|
||||
}
|
||||
|
||||
if (this.triggerValue < DISABLE_LASER_THRESHOLD && this.showLaser === true) {
|
||||
this.showLaser = false;
|
||||
Overlays.editOverlay(this.laser, {
|
||||
|
@ -74,6 +88,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
updateLaser: function() {
|
||||
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
|
@ -101,7 +116,7 @@
|
|||
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.initControllerMapping();
|
||||
// this.initControllerMapping();
|
||||
this.laser = Overlays.addOverlay("line3d", {
|
||||
start: ZERO_VECTOR,
|
||||
end: ZERO_VECTOR,
|
||||
|
@ -150,22 +165,7 @@
|
|||
}
|
||||
},
|
||||
|
||||
initControllerMapping: function() {
|
||||
this.mapping = Controller.newMapping();
|
||||
this.mapping.from(Controller.Standard.LT).hysteresis(0.0, 0.75).to(function(value) {
|
||||
_this.triggerPress(0, value);
|
||||
});
|
||||
|
||||
|
||||
this.mapping.from(Controller.Standard.RT).hysteresis(0.0, 0.75).to(function(value) {
|
||||
_this.triggerPress(1, value);
|
||||
});
|
||||
this.mapping.enable();
|
||||
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
this.mapping.disable();
|
||||
Overlays.deleteOverlay(this.laser);
|
||||
},
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ var createdStereoInputMenuItem = false;
|
|||
var DEVELOPER_MENU = "Developer";
|
||||
|
||||
var ENTITIES_MENU = DEVELOPER_MENU + " > Entities";
|
||||
var COLLISION_UPDATES_TO_SERVER = "Don't send collision updates to server";
|
||||
|
||||
var RENDER_MENU = DEVELOPER_MENU + " > Render";
|
||||
var ENTITIES_ITEM = "Entities";
|
||||
|
@ -66,7 +65,6 @@ function setupMenus() {
|
|||
Menu.addMenuItem({ menuName: "Developer > Entities", menuItemName: "Don't Do Precision Picking", isCheckable: true, isChecked: false });
|
||||
Menu.addMenuItem({ menuName: "Developer > Entities", menuItemName: "Disable Light Entities", isCheckable: true, isChecked: false });
|
||||
*/
|
||||
Menu.addMenuItem({ menuName: ENTITIES_MENU, menuItemName: COLLISION_UPDATES_TO_SERVER, isCheckable: true, isChecked: false });
|
||||
}
|
||||
|
||||
if (!Menu.menuExists(RENDER_MENU)) {
|
||||
|
@ -112,11 +110,7 @@ function setupMenus() {
|
|||
Menu.menuItemEvent.connect(function (menuItem) {
|
||||
print("menuItemEvent() in JS... menuItem=" + menuItem);
|
||||
|
||||
if (menuItem == COLLISION_UPDATES_TO_SERVER) {
|
||||
var dontSendUpdates = Menu.isOptionChecked(COLLISION_UPDATES_TO_SERVER);
|
||||
print(" dontSendUpdates... checked=" + dontSendUpdates);
|
||||
Entities.setSendPhysicsUpdates(!dontSendUpdates);
|
||||
} else if (menuItem == ENTITIES_ITEM) {
|
||||
if (menuItem == ENTITIES_ITEM) {
|
||||
Scene.shouldRenderEntities = Menu.isOptionChecked(ENTITIES_ITEM);
|
||||
} else if (menuItem == AVATARS_ITEM) {
|
||||
Scene.shouldRenderAvatars = Menu.isOptionChecked(AVATARS_ITEM);
|
||||
|
|
69
examples/utilities/tools/reverbTest.js
Normal file
69
examples/utilities/tools/reverbTest.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// reverbTest.js
|
||||
// examples
|
||||
//
|
||||
// Created by Ken Cooke on 11/23/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
Script.include("cookies.js");
|
||||
|
||||
var audioOptions = new AudioEffectOptions({
|
||||
maxRoomSize: 50,
|
||||
roomSize: 50,
|
||||
reverbTime: 4,
|
||||
damping: 0.50,
|
||||
inputBandwidth: 0.8,
|
||||
earlyLevel: 0,
|
||||
tailLevel: 0,
|
||||
dryLevel: -6,
|
||||
wetLevel: -6
|
||||
});
|
||||
|
||||
AudioDevice.setReverbOptions(audioOptions);
|
||||
AudioDevice.setReverb(true);
|
||||
print("Reverb is ON.");
|
||||
|
||||
var panel = new Panel(10, 200);
|
||||
|
||||
var parameters = [
|
||||
{ name: "roomSize", min: 0, max: 100, units: " feet" },
|
||||
{ name: "reverbTime", min: 0, max: 10, units: " sec" },
|
||||
{ name: "damping", min: 0, max: 1, units: " " },
|
||||
{ name: "inputBandwidth", min: 0, max: 1, units: " " },
|
||||
{ name: "earlyLevel", min: -48, max: 0, units: " dB" },
|
||||
{ name: "tailLevel", min: -48, max: 0, units: " dB" },
|
||||
{ name: "wetLevel", min: -48, max: 0, units: " dB" },
|
||||
]
|
||||
|
||||
function setter(name) {
|
||||
return function(value) { audioOptions[name] = value; AudioDevice.setReverbOptions(audioOptions); }
|
||||
}
|
||||
|
||||
function getter(name) {
|
||||
return function() { return audioOptions[name]; }
|
||||
}
|
||||
|
||||
function displayer(units) {
|
||||
return function(value) { return (value).toFixed(1) + units; };
|
||||
}
|
||||
|
||||
// create a slider for each parameter
|
||||
for (var i = 0; i < parameters.length; i++) {
|
||||
var p = parameters[i];
|
||||
panel.newSlider(p.name, p.min, p.max, setter(p.name), getter(p.name), displayer(p.units));
|
||||
}
|
||||
|
||||
Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); });
|
||||
Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); });
|
||||
Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); });
|
||||
|
||||
function scriptEnding() {
|
||||
panel.destroy();
|
||||
AudioDevice.setReverb(false);
|
||||
print("Reverb is OFF.");
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
|
@ -2,7 +2,7 @@ set(TARGET_NAME interface)
|
|||
project(${TARGET_NAME})
|
||||
|
||||
# set a default root dir for each of our optional externals if it was not passed
|
||||
set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK")
|
||||
set(OPTIONAL_EXTERNALS "LeapMotion")
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient")
|
||||
|
@ -161,13 +161,6 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
|||
endif ()
|
||||
endforeach()
|
||||
|
||||
# special OS X modifications for RtMidi library
|
||||
if (RTMIDI_FOUND AND NOT DISABLE_RTMIDI AND APPLE)
|
||||
find_library(CoreMIDI CoreMIDI)
|
||||
add_definitions(-D__MACOSX_CORE__)
|
||||
target_link_libraries(${TARGET_NAME} ${CoreMIDI})
|
||||
endif ()
|
||||
|
||||
# include headers for interface and InterfaceConfig.
|
||||
include_directories("${PROJECT_SOURCE_DIR}/src")
|
||||
|
||||
|
|
9
interface/external/rssdk/readme.txt
vendored
9
interface/external/rssdk/readme.txt
vendored
|
@ -1,9 +0,0 @@
|
|||
|
||||
Instructions for adding the Intel Realsense (RSSDK) to Interface
|
||||
Thijs Wenker, December 19, 2014
|
||||
|
||||
This is Windows only for now.
|
||||
|
||||
1. Download the SDK at https://software.intel.com/en-us/intel-realsense-sdk/download
|
||||
|
||||
2. Copy the `lib` and `include` folder inside this directory
|
43
interface/external/rtmidi/readme.txt
vendored
43
interface/external/rtmidi/readme.txt
vendored
|
@ -1,43 +0,0 @@
|
|||
|
||||
Instructions for adding the RtMidi library to Interface
|
||||
Stephen Birarda, June 30, 2014
|
||||
|
||||
1. Download the RtMidi tarball from High Fidelity S3.
|
||||
http://public.highfidelity.io/dependencies/rtmidi-2.1.0.tar.gz
|
||||
|
||||
2. Copy RtMidi.h to externals/rtmidi/include.
|
||||
|
||||
3. Compile the RtMidi library.
|
||||
|
||||
3. Copy either librtmidi.dylib (dynamic) or librtmidi.a (static) to externals/rtmidi/lib
|
||||
|
||||
4. Delete your build directory, run cmake and build, and you should be all set.
|
||||
|
||||
=========================
|
||||
|
||||
RtMidi: realtime MIDI i/o C++ classes<BR>
|
||||
Copyright (c) 2003-2014 Gary P. Scavone
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
Any person wishing to distribute modifications to the Software is
|
||||
asked to send the modifications to the original developer so that
|
||||
they can be incorporated into the canonical version. This is,
|
||||
however, not a binding provision of this license.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -13,12 +13,11 @@
|
|||
{ "from": "Hydra.RB", "to": "Standard.RB" },
|
||||
{ "from": "Hydra.RS", "to": "Standard.RS" },
|
||||
|
||||
{ "from": [ "Hydra.L3", "Hydra.L4" ], "to": "Standard.LeftPrimaryThumb" },
|
||||
{ "from": [ "Hydra.L1", "Hydra.L2" ], "to": "Standard.LeftSecondaryThumb" },
|
||||
|
||||
{ "from": [ "Hydra.R3", "Hydra.R4" ], "to": "Standard.RightPrimaryThumb" },
|
||||
{ "from": [ "Hydra.R1", "Hydra.R2" ], "to": "Standard.RightSecondaryThumb" },
|
||||
{ "from": [ "Hydra.L1", "Hydra.L2", "Hydra.L3", "Hydra.L4" ], "to": "Standard.LeftPrimaryThumb" },
|
||||
{ "from": [ "Hydra.L0" ], "to": "Standard.LeftSecondaryThumb" },
|
||||
|
||||
{ "from": [ "Hydra.R1", "Hydra.R2", "Hydra.R3", "Hydra.R4" ], "to": "Standard.RightPrimaryThumb" },
|
||||
{ "from": [ "Hydra.R0" ], "to": "Standard.RightSecondaryThumb" },
|
||||
|
||||
{ "from": "Hydra.LeftHand", "to": "Standard.LeftHand" },
|
||||
{ "from": "Hydra.RightHand", "to": "Standard.RightHand" }
|
||||
|
|
|
@ -45,7 +45,12 @@ Item {
|
|||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Framerate: " + root.framerate
|
||||
text: "Render Rate: " + root.renderrate
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
font.pixelSize: root.fontSize
|
||||
text: "Present Rate: " + root.presentrate
|
||||
}
|
||||
Text {
|
||||
color: root.fontColor;
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
|
||||
#include <QtNetwork/QNetworkDiskCache>
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <ApplicationVersion.h>
|
||||
|
@ -78,7 +81,7 @@
|
|||
#include <ObjectMotionState.h>
|
||||
#include <OctalCode.h>
|
||||
#include <OctreeSceneStats.h>
|
||||
#include <gl/OffscreenGlCanvas.h>
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <PathUtils.h>
|
||||
#include <PerfStat.h>
|
||||
#include <PhysicsEngine.h>
|
||||
|
@ -110,8 +113,6 @@
|
|||
#include "devices/EyeTracker.h"
|
||||
#include "devices/Faceshift.h"
|
||||
#include "devices/Leapmotion.h"
|
||||
#include "devices/MIDIManager.h"
|
||||
#include "devices/RealSense.h"
|
||||
#include "DiscoverabilityManager.h"
|
||||
#include "GLCanvas.h"
|
||||
#include "InterfaceActionFactory.h"
|
||||
|
@ -149,6 +150,8 @@
|
|||
#include "ui/Stats.h"
|
||||
#include "ui/UpdateDialog.h"
|
||||
#include "Util.h"
|
||||
#include "InterfaceParentFinder.h"
|
||||
|
||||
|
||||
|
||||
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
|
@ -297,6 +300,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
|
||||
DependencyManager::registerInheritance<EntityActionFactoryInterface, InterfaceActionFactory>();
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>();
|
||||
|
||||
Setting::init();
|
||||
|
||||
|
@ -345,6 +349,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
DependencyManager::set<MessagesClient>();
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
|
||||
DependencyManager::set<InterfaceParentFinder>();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -615,8 +620,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
|
||||
// enable mouse tracking; otherwise, we only get drag events
|
||||
_glWidget->setMouseTracking(true);
|
||||
_glWidget->makeCurrent();
|
||||
_glWidget->initializeGL();
|
||||
|
||||
_offscreenContext = new OffscreenGlCanvas();
|
||||
_offscreenContext = new OffscreenGLCanvas();
|
||||
_offscreenContext->create(_glWidget->context()->contextHandle());
|
||||
_offscreenContext->makeCurrent();
|
||||
initializeGL();
|
||||
|
@ -719,12 +726,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
// set the local loopback interface for local sounds from audio scripts
|
||||
AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data());
|
||||
|
||||
#ifdef HAVE_RTMIDI
|
||||
// setup the MIDIManager
|
||||
MIDIManager& midiManagerInstance = MIDIManager::getInstance();
|
||||
midiManagerInstance.openDefaultPort();
|
||||
#endif
|
||||
|
||||
this->installEventFilter(this);
|
||||
|
||||
// initialize our face trackers after loading the menu settings
|
||||
|
@ -970,7 +971,6 @@ Application::~Application() {
|
|||
nodeThread->wait();
|
||||
|
||||
Leapmotion::destroy();
|
||||
RealSense::destroy();
|
||||
|
||||
#if 0
|
||||
ConnexionClient::getInstance().destroy();
|
||||
|
@ -1141,7 +1141,7 @@ void Application::paintGL() {
|
|||
_lastInstantaneousFps = instantaneousFps;
|
||||
|
||||
auto displayPlugin = getActiveDisplayPlugin();
|
||||
displayPlugin->preRender();
|
||||
// FIXME not needed anymore?
|
||||
_offscreenContext->makeCurrent();
|
||||
|
||||
// update the avatar with a fresh HMD pose
|
||||
|
@ -1196,6 +1196,9 @@ void Application::paintGL() {
|
|||
QSize size = getDeviceSize();
|
||||
renderArgs._viewport = glm::ivec4(0, 0, size.width(), size.height());
|
||||
_applicationOverlay.renderOverlay(&renderArgs);
|
||||
gpu::FramebufferPointer overlayFramebuffer = _applicationOverlay.getOverlayFramebuffer();
|
||||
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1250,7 +1253,7 @@ void Application::paintGL() {
|
|||
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)) * hmdRotation);
|
||||
glm::vec3 hmdOffset = extractTranslation(myAvatar->getHMDSensorMatrix());
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getScale(), 0)
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0)
|
||||
+ (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) *
|
||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror
|
||||
+ (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))) * hmdOffset);
|
||||
|
@ -1258,7 +1261,7 @@ void Application::paintGL() {
|
|||
_myCamera.setRotation(myAvatar->getWorldAlignedOrientation()
|
||||
* glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)));
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getScale(), 0)
|
||||
+ glm::vec3(0, _raiseMirror * myAvatar->getAvatarScale(), 0)
|
||||
+ (myAvatar->getOrientation() * glm::quat(glm::vec3(0.0f, _rotateMirror, 0.0f))) *
|
||||
glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_FULLSCREEN_DISTANCE * _scaleMirror);
|
||||
}
|
||||
|
@ -1309,6 +1312,13 @@ void Application::paintGL() {
|
|||
auto baseProjection = renderArgs._viewFrustum->getProjection();
|
||||
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
|
||||
float IPDScale = hmdInterface->getIPDScale();
|
||||
|
||||
// Tell the plugin what pose we're using to render. In this case we're just using the
|
||||
// unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
|
||||
// for rotational timewarp. If we move to support positonal timewarp, we need to
|
||||
// ensure this contains the full pose composed with the eye offsets.
|
||||
mat4 headPose = displayPlugin->getHeadPose(_frameCount);
|
||||
|
||||
// FIXME we probably don't need to set the projection matrix every frame,
|
||||
// only when the display plugin changes (or in non-HMD modes when the user
|
||||
// changes the FOV manually, which right now I don't think they can.
|
||||
|
@ -1324,12 +1334,7 @@ void Application::paintGL() {
|
|||
mat4 eyeOffsetTransform = glm::translate(mat4(), eyeOffset * -1.0f * IPDScale);
|
||||
eyeOffsets[eye] = eyeOffsetTransform;
|
||||
|
||||
// Tell the plugin what pose we're using to render. In this case we're just using the
|
||||
// unmodified head pose because the only plugin that cares (the Oculus plugin) uses it
|
||||
// for rotational timewarp. If we move to support positonal timewarp, we need to
|
||||
// ensure this contains the full pose composed with the eye offsets.
|
||||
mat4 headPose = displayPlugin->getHeadPose();
|
||||
displayPlugin->setEyeRenderPose(eye, headPose);
|
||||
displayPlugin->setEyeRenderPose(_frameCount, eye, headPose);
|
||||
|
||||
eyeProjections[eye] = displayPlugin->getProjection(eye, baseProjection);
|
||||
});
|
||||
|
@ -1344,6 +1349,7 @@ void Application::paintGL() {
|
|||
}
|
||||
|
||||
// Overlay Composition, needs to occur after screen space effects have completed
|
||||
// FIXME migrate composition into the display plugins
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/compositor");
|
||||
PerformanceTimer perfTimer("compositor");
|
||||
|
@ -1372,44 +1378,40 @@ void Application::paintGL() {
|
|||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginOutput");
|
||||
PerformanceTimer perfTimer("pluginOutput");
|
||||
auto primaryFbo = framebufferCache->getPrimaryFramebuffer();
|
||||
GLuint finalTexture = gpu::GLBackend::getTextureID(primaryFbo->getRenderBuffer(0));
|
||||
// Ensure the rendering context commands are completed when rendering
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
// Ensure the sync object is flushed to the driver thread before releasing the context
|
||||
// CRITICAL for the mac driver apparently.
|
||||
glFlush();
|
||||
_offscreenContext->doneCurrent();
|
||||
auto primaryFramebuffer = framebufferCache->getPrimaryFramebuffer();
|
||||
auto scratchFramebuffer = framebufferCache->getFramebuffer();
|
||||
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
|
||||
gpu::Vec4i rect;
|
||||
rect.z = size.width();
|
||||
rect.w = size.height();
|
||||
batch.setFramebuffer(scratchFramebuffer);
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
batch.blit(primaryFramebuffer, rect, scratchFramebuffer, rect);
|
||||
batch.setFramebuffer(nullptr);
|
||||
});
|
||||
auto finalTexturePointer = scratchFramebuffer->getRenderBuffer(0);
|
||||
GLuint finalTexture = gpu::GLBackend::getTextureID(finalTexturePointer);
|
||||
Q_ASSERT(0 != finalTexture);
|
||||
|
||||
Q_ASSERT(!_lockedFramebufferMap.contains(finalTexture));
|
||||
_lockedFramebufferMap[finalTexture] = scratchFramebuffer;
|
||||
|
||||
// Switches to the display plugin context
|
||||
displayPlugin->preDisplay();
|
||||
// Ensure all operations from the previous context are complete before we try to read the fbo
|
||||
glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync(sync);
|
||||
uint64_t displayStart = usecTimestampNow();
|
||||
|
||||
Q_ASSERT(QOpenGLContext::currentContext() == _offscreenContext->getContext());
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginDisplay");
|
||||
PerformanceTimer perfTimer("pluginDisplay");
|
||||
displayPlugin->display(finalTexture, toGlm(size));
|
||||
PROFILE_RANGE(__FUNCTION__ "/pluginSubmitScene");
|
||||
PerformanceTimer perfTimer("pluginSubmitScene");
|
||||
displayPlugin->submitSceneTexture(_frameCount, finalTexture, toGlm(size));
|
||||
}
|
||||
Q_ASSERT(QOpenGLContext::currentContext() == _offscreenContext->getContext());
|
||||
|
||||
{
|
||||
PROFILE_RANGE(__FUNCTION__ "/bufferSwap");
|
||||
PerformanceTimer perfTimer("bufferSwap");
|
||||
displayPlugin->finishFrame();
|
||||
}
|
||||
uint64_t displayEnd = usecTimestampNow();
|
||||
const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs
|
||||
_lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND;
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("makeCurrent");
|
||||
_offscreenContext->makeCurrent();
|
||||
Stats::getInstance()->setRenderDetails(renderArgs._details);
|
||||
|
||||
// Reset the gpu::Context Stages
|
||||
// Back to the default framebuffer;
|
||||
gpu::doInBatch(renderArgs._context, [=](gpu::Batch& batch) {
|
||||
|
@ -2519,7 +2521,6 @@ void Application::init() {
|
|||
qCDebug(interfaceapp) << "Loaded settings";
|
||||
|
||||
Leapmotion::init();
|
||||
RealSense::init();
|
||||
|
||||
// fire off an immediate domain-server check in now that settings are loaded
|
||||
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
|
||||
|
@ -2577,7 +2578,6 @@ void Application::setAvatarUpdateThreading(bool isThreaded) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto myAvatar = getMyAvatar();
|
||||
if (_avatarUpdate) {
|
||||
_avatarUpdate->terminate(); // Must be before we shutdown anim graph.
|
||||
}
|
||||
|
@ -2619,7 +2619,7 @@ void Application::updateMyAvatarLookAtPosition() {
|
|||
lookAtPosition.x = -lookAtPosition.x;
|
||||
}
|
||||
if (isHMD) {
|
||||
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose();
|
||||
glm::mat4 headPose = getActiveDisplayPlugin()->getHeadPose(_frameCount);
|
||||
glm::quat hmdRotation = glm::quat_cast(headPose);
|
||||
lookAtSpot = _myCamera.getPosition() + myAvatar->getOrientation() * (hmdRotation * lookAtPosition);
|
||||
} else {
|
||||
|
@ -2856,6 +2856,8 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
_controllerScriptingInterface->updateInputControllers();
|
||||
|
||||
// Transfer the user inputs to the driveKeys
|
||||
// FIXME can we drop drive keys and just have the avatar read the action states directly?
|
||||
myAvatar->clearDriveKeys();
|
||||
|
@ -3073,9 +3075,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
|
|||
|
||||
// These will be the same for all servers, so we can set them up once and then reuse for each server we send to.
|
||||
_octreeQuery.setWantLowResMoving(true);
|
||||
_octreeQuery.setWantColor(true);
|
||||
_octreeQuery.setWantDelta(true);
|
||||
_octreeQuery.setWantOcclusionCulling(false);
|
||||
_octreeQuery.setWantCompression(true);
|
||||
|
||||
_octreeQuery.setCameraPosition(_viewFrustum.getPosition());
|
||||
|
@ -3321,7 +3321,7 @@ MyAvatar* Application::getMyAvatar() const {
|
|||
return DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
}
|
||||
|
||||
const glm::vec3& Application::getAvatarPosition() const {
|
||||
glm::vec3 Application::getAvatarPosition() const {
|
||||
return getMyAvatar()->getPosition();
|
||||
}
|
||||
|
||||
|
@ -4068,10 +4068,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget());
|
||||
|
||||
#ifdef HAVE_RTMIDI
|
||||
scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Application::canAcceptURL(const QString& urlString) {
|
||||
|
@ -4519,7 +4515,7 @@ void Application::takeSnapshot() {
|
|||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||
player->play();
|
||||
|
||||
QString fileName = Snapshot::saveSnapshot(_glWidget->grabFrameBuffer());
|
||||
QString fileName = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot());
|
||||
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
if (!accountManager.isLoggedIn()) {
|
||||
|
@ -4530,7 +4526,6 @@ void Application::takeSnapshot() {
|
|||
_snapshotShareDialog = new SnapshotShareDialog(fileName, _glWidget);
|
||||
}
|
||||
_snapshotShareDialog->show();
|
||||
|
||||
}
|
||||
|
||||
float Application::getRenderResolutionScale() const {
|
||||
|
@ -4713,10 +4708,6 @@ const DisplayPlugin* Application::getActiveDisplayPlugin() const {
|
|||
return ((Application*)this)->getActiveDisplayPlugin();
|
||||
}
|
||||
|
||||
bool _activatingDisplayPlugin{ false };
|
||||
QVector<QPair<QString, QString>> _currentDisplayPluginActions;
|
||||
QVector<QPair<QString, QString>> _currentInputPluginActions;
|
||||
|
||||
static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
|
||||
auto menu = Menu::getInstance();
|
||||
QString name = displayPlugin->getName();
|
||||
|
@ -4746,9 +4737,10 @@ void Application::updateDisplayMode() {
|
|||
bool first = true;
|
||||
foreach(auto displayPlugin, displayPlugins) {
|
||||
addDisplayPluginToMenu(displayPlugin, first);
|
||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender, [this] {
|
||||
paintGL();
|
||||
});
|
||||
// This must be a queued connection to avoid a deadlock
|
||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender,
|
||||
this, &Application::paintGL, Qt::QueuedConnection);
|
||||
|
||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {
|
||||
resizeGL();
|
||||
});
|
||||
|
@ -4790,19 +4782,18 @@ void Application::updateDisplayMode() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!_currentDisplayPluginActions.isEmpty()) {
|
||||
|
||||
if (!_pluginContainer->currentDisplayActions().isEmpty()) {
|
||||
auto menu = Menu::getInstance();
|
||||
foreach(auto itemInfo, _currentDisplayPluginActions) {
|
||||
foreach(auto itemInfo, _pluginContainer->currentDisplayActions()) {
|
||||
menu->removeMenuItem(itemInfo.first, itemInfo.second);
|
||||
}
|
||||
_currentDisplayPluginActions.clear();
|
||||
_pluginContainer->currentDisplayActions().clear();
|
||||
}
|
||||
|
||||
if (newDisplayPlugin) {
|
||||
_offscreenContext->makeCurrent();
|
||||
_activatingDisplayPlugin = true;
|
||||
newDisplayPlugin->activate();
|
||||
_activatingDisplayPlugin = false;
|
||||
_offscreenContext->makeCurrent();
|
||||
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
|
||||
_offscreenContext->makeCurrent();
|
||||
|
@ -4928,7 +4919,7 @@ mat4 Application::getEyeOffset(int eye) const {
|
|||
|
||||
mat4 Application::getHMDSensorPose() const {
|
||||
if (isHMDMode()) {
|
||||
return getActiveDisplayPlugin()->getHeadPose();
|
||||
return getActiveDisplayPlugin()->getHeadPose(_frameCount);
|
||||
}
|
||||
return mat4();
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
#include "ui/ToolWindow.h"
|
||||
#include "UndoStackScriptingInterface.h"
|
||||
|
||||
class OffscreenGlCanvas;
|
||||
class OffscreenGLCanvas;
|
||||
class GLCanvas;
|
||||
class FaceTracker;
|
||||
class MainWindow;
|
||||
|
@ -158,6 +158,7 @@ public:
|
|||
|
||||
bool isForeground() const { return _isForeground; }
|
||||
|
||||
uint32_t getFrameCount() { return _frameCount; }
|
||||
float getFps() const { return _fps; }
|
||||
float const HMD_TARGET_FRAME_RATE = 75.0f;
|
||||
float const DESKTOP_TARGET_FRAME_RATE = 60.0f;
|
||||
|
@ -185,7 +186,7 @@ public:
|
|||
virtual float getSizeScale() const;
|
||||
virtual int getBoundaryLevelAdjust() const;
|
||||
virtual PickRay computePickRay(float x, float y) const;
|
||||
virtual const glm::vec3& getAvatarPosition() const;
|
||||
virtual glm::vec3 getAvatarPosition() const;
|
||||
virtual void overrideEnvironmentData(const EnvironmentData& newData) { _environment.override(newData); }
|
||||
virtual void endOverrideEnvironmentData() { _environment.endOverride(); }
|
||||
virtual qreal getDevicePixelRatio();
|
||||
|
@ -421,10 +422,13 @@ private:
|
|||
|
||||
bool _dependencyManagerIsSetup;
|
||||
|
||||
OffscreenGlCanvas* _offscreenContext { nullptr };
|
||||
OffscreenGLCanvas* _offscreenContext { nullptr };
|
||||
DisplayPluginPointer _displayPlugin;
|
||||
InputPluginList _activeInputPlugins;
|
||||
|
||||
bool _activatingDisplayPlugin { false };
|
||||
QMap<uint32_t, gpu::FramebufferPointer> _lockedFramebufferMap;
|
||||
|
||||
MainWindow* _window;
|
||||
|
||||
ToolWindow* _toolWindow;
|
||||
|
|
|
@ -9,133 +9,13 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// FIXME ordering of headers
|
||||
#include "Application.h"
|
||||
#include "GLCanvas.h"
|
||||
|
||||
#include <QMimeData>
|
||||
#include <QUrl>
|
||||
#include <QWindow>
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "Menu.h"
|
||||
|
||||
static QGLFormat& getDesiredGLFormat() {
|
||||
// Specify an OpenGL 3.3 format using the Core profile.
|
||||
// That is, no old-school fixed pipeline functionality
|
||||
static QGLFormat glFormat;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [] {
|
||||
glFormat.setVersion(4, 1);
|
||||
glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0
|
||||
glFormat.setSampleBuffers(false);
|
||||
glFormat.setDepth(false);
|
||||
glFormat.setStencil(false);
|
||||
});
|
||||
return glFormat;
|
||||
}
|
||||
|
||||
GLCanvas::GLCanvas() : QGLWidget(getDesiredGLFormat()) {
|
||||
#ifdef Q_OS_LINUX
|
||||
// Cause GLCanvas::eventFilter to be called.
|
||||
// It wouldn't hurt to do this on Mac and PC too; but apparently it's only needed on linux.
|
||||
qApp->installEventFilter(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
int GLCanvas::getDeviceWidth() const {
|
||||
return width() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f);
|
||||
}
|
||||
|
||||
int GLCanvas::getDeviceHeight() const {
|
||||
return height() * (windowHandle() ? (float)windowHandle()->devicePixelRatio() : 1.0f);
|
||||
}
|
||||
|
||||
void GLCanvas::initializeGL() {
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
setAcceptDrops(true);
|
||||
// Note, we *DO NOT* want Qt to automatically swap buffers for us. This results in the "ringing" bug mentioned in WL#19514 when we're throttling the framerate.
|
||||
setAutoBufferSwap(false);
|
||||
}
|
||||
|
||||
void GLCanvas::paintGL() {
|
||||
PROFILE_RANGE(__FUNCTION__);
|
||||
|
||||
// FIXME - I'm not sure why this still remains, it appears as if this GLCanvas gets a single paintGL call near
|
||||
// the beginning of the application starting up. I'm not sure if we really need to call Application::paintGL()
|
||||
// in this case, since the display plugins eventually handle all the painting
|
||||
bool isThrottleFPSEnabled = Menu::getInstance()->isOptionChecked(MenuOption::ThrottleFPSIfNotFocus);
|
||||
if (!qApp->getWindow()->isMinimized() || !isThrottleFPSEnabled) {
|
||||
qApp->paintGL();
|
||||
}
|
||||
}
|
||||
|
||||
void GLCanvas::resizeGL(int width, int height) {
|
||||
qApp->resizeGL();
|
||||
}
|
||||
|
||||
bool GLCanvas::event(QEvent* event) {
|
||||
switch (event->type()) {
|
||||
case QEvent::MouseMove:
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::MouseButtonDblClick:
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
case QEvent::FocusIn:
|
||||
case QEvent::FocusOut:
|
||||
case QEvent::Resize:
|
||||
case QEvent::TouchBegin:
|
||||
case QEvent::TouchEnd:
|
||||
case QEvent::TouchUpdate:
|
||||
case QEvent::Wheel:
|
||||
case QEvent::DragEnter:
|
||||
case QEvent::Drop:
|
||||
if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case QEvent::Paint:
|
||||
// Ignore paint events that occur after we've decided to quit
|
||||
if (qApp->isAboutToQuit()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
if (QEvent::Paint == event->type() && qApp->isAboutToQuit()) {
|
||||
return true;
|
||||
}
|
||||
return QGLWidget::event(event);
|
||||
}
|
||||
|
||||
|
||||
// Pressing Alt (and Meta) key alone activates the menubar because its style inherits the
|
||||
// SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to
|
||||
// receive keyPress events for the Alt (and Meta) key in a reliable manner.
|
||||
//
|
||||
// This filter catches events before QMenuBar can steal the keyboard focus.
|
||||
// The idea was borrowed from
|
||||
// http://www.archivum.info/qt-interest@trolltech.com/2006-09/00053/Re-(Qt4)-Alt-key-focus-QMenuBar-(solved).html
|
||||
|
||||
bool GLCanvas::eventFilter(QObject*, QEvent* event) {
|
||||
switch (event->type()) {
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
case QEvent::ShortcutOverride:
|
||||
{
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->key() == Qt::Key_Alt || keyEvent->key() == Qt::Key_Meta) {
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
keyPressEvent(keyEvent);
|
||||
} else if (event->type() == QEvent::KeyRelease) {
|
||||
keyReleaseEvent(keyEvent);
|
||||
} else {
|
||||
QGLWidget::event(event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
return GLWidget::event(event);
|
||||
}
|
||||
|
|
|
@ -12,31 +12,13 @@
|
|||
#ifndef hifi_GLCanvas_h
|
||||
#define hifi_GLCanvas_h
|
||||
|
||||
#include <QDebug>
|
||||
#include <QGLWidget>
|
||||
#include <QTimer>
|
||||
#include <gl/GLWidget.h>
|
||||
|
||||
/// customized canvas that simply forwards requests/events to the singleton application
|
||||
class GLCanvas : public QGLWidget {
|
||||
class GLCanvas : public GLWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GLCanvas();
|
||||
|
||||
int getDeviceWidth() const;
|
||||
int getDeviceHeight() const;
|
||||
QSize getDeviceSize() const { return QSize(getDeviceWidth(), getDeviceHeight()); }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void initializeGL();
|
||||
virtual void paintGL();
|
||||
virtual void resizeGL(int width, int height);
|
||||
virtual bool event(QEvent* event);
|
||||
|
||||
private slots:
|
||||
bool eventFilter(QObject*, QEvent* event);
|
||||
|
||||
virtual bool event(QEvent* event) override;
|
||||
};
|
||||
|
||||
|
||||
|
|
37
interface/src/InterfaceParentFinder.cpp
Normal file
37
interface/src/InterfaceParentFinder.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// InterfaceParentFinder.cpp
|
||||
// interface/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-10-21
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <Application.h>
|
||||
#include <EntityTree.h>
|
||||
#include <EntityTreeRenderer.h>
|
||||
#include <avatar/AvatarManager.h>
|
||||
|
||||
#include "InterfaceParentFinder.h"
|
||||
|
||||
SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID) const {
|
||||
SpatiallyNestableWeakPointer parent;
|
||||
|
||||
if (parentID.isNull()) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
// search entities
|
||||
EntityTreeRenderer* treeRenderer = qApp->getEntities();
|
||||
EntityTreePointer tree = treeRenderer->getTree();
|
||||
parent = tree->findEntityByEntityItemID(parentID);
|
||||
if (!parent.expired()) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
// search avatars
|
||||
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
|
||||
return avatarManager->getAvatarBySessionID(parentID);
|
||||
}
|
27
interface/src/InterfaceParentFinder.h
Normal file
27
interface/src/InterfaceParentFinder.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// InterfaceParentFinder.h
|
||||
// interface/src/
|
||||
//
|
||||
// Created by Seth Alves on 2015-10-21
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_InterfaceParentFinder_h
|
||||
#define hifi_InterfaceParentFinder_h
|
||||
|
||||
#include <memory>
|
||||
#include <QUuid>
|
||||
|
||||
#include <SpatialParentFinder.h>
|
||||
|
||||
class InterfaceParentFinder : public SpatialParentFinder {
|
||||
public:
|
||||
InterfaceParentFinder() { }
|
||||
virtual ~InterfaceParentFinder() { }
|
||||
virtual SpatiallyNestableWeakPointer find(QUuid parentID) const;
|
||||
};
|
||||
|
||||
#endif // hifi_InterfaceParentFinder_h
|
|
@ -29,7 +29,6 @@
|
|||
#include "avatar/AvatarManager.h"
|
||||
#include "devices/DdeFaceTracker.h"
|
||||
#include "devices/Faceshift.h"
|
||||
#include "devices/RealSense.h"
|
||||
#include "input-plugins/SpacemouseManager.h"
|
||||
#include "MainWindow.h"
|
||||
#include "scripting/MenuScriptingInterface.h"
|
||||
|
@ -433,8 +432,6 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false,
|
||||
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSkeletonCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtTargets, 0, false);
|
||||
|
@ -462,12 +459,6 @@ Menu::Menu() {
|
|||
MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion");
|
||||
addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false);
|
||||
|
||||
#ifdef HAVE_RSSDK
|
||||
MenuWrapper* realSenseOptionsMenu = handOptionsMenu->addMenu("RealSense");
|
||||
addActionToQMenuAndActionHash(realSenseOptionsMenu, MenuOption::LoadRSSDKFile, 0,
|
||||
RealSense::getInstance(), SLOT(loadRSSDKFile()));
|
||||
#endif
|
||||
|
||||
MenuWrapper* networkMenu = developerMenu->addMenu("Network");
|
||||
addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
|
||||
addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false,
|
||||
|
|
|
@ -211,7 +211,6 @@ namespace MenuOption {
|
|||
const QString LeapMotionOnHMD = "Leap Motion on HMD";
|
||||
const QString LoadScript = "Open and Run Script File...";
|
||||
const QString LoadScriptURL = "Open and Run Script from URL...";
|
||||
const QString LoadRSSDKFile = "Load .rssdk file";
|
||||
const QString LodTools = "LOD Tools";
|
||||
const QString Login = "Login";
|
||||
const QString Log = "Log";
|
||||
|
@ -239,10 +238,8 @@ namespace MenuOption {
|
|||
const QString ReloadContent = "Reload Content (Clears all caches)";
|
||||
const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes";
|
||||
const QString RenderFocusIndicator = "Show Eye Focus";
|
||||
const QString RenderHeadCollisionShapes = "Show Head Collision Shapes";
|
||||
const QString RenderLookAtTargets = "Show Look-at Targets";
|
||||
const QString RenderLookAtVectors = "Show Look-at Vectors";
|
||||
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
|
||||
const QString RenderResolution = "Scale Resolution";
|
||||
const QString RenderResolutionOne = "1";
|
||||
const QString RenderResolutionTwoThird = "2/3";
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
#include "PluginContainerProxy.h"
|
||||
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
#include <QtGui/QScreen>
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
#include <plugins/Plugin.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <FramebufferCache.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MainWindow.h"
|
||||
#include "GLCanvas.h"
|
||||
#include "ui/DialogsManager.h"
|
||||
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
|
||||
PluginContainerProxy::PluginContainerProxy() {
|
||||
}
|
||||
|
||||
|
@ -30,12 +35,7 @@ void PluginContainerProxy::removeMenu(const QString& menuName) {
|
|||
Menu::getInstance()->removeMenu(menuName);
|
||||
}
|
||||
|
||||
extern bool _activatingDisplayPlugin;
|
||||
extern QVector<QPair<QString, QString>> _currentDisplayPluginActions;
|
||||
extern QVector<QPair<QString, QString>> _currentInputPluginActions;
|
||||
std::map<QString, QActionGroup*> _exclusiveGroups;
|
||||
|
||||
QAction* PluginContainerProxy::addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName) {
|
||||
QAction* PluginContainerProxy::addMenuItem(PluginType type, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable, bool checked, const QString& groupName) {
|
||||
auto menu = Menu::getInstance();
|
||||
MenuWrapper* parentItem = menu->getMenu(path);
|
||||
QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name);
|
||||
|
@ -54,7 +54,7 @@ QAction* PluginContainerProxy::addMenuItem(const QString& path, const QString& n
|
|||
});
|
||||
action->setCheckable(checkable);
|
||||
action->setChecked(checked);
|
||||
if (_activatingDisplayPlugin) {
|
||||
if (type == PluginType::DISPLAY_PLUGIN) {
|
||||
_currentDisplayPluginActions.push_back({ path, name });
|
||||
} else {
|
||||
_currentInputPluginActions.push_back({ path, name });
|
||||
|
@ -150,10 +150,37 @@ void PluginContainerProxy::showDisplayPluginsTools() {
|
|||
DependencyManager::get<DialogsManager>()->hmdTools(true);
|
||||
}
|
||||
|
||||
QGLWidget* PluginContainerProxy::getPrimarySurface() {
|
||||
GLWidget* PluginContainerProxy::getPrimaryWidget() {
|
||||
return qApp->_glWidget;
|
||||
}
|
||||
|
||||
QWindow* PluginContainerProxy::getPrimaryWindow() {
|
||||
return qApp->_glWidget->windowHandle();
|
||||
}
|
||||
|
||||
QOpenGLContext* PluginContainerProxy::getPrimaryContext() {
|
||||
return qApp->_glWidget->context()->contextHandle();
|
||||
}
|
||||
|
||||
const DisplayPlugin* PluginContainerProxy::getActiveDisplayPlugin() const {
|
||||
return qApp->getActiveDisplayPlugin();
|
||||
}
|
||||
|
||||
bool PluginContainerProxy::makeRenderingContextCurrent() {
|
||||
return qApp->_offscreenContext->makeCurrent();
|
||||
}
|
||||
|
||||
void PluginContainerProxy::releaseSceneTexture(uint32_t texture) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
auto& framebufferMap = qApp->_lockedFramebufferMap;
|
||||
Q_ASSERT(framebufferMap.contains(texture));
|
||||
auto framebufferPointer = framebufferMap[texture];
|
||||
framebufferMap.remove(texture);
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
framebufferCache->releaseFramebuffer(framebufferPointer);
|
||||
}
|
||||
|
||||
void PluginContainerProxy::releaseOverlayTexture(uint32_t texture) {
|
||||
// FIXME implement present thread compositing
|
||||
}
|
||||
|
||||
|
|
|
@ -2,19 +2,21 @@
|
|||
#ifndef hifi_PluginContainerProxy_h
|
||||
#define hifi_PluginContainerProxy_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QRect>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QRect>
|
||||
|
||||
#include <plugins/Forward.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
|
||||
class QActionGroup;
|
||||
|
||||
class PluginContainerProxy : public QObject, PluginContainer {
|
||||
Q_OBJECT
|
||||
PluginContainerProxy();
|
||||
virtual ~PluginContainerProxy();
|
||||
virtual void addMenu(const QString& menuName) override;
|
||||
virtual void removeMenu(const QString& menuName) override;
|
||||
virtual QAction* addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override;
|
||||
virtual QAction* addMenuItem(PluginType type, const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override;
|
||||
virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override;
|
||||
virtual bool isOptionChecked(const QString& name) override;
|
||||
virtual void setIsOptionChecked(const QString& path, bool checked) override;
|
||||
|
@ -22,13 +24,20 @@ class PluginContainerProxy : public QObject, PluginContainer {
|
|||
virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override;
|
||||
virtual void showDisplayPluginsTools() override;
|
||||
virtual void requestReset() override;
|
||||
virtual QGLWidget* getPrimarySurface() override;
|
||||
virtual bool makeRenderingContextCurrent() override;
|
||||
virtual void releaseSceneTexture(uint32_t texture) override;
|
||||
virtual void releaseOverlayTexture(uint32_t texture) override;
|
||||
virtual GLWidget* getPrimaryWidget() override;
|
||||
virtual QWindow* getPrimaryWindow() override;
|
||||
virtual QOpenGLContext* getPrimaryContext() override;
|
||||
virtual bool isForeground() override;
|
||||
virtual const DisplayPlugin* getActiveDisplayPlugin() const override;
|
||||
|
||||
QRect _savedGeometry{ 10, 120, 800, 600 };
|
||||
std::map<QString, QActionGroup*> _exclusiveGroups;
|
||||
|
||||
friend class Application;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -130,9 +130,9 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
|||
|
||||
std::call_once(once, [&] {
|
||||
{
|
||||
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)));
|
||||
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(starsGrid_frag)));
|
||||
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
|
||||
auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert));
|
||||
auto ps = gpu::Shader::createPixel(std::string(starsGrid_frag));
|
||||
auto program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram((*program));
|
||||
_timeSlot = program->getBuffers().findLocation(UNIFORM_TIME_NAME);
|
||||
if (_timeSlot == gpu::Shader::INVALID_LOCATION) {
|
||||
|
@ -143,12 +143,12 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
|||
state->setDepthTest(gpu::State::DepthTest(false));
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
_gridPipeline.reset(gpu::Pipeline::create(program, state));
|
||||
_gridPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
{
|
||||
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(stars_vert)));
|
||||
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(stars_frag)));
|
||||
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
|
||||
auto vs = gpu::Shader::createVertex(std::string(stars_vert));
|
||||
auto ps = gpu::Shader::createPixel(std::string(stars_frag));
|
||||
auto program = gpu::Shader::createProgram(vs, ps);
|
||||
gpu::Shader::makeProgram((*program));
|
||||
auto state = gpu::StatePointer(new gpu::State());
|
||||
// enable decal blend
|
||||
|
@ -156,7 +156,7 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
|
|||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
state->setAntialiasedLineEnable(true); // line smoothing also smooth points
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
_starsPipeline.reset(gpu::Pipeline::create(program, state));
|
||||
_starsPipeline = gpu::Pipeline::create(program, state);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include "Hand.h"
|
||||
#include "Head.h"
|
||||
#include "Menu.h"
|
||||
#include "ModelReferential.h"
|
||||
#include "Physics.h"
|
||||
#include "Util.h"
|
||||
#include "world.h"
|
||||
|
@ -91,7 +90,6 @@ Avatar::Avatar(RigPointer rig) :
|
|||
_angularAcceleration(0.0f),
|
||||
_lastOrientation(),
|
||||
_leanScale(0.5f),
|
||||
_scale(1.0f),
|
||||
_worldUpDirection(DEFAULT_UP_DIRECTION),
|
||||
_moving(false),
|
||||
_initialized(false),
|
||||
|
@ -101,6 +99,7 @@ Avatar::Avatar(RigPointer rig) :
|
|||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(qApp->thread());
|
||||
|
||||
setAvatarScale(1.0f);
|
||||
// give the pointer to our head to inherited _headData variable from AvatarData
|
||||
_headData = static_cast<HeadData*>(new Head(this));
|
||||
_handData = static_cast<HandData*>(new Hand(this));
|
||||
|
@ -125,12 +124,12 @@ void Avatar::init() {
|
|||
glm::vec3 Avatar::getChestPosition() const {
|
||||
// for now, let's just assume that the "chest" is halfway between the root and the neck
|
||||
glm::vec3 neckPosition;
|
||||
return _skeletonModel.getNeckPosition(neckPosition) ? (_position + neckPosition) * 0.5f : _position;
|
||||
return _skeletonModel.getNeckPosition(neckPosition) ? (getPosition() + neckPosition) * 0.5f : getPosition();
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getNeckPosition() const {
|
||||
glm::vec3 neckPosition;
|
||||
return _skeletonModel.getNeckPosition(neckPosition) ? neckPosition : _position;
|
||||
return _skeletonModel.getNeckPosition(neckPosition) ? neckPosition : getPosition();
|
||||
}
|
||||
|
||||
|
||||
|
@ -144,38 +143,14 @@ AABox Avatar::getBounds() const {
|
|||
|
||||
float Avatar::getLODDistance() const {
|
||||
return DependencyManager::get<LODManager>()->getAvatarLODDistanceMultiplier() *
|
||||
glm::distance(qApp->getCamera()->getPosition(), _position) / _scale;
|
||||
glm::distance(qApp->getCamera()->getPosition(), getPosition()) / getAvatarScale();
|
||||
}
|
||||
|
||||
void Avatar::simulate(float deltaTime) {
|
||||
PerformanceTimer perfTimer("simulate");
|
||||
|
||||
// update the avatar's position according to its referential
|
||||
if (_referential) {
|
||||
if (_referential->hasExtraData()) {
|
||||
EntityTreePointer tree = qApp->getEntities()->getTree();
|
||||
switch (_referential->type()) {
|
||||
case Referential::MODEL:
|
||||
_referential = new ModelReferential(_referential,
|
||||
tree,
|
||||
this);
|
||||
break;
|
||||
case Referential::JOINT:
|
||||
_referential = new JointReferential(_referential,
|
||||
tree,
|
||||
this);
|
||||
break;
|
||||
default:
|
||||
qCDebug(interfaceapp) << "[WARNING] Avatar::simulate(): Unknown referential type.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_referential->update();
|
||||
}
|
||||
|
||||
if (_scale != _targetScale) {
|
||||
setScale(_targetScale);
|
||||
if (getAvatarScale() != _targetScale) {
|
||||
setAvatarScale(_targetScale);
|
||||
}
|
||||
|
||||
// update the billboard render flag
|
||||
|
@ -193,7 +168,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
const bool isControllerLogging = DependencyManager::get<AvatarManager>()->getRenderDistanceControllerIsLogging();
|
||||
float renderDistance = DependencyManager::get<AvatarManager>()->getRenderDistance();
|
||||
const float SKIP_HYSTERESIS_PROPORTION = isControllerLogging ? 0.0f : BILLBOARD_HYSTERESIS_PROPORTION;
|
||||
float distance = glm::distance(qApp->getCamera()->getPosition(), _position);
|
||||
float distance = glm::distance(qApp->getCamera()->getPosition(), getPosition());
|
||||
if (_shouldSkipRender) {
|
||||
if (distance < renderDistance * (1.0f - SKIP_HYSTERESIS_PROPORTION)) {
|
||||
_shouldSkipRender = false;
|
||||
|
@ -212,7 +187,7 @@ void Avatar::simulate(float deltaTime) {
|
|||
|
||||
// simple frustum check
|
||||
float boundingRadius = getBillboardSize();
|
||||
bool inViewFrustum = qApp->getViewFrustum()->sphereInFrustum(_position, boundingRadius) !=
|
||||
bool inViewFrustum = qApp->getViewFrustum()->sphereInFrustum(getPosition(), boundingRadius) !=
|
||||
ViewFrustum::OUTSIDE;
|
||||
|
||||
{
|
||||
|
@ -231,11 +206,11 @@ void Avatar::simulate(float deltaTime) {
|
|||
}
|
||||
{
|
||||
PerformanceTimer perfTimer("head");
|
||||
glm::vec3 headPosition = _position;
|
||||
glm::vec3 headPosition = getPosition();
|
||||
_skeletonModel.getHeadPosition(headPosition);
|
||||
Head* head = getHead();
|
||||
head->setPosition(headPosition);
|
||||
head->setScale(_scale);
|
||||
head->setScale(getAvatarScale());
|
||||
head->simulate(deltaTime, false, _shouldRenderBillboard);
|
||||
}
|
||||
}
|
||||
|
@ -268,7 +243,7 @@ bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) {
|
|||
glm::vec3 theirLookAt = dynamic_pointer_cast<Avatar>(avatar)->getHead()->getLookAtPosition();
|
||||
glm::vec3 myEyePosition = getHead()->getEyePosition();
|
||||
|
||||
return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getScale());
|
||||
return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getAvatarScale());
|
||||
}
|
||||
|
||||
void Avatar::slamPosition(const glm::vec3& newPosition) {
|
||||
|
@ -279,7 +254,7 @@ void Avatar::slamPosition(const glm::vec3& newPosition) {
|
|||
}
|
||||
|
||||
void Avatar::applyPositionDelta(const glm::vec3& delta) {
|
||||
_position += delta;
|
||||
setPosition(getPosition() + delta);
|
||||
_positionDeltaAccumulator += delta;
|
||||
}
|
||||
|
||||
|
@ -345,15 +320,10 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
|
|||
}
|
||||
|
||||
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
||||
startRender();
|
||||
if (_referential) {
|
||||
_referential->update();
|
||||
}
|
||||
|
||||
auto& batch = *renderArgs->_batch;
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__);
|
||||
|
||||
if (glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), _position) < 10.0f) {
|
||||
if (glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), getPosition()) < 10.0f) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
|
||||
|
||||
|
@ -453,7 +423,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
const float BASE_LIGHT_DISTANCE = 2.0f;
|
||||
const float LIGHT_EXPONENT = 1.0f;
|
||||
const float LIGHT_CUTOFF = glm::radians(80.0f);
|
||||
float distance = BASE_LIGHT_DISTANCE * _scale;
|
||||
float distance = BASE_LIGHT_DISTANCE * getAvatarScale();
|
||||
glm::vec3 position = glm::mix(_skeletonModel.getTranslation(), getHead()->getFaceModel().getTranslation(), 0.9f);
|
||||
glm::quat orientation = getOrientation();
|
||||
foreach (const AvatarManager::LocalLight& light, DependencyManager::get<AvatarManager>()->getLocalLights()) {
|
||||
|
@ -463,16 +433,6 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: re-implement these when we have more detailed avatar collision shapes
|
||||
bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes);
|
||||
if (renderSkeleton) {
|
||||
}
|
||||
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
|
||||
if (renderHead && shouldRenderHead(renderArgs)) {
|
||||
}
|
||||
*/
|
||||
|
||||
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
|
||||
if (renderBounding && shouldRenderHead(renderArgs) && _skeletonModel.isRenderable()) {
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__":skeletonBoundingCollisionShapes");
|
||||
|
@ -484,7 +444,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
static const float INDICATOR_OFFSET = 0.22f;
|
||||
static const float INDICATOR_RADIUS = 0.03f;
|
||||
static const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
|
||||
glm::vec3 position = glm::vec3(_position.x, getDisplayNamePosition().y + INDICATOR_OFFSET, _position.z);
|
||||
glm::vec3 avatarPosition = getPosition();
|
||||
glm::vec3 position = glm::vec3(avatarPosition.x, getDisplayNamePosition().y + INDICATOR_OFFSET, avatarPosition.z);
|
||||
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderFocusIndicator");
|
||||
Transform transform;
|
||||
transform.setTranslation(position);
|
||||
|
@ -518,7 +479,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
}
|
||||
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch,
|
||||
Transform(transform).postScale(eyeDiameter * _scale / 2.0f + RADIUS_INCREMENT),
|
||||
Transform(transform).postScale(eyeDiameter * getAvatarScale() / 2.0f + RADIUS_INCREMENT),
|
||||
glm::vec4(LOOKING_AT_ME_COLOR, alpha));
|
||||
|
||||
position = getHead()->getRightEyePosition();
|
||||
|
@ -528,7 +489,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
eyeDiameter = DEFAULT_EYE_DIAMETER;
|
||||
}
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch,
|
||||
Transform(transform).postScale(eyeDiameter * _scale / 2.0f + RADIUS_INCREMENT),
|
||||
Transform(transform).postScale(eyeDiameter * getAvatarScale() / 2.0f + RADIUS_INCREMENT),
|
||||
glm::vec4(LOOKING_AT_ME_COLOR, alpha));
|
||||
|
||||
}
|
||||
|
@ -559,7 +520,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_position);
|
||||
transform.setTranslation(getPosition());
|
||||
transform.setScale(height);
|
||||
transform.postScale(sphereRadius);
|
||||
DependencyManager::get<DeferredLightingEffect>()->renderSolidSphereInstance(batch,
|
||||
|
@ -662,9 +623,9 @@ void Avatar::simulateAttachments(float deltaTime) {
|
|||
glm::quat jointRotation;
|
||||
if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) &&
|
||||
_skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) {
|
||||
model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale);
|
||||
model->setTranslation(jointPosition + jointRotation * attachment.translation * getAvatarScale());
|
||||
model->setRotation(jointRotation * attachment.rotation);
|
||||
model->setScaleToFit(true, _scale * attachment.scale, true); // hack to force rescale
|
||||
model->setScaleToFit(true, getAvatarScale() * attachment.scale, true); // hack to force rescale
|
||||
model->setSnapModelToCenter(false); // hack to force resnap
|
||||
model->setSnapModelToCenter(true);
|
||||
model->simulate(deltaTime);
|
||||
|
@ -694,14 +655,14 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) {
|
|||
}
|
||||
// rotate about vertical to face the camera
|
||||
glm::quat rotation = getOrientation();
|
||||
glm::vec3 cameraVector = glm::inverse(rotation) * (qApp->getCamera()->getPosition() - _position);
|
||||
glm::vec3 cameraVector = glm::inverse(rotation) * (qApp->getCamera()->getPosition() - getPosition());
|
||||
rotation = rotation * glm::angleAxis(atan2f(-cameraVector.x, -cameraVector.z), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
// compute the size from the billboard camera parameters and scale
|
||||
float size = getBillboardSize();
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_position);
|
||||
transform.setTranslation(getPosition());
|
||||
transform.setRotation(rotation);
|
||||
transform.setScale(size);
|
||||
|
||||
|
@ -719,7 +680,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) {
|
|||
}
|
||||
|
||||
float Avatar::getBillboardSize() const {
|
||||
return _scale * BILLBOARD_DISTANCE * glm::tan(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
|
||||
return getAvatarScale() * BILLBOARD_DISTANCE * glm::tan(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -754,9 +715,9 @@ glm::vec3 Avatar::getDisplayNamePosition() const {
|
|||
const float HEAD_PROPORTION = 0.75f;
|
||||
float billboardSize = getBillboardSize();
|
||||
|
||||
DEBUG_VALUE("_position =", _position);
|
||||
DEBUG_VALUE("_position =", getPosition());
|
||||
DEBUG_VALUE("billboardSize =", billboardSize);
|
||||
namePosition = _position + bodyUpDirection * (billboardSize * HEAD_PROPORTION);
|
||||
namePosition = getPosition() + bodyUpDirection * (billboardSize * HEAD_PROPORTION);
|
||||
}
|
||||
|
||||
if (glm::any(glm::isnan(namePosition)) || glm::any(glm::isinf(namePosition))) {
|
||||
|
@ -868,7 +829,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co
|
|||
}
|
||||
|
||||
void Avatar::setSkeletonOffset(const glm::vec3& offset) {
|
||||
const float MAX_OFFSET_LENGTH = _scale * 0.5f;
|
||||
const float MAX_OFFSET_LENGTH = getAvatarScale() * 0.5f;
|
||||
float offsetLength = glm::length(offset);
|
||||
if (offsetLength > MAX_OFFSET_LENGTH) {
|
||||
_skeletonOffset = (MAX_OFFSET_LENGTH / offsetLength) * offset;
|
||||
|
@ -881,7 +842,7 @@ glm::vec3 Avatar::getSkeletonPosition() const {
|
|||
// The avatar is rotated PI about the yAxis, so we have to correct for it
|
||||
// to get the skeleton offset contribution in the world-frame.
|
||||
const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
return _position + getOrientation() * FLIP * _skeletonOffset;
|
||||
return getPosition() + getOrientation() * FLIP * _skeletonOffset;
|
||||
}
|
||||
|
||||
QVector<glm::quat> Avatar::getJointRotations() const {
|
||||
|
@ -960,7 +921,7 @@ glm::vec3 Avatar::getJointPosition(const QString& name) const {
|
|||
|
||||
void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const {
|
||||
//Scale a world space vector as if it was relative to the position
|
||||
positionToScale = _position + _scale * (positionToScale - _position);
|
||||
positionToScale = getPosition() + getAvatarScale() * (positionToScale - getPosition());
|
||||
}
|
||||
|
||||
void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
|
||||
|
@ -1000,7 +961,7 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
for (int i = 0; i < attachmentData.size(); i++) {
|
||||
_attachmentModels[i]->setURL(attachmentData.at(i).modelURL);
|
||||
_attachmentModels[i]->setSnapModelToCenter(true);
|
||||
_attachmentModels[i]->setScaleToFit(true, _scale * _attachmentData.at(i).scale);
|
||||
_attachmentModels[i]->setScaleToFit(true, getAvatarScale() * _attachmentData.at(i).scale);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1019,12 +980,12 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
}
|
||||
|
||||
// change in position implies movement
|
||||
glm::vec3 oldPosition = _position;
|
||||
glm::vec3 oldPosition = getPosition();
|
||||
|
||||
int bytesRead = AvatarData::parseDataFromBuffer(buffer);
|
||||
|
||||
const float MOVE_DISTANCE_THRESHOLD = 0.001f;
|
||||
_moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD;
|
||||
_moving = glm::distance(oldPosition, getPosition()) > MOVE_DISTANCE_THRESHOLD;
|
||||
if (_moving && _motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
|
||||
}
|
||||
|
@ -1088,12 +1049,12 @@ void Avatar::renderJointConnectingCone(gpu::Batch& batch, glm::vec3 position1, g
|
|||
}
|
||||
}
|
||||
|
||||
void Avatar::setScale(float scale) {
|
||||
_scale = scale;
|
||||
|
||||
if (_targetScale * (1.0f - RESCALING_TOLERANCE) < _scale &&
|
||||
_scale < _targetScale * (1.0f + RESCALING_TOLERANCE)) {
|
||||
_scale = _targetScale;
|
||||
void Avatar::setAvatarScale(float scale) {
|
||||
if (_targetScale * (1.0f - RESCALING_TOLERANCE) < scale &&
|
||||
scale < _targetScale * (1.0f + RESCALING_TOLERANCE)) {
|
||||
setScale(glm::vec3(_targetScale));
|
||||
} else {
|
||||
setScale(glm::vec3(scale));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1108,7 +1069,7 @@ float Avatar::getHeadHeight() const {
|
|||
|
||||
// HACK: We have a really odd case when fading out for some models where this value explodes
|
||||
float result = extents.maximum.y - extents.minimum.y;
|
||||
if (result >= 0.0f && result < 100.0f * _scale ) {
|
||||
if (result >= 0.0f && result < 100.0f * getAvatarScale() ) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -1116,7 +1077,7 @@ float Avatar::getHeadHeight() const {
|
|||
extents = _skeletonModel.getMeshExtents();
|
||||
glm::vec3 neckPosition;
|
||||
if (!extents.isEmpty() && extents.isValid() && _skeletonModel.getNeckPosition(neckPosition)) {
|
||||
return extents.maximum.y / 2.0f - neckPosition.y + _position.y;
|
||||
return extents.maximum.y / 2.0f - neckPosition.y + getPosition().y;
|
||||
}
|
||||
|
||||
const float DEFAULT_HEAD_HEIGHT = 0.25f;
|
||||
|
@ -1189,3 +1150,13 @@ glm::quat Avatar::getRightPalmRotation() {
|
|||
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
|
||||
return rightRotation;
|
||||
}
|
||||
|
||||
void Avatar::setPosition(const glm::vec3 position) {
|
||||
AvatarData::setPosition(position);
|
||||
updateAttitude();
|
||||
}
|
||||
|
||||
void Avatar::setOrientation(const glm::quat orientation) {
|
||||
AvatarData::setOrientation(orientation);
|
||||
updateAttitude();
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ public:
|
|||
const SkeletonModel& getSkeletonModel() const { return _skeletonModel; }
|
||||
const QVector<Model*>& getAttachmentModels() const { return _attachmentModels; }
|
||||
glm::vec3 getChestPosition() const;
|
||||
float getScale() const { return _scale; }
|
||||
float getAvatarScale() const { return getScale().y; }
|
||||
const Head* getHead() const { return static_cast<const Head*>(_headData); }
|
||||
Head* getHead() { return static_cast<Head*>(_headData); }
|
||||
Hand* getHand() { return static_cast<Hand*>(_handData); }
|
||||
|
@ -155,6 +155,9 @@ public:
|
|||
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
|
||||
AvatarMotionState* getMotionState() { return _motionState; }
|
||||
|
||||
virtual void setPosition(glm::vec3 position);
|
||||
virtual void setOrientation(glm::quat orientation);
|
||||
|
||||
public slots:
|
||||
|
||||
// FIXME - these should be migrated to use Pose data instead
|
||||
|
@ -186,7 +189,6 @@ protected:
|
|||
glm::quat _lastOrientation;
|
||||
|
||||
float _leanScale;
|
||||
float _scale;
|
||||
glm::vec3 _worldUpDirection;
|
||||
float _stringLength;
|
||||
bool _moving; ///< set when position is changing
|
||||
|
@ -198,7 +200,7 @@ protected:
|
|||
glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; }
|
||||
glm::vec3 getBodyFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
|
||||
glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const;
|
||||
void setScale(float scale);
|
||||
void setAvatarScale(float scale);
|
||||
void measureMotionDerivatives(float deltaTime);
|
||||
|
||||
float getSkeletonHeight() const;
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
#include <PerfStat.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <Rig.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <UUID.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -35,7 +37,6 @@
|
|||
#include "Menu.h"
|
||||
#include "MyAvatar.h"
|
||||
#include "SceneScriptingInterface.h"
|
||||
#include <Rig.h>
|
||||
|
||||
// 70 times per second - target is 60hz, but this helps account for any small deviations
|
||||
// in the update loop
|
||||
|
@ -75,6 +76,13 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
packetReceiver.registerListener(PacketType::AvatarBillboard, this, "processAvatarBillboardPacket");
|
||||
}
|
||||
|
||||
const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters
|
||||
Setting::Handle<float> avatarRenderDistanceInverseHighLimit("avatarRenderDistanceHighLimit", 1.0f / SMALLEST_REASONABLE_HORIZON);
|
||||
void AvatarManager::setRenderDistanceInverseHighLimit(float newValue) {
|
||||
avatarRenderDistanceInverseHighLimit.set(newValue);
|
||||
_renderDistanceController.setControlledValueHighLimit(newValue);
|
||||
}
|
||||
|
||||
void AvatarManager::init() {
|
||||
_myAvatar->init();
|
||||
{
|
||||
|
@ -93,8 +101,7 @@ void AvatarManager::init() {
|
|||
|
||||
const float target_fps = qApp->getTargetFrameRate();
|
||||
_renderDistanceController.setMeasuredValueSetpoint(target_fps);
|
||||
const float SMALLEST_REASONABLE_HORIZON = 5.0f; // meters
|
||||
_renderDistanceController.setControlledValueHighLimit(1.0f / SMALLEST_REASONABLE_HORIZON);
|
||||
_renderDistanceController.setControlledValueHighLimit(avatarRenderDistanceInverseHighLimit.get());
|
||||
_renderDistanceController.setControlledValueLowLimit(1.0f / (float) TREE_SCALE);
|
||||
// Advice for tuning parameters:
|
||||
// See PIDController.h. There's a section on tuning in the reference.
|
||||
|
@ -191,7 +198,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) {
|
|||
while (fadingIterator != _avatarFades.end()) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(*fadingIterator);
|
||||
avatar->startUpdate();
|
||||
avatar->setTargetScale(avatar->getScale() * SHRINK_RATE, true);
|
||||
avatar->setTargetScale(avatar->getAvatarScale() * SHRINK_RATE);
|
||||
if (avatar->getTargetScale() <= MIN_FADE_SCALE) {
|
||||
avatar->removeFromScene(*fadingIterator, scene, pendingChanges);
|
||||
fadingIterator = _avatarFades.erase(fadingIterator);
|
||||
|
|
|
@ -70,14 +70,16 @@ public:
|
|||
|
||||
// Expose results and parameter-tuning operations to other systems, such as stats and javascript.
|
||||
Q_INVOKABLE float getRenderDistance() { return _renderDistance; }
|
||||
Q_INVOKABLE float getRenderDistanceInverseLowLimit() { return _renderDistanceController.getControlledValueLowLimit(); }
|
||||
Q_INVOKABLE float getRenderDistanceInverseHighLimit() { return _renderDistanceController.getControlledValueHighLimit(); }
|
||||
Q_INVOKABLE int getNumberInRenderRange() { return _renderedAvatarCount; }
|
||||
Q_INVOKABLE bool getRenderDistanceControllerIsLogging() { return _renderDistanceController.getIsLogging(); }
|
||||
Q_INVOKABLE void setRenderDistanceControllerHistory(QString label, int size) { return _renderDistanceController.setHistorySize(label, size); }
|
||||
Q_INVOKABLE void setRenderDistanceKP(float newValue) { _renderDistanceController.setKP(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceKI(float newValue) { _renderDistanceController.setKI(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceKD(float newValue) { _renderDistanceController.setKD(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceHighLimit(float newValue) { _renderDistanceController.setControlledValueHighLimit(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceInverseLowLimit(float newValue) { _renderDistanceController.setControlledValueLowLimit(newValue); }
|
||||
Q_INVOKABLE void setRenderDistanceInverseHighLimit(float newValue);
|
||||
|
||||
public slots:
|
||||
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
||||
|
|
|
@ -30,7 +30,8 @@ void AvatarUpdate::synchronousProcess() {
|
|||
|
||||
// Keep our own updated value, so that our asynchronous code can consult it.
|
||||
_isHMDMode = qApp->isHMDMode();
|
||||
_headPose = qApp->getActiveDisplayPlugin()->getHeadPose();
|
||||
auto frameCount = qApp->getFrameCount();
|
||||
_headPose = qApp->getActiveDisplayPlugin()->getHeadPose(frameCount);
|
||||
|
||||
if (_updateBillboard) {
|
||||
DependencyManager::get<AvatarManager>()->getMyAvatar()->doUpdateBillboard();
|
||||
|
|
|
@ -37,7 +37,7 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
|
||||
float avatarScale = 1.0f;
|
||||
if (_owningAvatar) {
|
||||
avatarScale = _owningAvatar->getScale();
|
||||
avatarScale = _owningAvatar->getAvatarScale();
|
||||
}
|
||||
|
||||
const float alpha = 1.0f;
|
||||
|
|
|
@ -43,9 +43,11 @@ Head::Head(Avatar* owningAvatar) :
|
|||
_longTermAverageLoudness(-1.0f),
|
||||
_audioAttack(0.0f),
|
||||
_audioJawOpen(0.0f),
|
||||
_trailingAudioJawOpen(0.0f),
|
||||
_mouth2(0.0f),
|
||||
_mouth3(0.0f),
|
||||
_mouth4(0.0f),
|
||||
_mouthTime(0.0f),
|
||||
_renderLookatVectors(false),
|
||||
_renderLookatTarget(false),
|
||||
_saccade(0.0f, 0.0f, 0.0f),
|
||||
|
@ -246,6 +248,16 @@ void Head::calculateMouthShapes() {
|
|||
const float JAW_OPEN_SCALE = 0.015f;
|
||||
const float JAW_OPEN_RATE = 0.9f;
|
||||
const float JAW_CLOSE_RATE = 0.90f;
|
||||
const float TIMESTEP_CONSTANT = 0.0032f;
|
||||
const float MMMM_POWER = 0.10f;
|
||||
const float SMILE_POWER = 0.10f;
|
||||
const float FUNNEL_POWER = 0.35f;
|
||||
const float MMMM_SPEED = 2.685f;
|
||||
const float SMILE_SPEED = 1.0f;
|
||||
const float FUNNEL_SPEED = 2.335f;
|
||||
const float STOP_GAIN = 5.0f;
|
||||
|
||||
// From the change in loudness, decide how much to open or close the jaw
|
||||
float audioDelta = sqrtf(glm::max(_averageLoudness - _longTermAverageLoudness, 0.0f)) * JAW_OPEN_SCALE;
|
||||
if (audioDelta > _audioJawOpen) {
|
||||
_audioJawOpen += (audioDelta - _audioJawOpen) * JAW_OPEN_RATE;
|
||||
|
@ -253,21 +265,14 @@ void Head::calculateMouthShapes() {
|
|||
_audioJawOpen *= JAW_CLOSE_RATE;
|
||||
}
|
||||
_audioJawOpen = glm::clamp(_audioJawOpen, 0.0f, 1.0f);
|
||||
_trailingAudioJawOpen = glm::mix(_trailingAudioJawOpen, _audioJawOpen, 0.99f);
|
||||
|
||||
// _mouth2 = "mmmm" shape
|
||||
// _mouth3 = "funnel" shape
|
||||
// _mouth4 = "smile" shape
|
||||
const float FUNNEL_PERIOD = 0.985f;
|
||||
const float FUNNEL_RANDOM_PERIOD = 0.01f;
|
||||
const float MMMM_POWER = 0.25f;
|
||||
const float MMMM_PERIOD = 0.91f;
|
||||
const float MMMM_RANDOM_PERIOD = 0.15f;
|
||||
const float SMILE_PERIOD = 0.925f;
|
||||
const float SMILE_RANDOM_PERIOD = 0.05f;
|
||||
|
||||
_mouth3 = glm::mix(_audioJawOpen, _mouth3, FUNNEL_PERIOD + randFloat() * FUNNEL_RANDOM_PERIOD);
|
||||
_mouth2 = glm::mix(_audioJawOpen * MMMM_POWER, _mouth2, MMMM_PERIOD + randFloat() * MMMM_RANDOM_PERIOD);
|
||||
_mouth4 = glm::mix(_audioJawOpen, _mouth4, SMILE_PERIOD + randFloat() * SMILE_RANDOM_PERIOD);
|
||||
// Advance time at a rate proportional to loudness, and move the mouth shapes through
|
||||
// a cycle at differing speeds to create a continuous random blend of shapes.
|
||||
_mouthTime += sqrtf(_averageLoudness) * TIMESTEP_CONSTANT;
|
||||
_mouth2 = (sinf(_mouthTime * MMMM_SPEED) + 1.0f) * MMMM_POWER * glm::min(1.0f, _trailingAudioJawOpen * STOP_GAIN);
|
||||
_mouth3 = (sinf(_mouthTime * FUNNEL_SPEED) + 1.0f) * FUNNEL_POWER * glm::min(1.0f, _trailingAudioJawOpen * STOP_GAIN);
|
||||
_mouth4 = (sinf(_mouthTime * SMILE_SPEED) + 1.0f) * SMILE_POWER * glm::min(1.0f, _trailingAudioJawOpen * STOP_GAIN);
|
||||
}
|
||||
|
||||
void Head::applyEyelidOffset(glm::quat headOrientation) {
|
||||
|
|
|
@ -124,9 +124,11 @@ private:
|
|||
float _longTermAverageLoudness;
|
||||
float _audioAttack;
|
||||
float _audioJawOpen;
|
||||
float _trailingAudioJawOpen;
|
||||
float _mouth2;
|
||||
float _mouth3;
|
||||
float _mouth4;
|
||||
float _mouthTime;
|
||||
bool _renderLookatVectors;
|
||||
bool _renderLookatTarget;
|
||||
glm::vec3 _saccade;
|
||||
|
|
|
@ -1,192 +0,0 @@
|
|||
//
|
||||
// ModelReferential.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/30/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <AvatarData.h>
|
||||
#include <EntityTree.h>
|
||||
#include <Model.h>
|
||||
|
||||
#include "InterfaceLogging.h"
|
||||
#include "ModelReferential.h"
|
||||
|
||||
ModelReferential::ModelReferential(Referential* referential, EntityTreePointer tree, AvatarData* avatar) :
|
||||
Referential(MODEL, avatar),
|
||||
_tree(tree)
|
||||
{
|
||||
_translation = referential->getTranslation();
|
||||
_rotation = referential->getRotation();
|
||||
unpackExtraData(reinterpret_cast<unsigned char*>(referential->getExtraData().data()),
|
||||
referential->getExtraData().size());
|
||||
|
||||
if (!isValid()) {
|
||||
qCDebug(interfaceapp) << "ModelReferential::copyConstructor(): Not Valid";
|
||||
return;
|
||||
}
|
||||
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
if (item != NULL) {
|
||||
_lastRefDimension = item->getDimensions();
|
||||
_refRotation = item->getRotation();
|
||||
_refPosition = item->getPosition();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
ModelReferential::ModelReferential(const QUuid& entityID, EntityTreePointer tree, AvatarData* avatar) :
|
||||
Referential(MODEL, avatar),
|
||||
_entityID(entityID),
|
||||
_tree(tree)
|
||||
{
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
if (!isValid() || item == NULL) {
|
||||
qCDebug(interfaceapp) << "ModelReferential::constructor(): Not Valid";
|
||||
_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_lastRefDimension = item->getDimensions();
|
||||
_refRotation = item->getRotation();
|
||||
_refPosition = item->getPosition();
|
||||
|
||||
glm::quat refInvRot = glm::inverse(_refRotation);
|
||||
_rotation = refInvRot * _avatar->getOrientation();
|
||||
_translation = refInvRot * (avatar->getPosition() - _refPosition);
|
||||
}
|
||||
|
||||
void ModelReferential::update() {
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
if (!isValid() || item == NULL || _avatar == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool somethingChanged = false;
|
||||
if (item->getDimensions() != _lastRefDimension) {
|
||||
glm::vec3 oldDimension = _lastRefDimension;
|
||||
_lastRefDimension = item->getDimensions();
|
||||
_translation *= _lastRefDimension / oldDimension;
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (item->getRotation() != _refRotation) {
|
||||
_refRotation = item->getRotation();
|
||||
_avatar->setOrientation(_refRotation * _rotation, true);
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (item->getPosition() != _refPosition || somethingChanged) {
|
||||
_refPosition = item->getPosition();
|
||||
_avatar->setPosition(_refPosition + _refRotation * _translation, true);
|
||||
}
|
||||
}
|
||||
|
||||
int ModelReferential::packExtraData(unsigned char* destinationBuffer) const {
|
||||
QByteArray encodedEntityID = _entityID.toRfc4122();
|
||||
memcpy(destinationBuffer, encodedEntityID.constData(), encodedEntityID.size());
|
||||
return encodedEntityID.size();
|
||||
}
|
||||
|
||||
int ModelReferential::unpackExtraData(const unsigned char *sourceBuffer, int size) {
|
||||
QByteArray encodedEntityID((const char*)sourceBuffer, NUM_BYTES_RFC4122_UUID);
|
||||
_entityID = QUuid::fromRfc4122(encodedEntityID);
|
||||
return NUM_BYTES_RFC4122_UUID;
|
||||
}
|
||||
|
||||
JointReferential::JointReferential(Referential* referential, EntityTreePointer tree, AvatarData* avatar) :
|
||||
ModelReferential(referential, tree, avatar)
|
||||
{
|
||||
_type = JOINT;
|
||||
if (!isValid()) {
|
||||
qCDebug(interfaceapp) << "JointReferential::copyConstructor(): Not Valid";
|
||||
return;
|
||||
}
|
||||
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
const Model* model = getModel(item);
|
||||
if (isValid() && model != NULL && _jointIndex < (uint32_t)(model->getJointStateCount())) {
|
||||
_lastRefDimension = item->getDimensions();
|
||||
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
|
||||
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
JointReferential::JointReferential(uint32_t jointIndex, const QUuid& entityID, EntityTreePointer tree, AvatarData* avatar) :
|
||||
ModelReferential(entityID, tree, avatar),
|
||||
_jointIndex(jointIndex)
|
||||
{
|
||||
_type = JOINT;
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
const Model* model = getModel(item);
|
||||
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
|
||||
qCDebug(interfaceapp) << "JointReferential::constructor(): Not Valid";
|
||||
_isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_lastRefDimension = item->getDimensions();
|
||||
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
|
||||
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
|
||||
|
||||
glm::quat refInvRot = glm::inverse(_refRotation);
|
||||
_rotation = refInvRot * _avatar->getOrientation();
|
||||
// BUG! _refPosition is in domain units, but avatar is in meters
|
||||
_translation = refInvRot * (avatar->getPosition() - _refPosition);
|
||||
}
|
||||
|
||||
void JointReferential::update() {
|
||||
EntityItemPointer item = _tree->findEntityByID(_entityID);
|
||||
const Model* model = getModel(item);
|
||||
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool somethingChanged = false;
|
||||
if (item->getDimensions() != _lastRefDimension) {
|
||||
glm::vec3 oldDimension = _lastRefDimension;
|
||||
_lastRefDimension = item->getDimensions();
|
||||
_translation *= _lastRefDimension / oldDimension;
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (item->getRotation() != _refRotation) {
|
||||
model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
|
||||
_avatar->setOrientation(_refRotation * _rotation, true);
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (item->getPosition() != _refPosition || somethingChanged) {
|
||||
model->getJointPositionInWorldFrame(_jointIndex, _refPosition);
|
||||
_avatar->setPosition(_refPosition + _refRotation * _translation, true);
|
||||
}
|
||||
}
|
||||
|
||||
const Model* JointReferential::getModel(EntityItemPointer item) {
|
||||
EntityItemFBXService* fbxService = _tree->getFBXService();
|
||||
if (item != NULL && fbxService != NULL) {
|
||||
return fbxService->getModelForEntityItem(item);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int JointReferential::packExtraData(unsigned char* destinationBuffer) const {
|
||||
unsigned char* startPosition = destinationBuffer;
|
||||
destinationBuffer += ModelReferential::packExtraData(destinationBuffer);
|
||||
|
||||
memcpy(destinationBuffer, &_jointIndex, sizeof(_jointIndex));
|
||||
destinationBuffer += sizeof(_jointIndex);
|
||||
|
||||
return destinationBuffer - startPosition;
|
||||
}
|
||||
|
||||
int JointReferential::unpackExtraData(const unsigned char *sourceBuffer, int size) {
|
||||
const unsigned char* startPosition = sourceBuffer;
|
||||
sourceBuffer += ModelReferential::unpackExtraData(sourceBuffer, size);
|
||||
|
||||
memcpy(&_jointIndex, sourceBuffer, sizeof(_jointIndex));
|
||||
sourceBuffer += sizeof(_jointIndex);
|
||||
|
||||
return sourceBuffer - startPosition;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
//
|
||||
// ModelReferential.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/30/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_ModelReferential_h
|
||||
#define hifi_ModelReferential_h
|
||||
|
||||
#include <Referential.h>
|
||||
|
||||
class EntityTree;
|
||||
class Model;
|
||||
|
||||
class ModelReferential : public Referential {
|
||||
public:
|
||||
ModelReferential(Referential* ref, EntityTreePointer tree, AvatarData* avatar);
|
||||
ModelReferential(const QUuid& entityID, EntityTreePointer tree, AvatarData* avatar);
|
||||
virtual void update();
|
||||
|
||||
protected:
|
||||
virtual int packExtraData(unsigned char* destinationBuffer) const;
|
||||
virtual int unpackExtraData(const unsigned char* sourceBuffer, int size);
|
||||
|
||||
QUuid _entityID;
|
||||
EntityTreePointer _tree;
|
||||
};
|
||||
|
||||
class JointReferential : public ModelReferential {
|
||||
public:
|
||||
JointReferential(Referential* ref, EntityTreePointer tree, AvatarData* avatar);
|
||||
JointReferential(uint32_t jointIndex, const QUuid& entityID, EntityTreePointer tree, AvatarData* avatar);
|
||||
virtual void update();
|
||||
|
||||
protected:
|
||||
const Model* getModel(EntityItemPointer item);
|
||||
virtual int packExtraData(unsigned char* destinationBuffer) const;
|
||||
virtual int unpackExtraData(const unsigned char* sourceBuffer, int size);
|
||||
|
||||
uint32_t _jointIndex;
|
||||
};
|
||||
|
||||
#endif // hifi_ModelReferential_h
|
|
@ -45,7 +45,6 @@
|
|||
#include "AvatarManager.h"
|
||||
#include "Environment.h"
|
||||
#include "Menu.h"
|
||||
#include "ModelReferential.h"
|
||||
#include "MyAvatar.h"
|
||||
#include "Physics.h"
|
||||
#include "Util.h"
|
||||
|
@ -204,13 +203,14 @@ MyAvatar::~MyAvatar() {
|
|||
|
||||
QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
|
||||
CameraMode mode = qApp->getCamera()->getMode();
|
||||
_globalPosition = getPosition();
|
||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
||||
// fake the avatar position that is sent up to the AvatarMixer
|
||||
glm::vec3 oldPosition = _position;
|
||||
_position = getSkeletonPosition();
|
||||
glm::vec3 oldPosition = getPosition();
|
||||
setPosition(getSkeletonPosition());
|
||||
QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll);
|
||||
// copy the correct position back
|
||||
_position = oldPosition;
|
||||
setPosition(oldPosition);
|
||||
return array;
|
||||
}
|
||||
return AvatarData::toByteArray(cullSmallChanges, sendAll);
|
||||
|
@ -232,30 +232,27 @@ void MyAvatar::reset(bool andReload) {
|
|||
setThrust(glm::vec3(0.0f));
|
||||
|
||||
if (andReload) {
|
||||
// Get fresh data, in case we're really slow and out of wack.
|
||||
_hmdSensorMatrix = qApp->getHMDSensorPose();
|
||||
_hmdSensorPosition = extractTranslation(_hmdSensorMatrix);
|
||||
_hmdSensorOrientation = glm::quat_cast(_hmdSensorMatrix);
|
||||
|
||||
// Reset body position/orientation under the head.
|
||||
// derive the desired body orientation from the *old* hmd orientation, before the sensor reset.
|
||||
auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
|
||||
|
||||
// transform this body into world space
|
||||
auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix;
|
||||
glm::vec3 worldBodyPos = extractTranslation(worldBodyMatrix);
|
||||
glm::quat worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix));
|
||||
|
||||
// FIXME: Hack to retain the previous behavior wrt height.
|
||||
// I'd like to make the body match head height, but that will have to wait for separate PR.
|
||||
worldBodyPos.y = getPosition().y;
|
||||
auto worldBodyPos = extractTranslation(worldBodyMatrix);
|
||||
auto worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix));
|
||||
|
||||
// this will become our new position.
|
||||
setPosition(worldBodyPos);
|
||||
setOrientation(worldBodyRot);
|
||||
// If there is any discrepency between positioning and the head (as there is in initial deriveBodyFromHMDSensor),
|
||||
// we can make that right by setting _bodySensorMatrix = newBodySensorMatrix.
|
||||
// However, doing so will make the head want to point to the previous body orientation, as cached above.
|
||||
//_bodySensorMatrix = newBodySensorMatrix;
|
||||
//updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes
|
||||
|
||||
qApp->setRawAvatarUpdateThreading();
|
||||
// now sample the new hmd orientation AFTER sensor reset.
|
||||
updateFromHMDSensorMatrix(qApp->getHMDSensorPose());
|
||||
|
||||
// update the body in sensor space using the new hmd sensor sample
|
||||
_bodySensorMatrix = deriveBodyFromHMDSensor();
|
||||
|
||||
// rebuild the sensor to world matrix such that, the HMD will point in the desired orientation.
|
||||
// i.e. the along avatar's current position and orientation.
|
||||
updateSensorToWorldMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,10 +269,6 @@ void MyAvatar::update(float deltaTime) {
|
|||
updateSensorToWorldMatrix();
|
||||
}
|
||||
|
||||
if (_referential) {
|
||||
_referential->update();
|
||||
}
|
||||
|
||||
Head* head = getHead();
|
||||
head->relaxLean(deltaTime);
|
||||
updateFromTrackers(deltaTime);
|
||||
|
@ -294,9 +287,9 @@ extern void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avata
|
|||
void MyAvatar::simulate(float deltaTime) {
|
||||
PerformanceTimer perfTimer("simulate");
|
||||
|
||||
if (_scale != _targetScale) {
|
||||
float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale;
|
||||
setScale(scale);
|
||||
if (getAvatarScale() != _targetScale) {
|
||||
float scale = (1.0f - SMOOTHING_RATIO) * getAvatarScale() + SMOOTHING_RATIO * _targetScale;
|
||||
setAvatarScale(scale);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -346,10 +339,10 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
Head* head = getHead();
|
||||
glm::vec3 headPosition;
|
||||
if (!_skeletonModel.getHeadPosition(headPosition)) {
|
||||
headPosition = _position;
|
||||
headPosition = getPosition();
|
||||
}
|
||||
head->setPosition(headPosition);
|
||||
head->setScale(_scale);
|
||||
head->setScale(getAvatarScale());
|
||||
head->simulate(deltaTime, true);
|
||||
}
|
||||
|
||||
|
@ -571,32 +564,6 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
Avatar::render(renderArgs, cameraPosition);
|
||||
}
|
||||
|
||||
void MyAvatar::clearReferential() {
|
||||
changeReferential(NULL);
|
||||
}
|
||||
|
||||
bool MyAvatar::setModelReferential(const QUuid& id) {
|
||||
EntityTreePointer tree = qApp->getEntities()->getTree();
|
||||
changeReferential(new ModelReferential(id, tree, this));
|
||||
if (_referential->isValid()) {
|
||||
return true;
|
||||
} else {
|
||||
changeReferential(NULL);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MyAvatar::setJointReferential(const QUuid& id, int jointIndex) {
|
||||
EntityTreePointer tree = qApp->getEntities()->getTree();
|
||||
changeReferential(new JointReferential(jointIndex, id, tree, this));
|
||||
if (!_referential->isValid()) {
|
||||
return true;
|
||||
} else {
|
||||
changeReferential(NULL);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "overrideAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps),
|
||||
|
@ -735,7 +702,7 @@ void MyAvatar::loadData() {
|
|||
|
||||
_leanScale = loadSetting(settings, "leanScale", 0.05f);
|
||||
_targetScale = loadSetting(settings, "scale", 1.0f);
|
||||
setScale(_scale);
|
||||
setAvatarScale(getAvatarScale());
|
||||
|
||||
_animGraphUrl = settings.value("animGraphURL", "").toString();
|
||||
_fullAvatarURLFromPreferences = settings.value("fullAvatarURL", AvatarData::defaultFullAvatarModelUrl()).toUrl();
|
||||
|
@ -862,7 +829,8 @@ void MyAvatar::updateLookAtTargetAvatar() {
|
|||
bool isCurrentTarget = avatar->getIsLookAtTarget();
|
||||
float distanceTo = glm::length(avatar->getHead()->getEyePosition() - cameraPosition);
|
||||
avatar->setIsLookAtTarget(false);
|
||||
if (!avatar->isMyAvatar() && avatar->isInitialized() && (distanceTo < GREATEST_LOOKING_AT_DISTANCE * getScale())) {
|
||||
if (!avatar->isMyAvatar() && avatar->isInitialized() &&
|
||||
(distanceTo < GREATEST_LOOKING_AT_DISTANCE * getAvatarScale())) {
|
||||
float angleTo = glm::angle(lookForward, glm::normalize(avatar->getHead()->getEyePosition() - cameraPosition));
|
||||
if (angleTo < (smallestAngleTo * (isCurrentTarget ? KEEP_LOOKING_AT_CURRENT_ANGLE_FACTOR : 1.0f))) {
|
||||
_lookAtTargetAvatar = avatarPointer;
|
||||
|
@ -1074,7 +1042,7 @@ glm::vec3 MyAvatar::getSkeletonPosition() const {
|
|||
// The avatar is rotated PI about the yAxis, so we have to correct for it
|
||||
// to get the skeleton offset contribution in the world-frame.
|
||||
const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
return _position + getOrientation() * FLIP * _skeletonOffset;
|
||||
return getPosition() + getOrientation() * FLIP * _skeletonOffset;
|
||||
}
|
||||
return Avatar::getPosition();
|
||||
}
|
||||
|
@ -1232,7 +1200,7 @@ void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, fl
|
|||
if (qApp->isHMDMode()) {
|
||||
glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
|
||||
|
||||
glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose();
|
||||
glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose(qApp->getFrameCount());
|
||||
glm::mat4 leftEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Left);
|
||||
leftEyePose = leftEyePose * headPose;
|
||||
glm::vec3 leftEyePosition = extractTranslation(leftEyePose);
|
||||
|
@ -1286,6 +1254,16 @@ void MyAvatar::initHeadBones() {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setAnimGraphUrl(const QUrl& url) {
|
||||
if (_animGraphUrl == url) {
|
||||
return;
|
||||
}
|
||||
destroyAnimGraph();
|
||||
_skeletonModel.reset(); // Why is this necessary? Without this, we crash in the next render.
|
||||
_animGraphUrl = url;
|
||||
initAnimGraph();
|
||||
}
|
||||
|
||||
void MyAvatar::initAnimGraph() {
|
||||
// avatar.json
|
||||
// https://gist.github.com/hyperlogic/7d6a0892a7319c69e2b9
|
||||
|
@ -1302,9 +1280,9 @@ void MyAvatar::initAnimGraph() {
|
|||
// or run a local web-server
|
||||
// python -m SimpleHTTPServer&
|
||||
//auto graphUrl = QUrl("http://localhost:8000/avatar.json");
|
||||
auto graphUrl = QUrl(_animGraphUrl.isEmpty() ?
|
||||
QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full/avatar-animation.json") :
|
||||
_animGraphUrl);
|
||||
auto graphUrl =_animGraphUrl.isEmpty() ?
|
||||
QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full/avatar-animation.json") :
|
||||
QUrl(_animGraphUrl);
|
||||
_rig->initAnimGraph(graphUrl);
|
||||
|
||||
_bodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation..
|
||||
|
@ -1364,7 +1342,7 @@ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f;
|
|||
bool MyAvatar::cameraInsideHead() const {
|
||||
const Head* head = getHead();
|
||||
const glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
|
||||
return glm::length(cameraPosition - head->getEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * _scale);
|
||||
return glm::length(cameraPosition - head->getEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getAvatarScale());
|
||||
}
|
||||
|
||||
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
||||
|
@ -1494,11 +1472,11 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe
|
|||
if (isHovering) {
|
||||
// we're flying --> complex acceleration curve with high max speed
|
||||
float motorSpeed = glm::length(_keyboardMotorVelocity);
|
||||
float finalMaxMotorSpeed = _scale * MAX_KEYBOARD_MOTOR_SPEED;
|
||||
float finalMaxMotorSpeed = getAvatarScale() * MAX_KEYBOARD_MOTOR_SPEED;
|
||||
float speedGrowthTimescale = 2.0f;
|
||||
float speedIncreaseFactor = 1.8f;
|
||||
motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor;
|
||||
const float maxBoostSpeed = _scale * MAX_BOOST_SPEED;
|
||||
const float maxBoostSpeed = getAvatarScale() * MAX_BOOST_SPEED;
|
||||
if (motorSpeed < maxBoostSpeed) {
|
||||
// an active keyboard motor should never be slower than this
|
||||
float boostCoefficient = (maxBoostSpeed - motorSpeed) / maxBoostSpeed;
|
||||
|
@ -1808,7 +1786,10 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const {
|
|||
int neckIndex = _rig->indexOfJoint("Neck");
|
||||
int hipsIndex = _rig->indexOfJoint("Hips");
|
||||
|
||||
glm::vec3 rigMiddleEyePos = leftEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(leftEyeIndex).trans : DEFAULT_RIG_MIDDLE_EYE_POS;
|
||||
glm::vec3 rigMiddleEyePos = DEFAULT_RIG_MIDDLE_EYE_POS;
|
||||
if (leftEyeIndex >= 0 && rightEyeIndex >= 0) {
|
||||
rigMiddleEyePos = (_rig->getAbsoluteDefaultPose(leftEyeIndex).trans + _rig->getAbsoluteDefaultPose(rightEyeIndex).trans) / 2.0f;
|
||||
}
|
||||
glm::vec3 rigNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans : DEFAULT_RIG_NECK_POS;
|
||||
glm::vec3 rigHipsPos = hipsIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans : DEFAULT_RIG_HIPS_POS;
|
||||
|
||||
|
|
|
@ -247,20 +247,16 @@ public slots:
|
|||
|
||||
Q_INVOKABLE void updateMotionBehaviorFromMenu();
|
||||
|
||||
void clearReferential();
|
||||
bool setModelReferential(const QUuid& id);
|
||||
bool setJointReferential(const QUuid& id, int jointIndex);
|
||||
|
||||
virtual void rebuildSkeletonBody() override;
|
||||
|
||||
const QString& getAnimGraphUrl() const { return _animGraphUrl; }
|
||||
Q_INVOKABLE QUrl getAnimGraphUrl() const { return _animGraphUrl; }
|
||||
|
||||
void setEnableDebugDrawDefaultPose(bool isEnabled);
|
||||
void setEnableDebugDrawAnimPose(bool isEnabled);
|
||||
void setEnableDebugDrawPosition(bool isEnabled);
|
||||
bool getEnableMeshVisible() const { return _skeletonModel.isVisible(); }
|
||||
void setEnableMeshVisible(bool isEnabled);
|
||||
void setAnimGraphUrl(const QString& url) { _animGraphUrl = url; }
|
||||
Q_INVOKABLE void setAnimGraphUrl(const QUrl& url);
|
||||
|
||||
glm::vec3 getPositionForAudio();
|
||||
glm::quat getOrientationForAudio();
|
||||
|
@ -361,7 +357,7 @@ private:
|
|||
// Avatar Preferences
|
||||
QUrl _fullAvatarURLFromPreferences;
|
||||
QString _fullAvatarModelName;
|
||||
QString _animGraphUrl {""};
|
||||
QUrl _animGraphUrl {""};
|
||||
|
||||
// cache of the current HMD sensor position and orientation
|
||||
// in sensor space.
|
||||
|
|
|
@ -42,7 +42,6 @@ SkeletonModel::~SkeletonModel() {
|
|||
|
||||
void SkeletonModel::initJointStates() {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::mat4 geometryOffset = geometry.offset;
|
||||
glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset);
|
||||
_rig->initJointStates(geometry, modelOffset);
|
||||
|
||||
|
@ -237,11 +236,6 @@ void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) {
|
|||
if (parentJointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the palm's position must be transformed into the model-frame
|
||||
glm::quat inverseRotation = glm::inverse(_rotation);
|
||||
glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation);
|
||||
glm::quat palmRotation = inverseRotation * palm.getRotation();
|
||||
}
|
||||
|
||||
bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const {
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
//
|
||||
// MIDIManager.cpp
|
||||
//
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-06-30.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "InterfaceLogging.h"
|
||||
#include "MIDIManager.h"
|
||||
|
||||
MIDIManager& MIDIManager::getInstance() {
|
||||
static MIDIManager sharedInstance;
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
void MIDIManager::midiCallback(double deltaTime, std::vector<unsigned char>* message, void* userData) {
|
||||
#ifdef HAVE_RTMIDI
|
||||
|
||||
MIDIEvent callbackEvent;
|
||||
callbackEvent.deltaTime = deltaTime;
|
||||
|
||||
callbackEvent.type = message->at(0);
|
||||
|
||||
if (message->size() > 1) {
|
||||
callbackEvent.data1 = message->at(1);
|
||||
}
|
||||
|
||||
if (message->size() > 2) {
|
||||
callbackEvent.data2 = message->at(2);
|
||||
}
|
||||
|
||||
emit getInstance().midiEvent(callbackEvent);
|
||||
#endif
|
||||
}
|
||||
|
||||
MIDIManager::~MIDIManager() {
|
||||
#ifdef HAVE_RTMIDI
|
||||
delete _midiInput;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_RTMIDI
|
||||
const int DEFAULT_MIDI_PORT = 0;
|
||||
#endif
|
||||
|
||||
void MIDIManager::openDefaultPort() {
|
||||
#ifdef HAVE_RTMIDI
|
||||
if (!_midiInput) {
|
||||
_midiInput = new RtMidiIn();
|
||||
|
||||
if (_midiInput->getPortCount() > 0) {
|
||||
qCDebug(interfaceapp) << "MIDIManager opening port" << DEFAULT_MIDI_PORT;
|
||||
|
||||
_midiInput->openPort(DEFAULT_MIDI_PORT);
|
||||
|
||||
// don't ignore sysex, timing, or active sensing messages
|
||||
_midiInput->ignoreTypes(false, false, false);
|
||||
|
||||
_midiInput->setCallback(&MIDIManager::midiCallback);
|
||||
} else {
|
||||
qCDebug(interfaceapp) << "MIDIManager openDefaultPort called but there are no ports available.";
|
||||
delete _midiInput;
|
||||
_midiInput = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
//
|
||||
// MIDIManager.h
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Stephen Birarda on 2014-06-30.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_MIDIManager_h
|
||||
#define hifi_MIDIManager_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <MIDIEvent.h>
|
||||
|
||||
#ifdef HAVE_RTMIDI
|
||||
#include <RtMidi.h>
|
||||
#endif
|
||||
|
||||
class MIDIManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(unsigned int NoteOn READ NoteOn)
|
||||
Q_PROPERTY(unsigned int NoteOff READ NoteOff)
|
||||
Q_PROPERTY(unsigned int ModWheel READ ModWheel)
|
||||
Q_PROPERTY(unsigned int PitchWheel READ PitchWheel)
|
||||
|
||||
public:
|
||||
static MIDIManager& getInstance();
|
||||
static void midiCallback(double deltaTime, std::vector<unsigned char>* message, void* userData);
|
||||
|
||||
~MIDIManager();
|
||||
|
||||
void openDefaultPort();
|
||||
#ifdef HAVE_RTMIDI
|
||||
bool hasDevice() const { return !!_midiInput; }
|
||||
#endif
|
||||
public slots:
|
||||
unsigned int NoteOn() const { return 144; }
|
||||
unsigned int NoteOff() const { return 128; }
|
||||
unsigned int ModWheel() const { return 176; }
|
||||
unsigned int PitchWheel() const { return 224; }
|
||||
signals:
|
||||
void midiEvent(const MIDIEvent& event);
|
||||
|
||||
private:
|
||||
#ifdef HAVE_RTMIDI
|
||||
RtMidiIn* _midiInput;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_MIDIManager_h
|
||||
|
|
@ -1,259 +0,0 @@
|
|||
//
|
||||
// RealSense.cpp
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Thijs Wenker on 12/10/14
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "Application.h"
|
||||
#include "RealSense.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Menu.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
#ifdef HAVE_RSSDK
|
||||
const int PALMROOT_NUM_JOINTS = 3;
|
||||
const int FINGER_NUM_JOINTS = 4;
|
||||
#endif // HAVE_RSSDK
|
||||
|
||||
const DeviceTracker::Name RealSense::NAME = "RealSense";
|
||||
|
||||
// find the index of a joint from
|
||||
// the side: true = right
|
||||
// the finger & the bone:
|
||||
// finger in [0..4] : bone in [0..3] a finger phalange
|
||||
// [-1] up the hand branch : bone in [0..1] <=> [ hand, forearm]
|
||||
MotionTracker::Index evalRealSenseJointIndex(bool isRightSide, int finger, int bone) {
|
||||
#ifdef HAVE_RSSDK
|
||||
MotionTracker::Index offset = 1 // start after root
|
||||
+ (int(isRightSide) * PXCHandData::NUMBER_OF_JOINTS) // then offset for side
|
||||
+ PALMROOT_NUM_JOINTS; // then add the arm/forearm/hand chain
|
||||
if (finger >= 0) {
|
||||
// from there go down in the correct finger and bone
|
||||
return offset + (finger * FINGER_NUM_JOINTS) + bone;
|
||||
} else {
|
||||
// or go back up for the correct root bone
|
||||
return offset - 1 - bone;
|
||||
}
|
||||
#else
|
||||
return -1;
|
||||
#endif // HAVE_RSSDK
|
||||
}
|
||||
|
||||
// static
|
||||
void RealSense::init() {
|
||||
DeviceTracker* device = DeviceTracker::getDevice(NAME);
|
||||
if (!device) {
|
||||
// create a new RealSense and register it
|
||||
RealSense* realSense = new RealSense();
|
||||
DeviceTracker::registerDevice(NAME, realSense);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void RealSense::destroy() {
|
||||
DeviceTracker::destroyDevice(NAME);
|
||||
}
|
||||
|
||||
// static
|
||||
RealSense* RealSense::getInstance() {
|
||||
DeviceTracker* device = DeviceTracker::getDevice(NAME);
|
||||
if (!device) {
|
||||
// create a new RealSense and register it
|
||||
RealSense* realSense = new RealSense();
|
||||
DeviceTracker::registerDevice(NAME, realSense);
|
||||
}
|
||||
return dynamic_cast< RealSense* > (device);
|
||||
}
|
||||
|
||||
RealSense::RealSense() :
|
||||
MotionTracker(),
|
||||
_active(false)
|
||||
{
|
||||
#ifdef HAVE_RSSDK
|
||||
_handData = NULL;
|
||||
_session = PXCSession_Create();
|
||||
initSession(false, NULL);
|
||||
|
||||
// Create the RealSense joint hierarchy
|
||||
std::vector< Semantic > sides;
|
||||
sides.push_back("joint_L_");
|
||||
sides.push_back("joint_R_");
|
||||
|
||||
std::vector< Semantic > rootBones;
|
||||
rootBones.push_back("wrist");
|
||||
rootBones.push_back("hand");
|
||||
|
||||
std::vector< Semantic > fingers;
|
||||
fingers.push_back("thumb");
|
||||
fingers.push_back("index");
|
||||
fingers.push_back("middle");
|
||||
fingers.push_back("ring");
|
||||
fingers.push_back("pinky");
|
||||
|
||||
std::vector< Semantic > fingerBones;
|
||||
fingerBones.push_back("1");
|
||||
fingerBones.push_back("2");
|
||||
fingerBones.push_back("3");
|
||||
fingerBones.push_back("4");
|
||||
|
||||
std::vector< Index > palms;
|
||||
for (unsigned int s = 0; s < sides.size(); s++) {
|
||||
Index rootJoint = 0;
|
||||
for (unsigned int rb = 0; rb < rootBones.size(); rb++) {
|
||||
rootJoint = addJoint(sides[s] + rootBones[rb], rootJoint);
|
||||
}
|
||||
|
||||
// capture the hand index for debug
|
||||
palms.push_back(rootJoint);
|
||||
|
||||
for (unsigned int f = 0; f < fingers.size(); f++) {
|
||||
for (unsigned int b = 0; b < fingerBones.size(); b++) {
|
||||
rootJoint = addJoint(sides[s] + fingers[f] + fingerBones[b], rootJoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // HAVE_RSSDK
|
||||
}
|
||||
|
||||
RealSense::~RealSense() {
|
||||
#ifdef HAVE_RSSDK
|
||||
_manager->Release();
|
||||
#endif // HAVE_RSSDK
|
||||
}
|
||||
|
||||
void RealSense::initSession(bool playback, QString filename) {
|
||||
#ifdef HAVE_RSSDK
|
||||
_active = false;
|
||||
_properlyInitialized = false;
|
||||
if (_handData != NULL) {
|
||||
_handData->Release();
|
||||
_handController->Release();
|
||||
_session->Release();
|
||||
_config->Release();
|
||||
}
|
||||
_manager = _session->CreateSenseManager();
|
||||
if (playback) {
|
||||
_manager->QueryCaptureManager()->SetFileName(filename.toStdWString().c_str(), false);
|
||||
}
|
||||
_manager->QueryCaptureManager()->SetRealtime(!playback);
|
||||
_manager->EnableHand(0);
|
||||
_handController = _manager->QueryHand();
|
||||
|
||||
if (_manager->Init() == PXC_STATUS_NO_ERROR) {
|
||||
_handData = _handController->CreateOutput();
|
||||
|
||||
PXCCapture::Device *device = _manager->QueryCaptureManager()->QueryDevice();
|
||||
PXCCapture::DeviceInfo dinfo;
|
||||
_manager->QueryCaptureManager()->QueryDevice()->QueryDeviceInfo(&dinfo);
|
||||
if (dinfo.model == PXCCapture::DEVICE_MODEL_IVCAM)
|
||||
{
|
||||
device->SetDepthConfidenceThreshold(1);
|
||||
device->SetMirrorMode(PXCCapture::Device::MIRROR_MODE_DISABLED);
|
||||
device->SetIVCAMFilterOption(6);
|
||||
}
|
||||
_properlyInitialized = true;
|
||||
}
|
||||
|
||||
_config = _handController->CreateActiveConfiguration();
|
||||
_config->EnableStabilizer(true);
|
||||
_config->SetTrackingMode(PXCHandData::TRACKING_MODE_FULL_HAND);
|
||||
_config->ApplyChanges();
|
||||
#endif // HAVE_RSSDK
|
||||
}
|
||||
|
||||
#ifdef HAVE_RSSDK
|
||||
glm::quat quatFromPXCPoint4DF32(const PXCPoint4DF32& basis) {
|
||||
return glm::normalize(glm::quat(basis.w, basis.x, basis.y, basis.z) * glm::quat(glm::vec3(0, M_PI, 0)));
|
||||
}
|
||||
|
||||
glm::vec3 vec3FromPXCPoint3DF32(const PXCPoint3DF32& vec) {
|
||||
return glm::vec3(-vec.x, vec.y, -vec.z);
|
||||
}
|
||||
#endif // HAVE_RSSDK
|
||||
|
||||
void RealSense::update() {
|
||||
#ifdef HAVE_RSSDK
|
||||
bool wasActive = _active;
|
||||
_active = _manager->IsConnected() && _properlyInitialized;
|
||||
if (_active || wasActive) {
|
||||
// Go through all the joints and increment their counter since last update.
|
||||
// Increment all counters once after controller first becomes inactive so that each joint reports itself as inactive.
|
||||
// TODO C++11 for (auto jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) {
|
||||
for (JointTracker::Vector::iterator jointIt = _jointsArray.begin(); jointIt != _jointsArray.end(); jointIt++) {
|
||||
(*jointIt).tickNewFrame();
|
||||
}
|
||||
}
|
||||
|
||||
if (!_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxcStatus sts = _manager->AcquireFrame(true);
|
||||
_handData->Update();
|
||||
PXCHandData::JointData nodes[2][PXCHandData::NUMBER_OF_JOINTS] = {};
|
||||
PXCHandData::ExtremityData extremitiesPointsNodes[2][PXCHandData::NUMBER_OF_EXTREMITIES] = {};
|
||||
for (pxcI32 i = 0; i < _handData->QueryNumberOfHands(); i++) {
|
||||
PXCHandData::IHand* handData;
|
||||
if (_handData->QueryHandData(PXCHandData::ACCESS_ORDER_BY_TIME, i, handData) == PXC_STATUS_NO_ERROR) {
|
||||
int rightSide = handData->QueryBodySide() == PXCHandData::BODY_SIDE_RIGHT;
|
||||
PXCHandData::JointData jointData;
|
||||
JointTracker* parentJointTracker = _jointsArray.data();
|
||||
//Iterate Joints
|
||||
int rootBranchIndex = -1;
|
||||
JointTracker* palmJoint = NULL;
|
||||
for (int j = 0; j < PXCHandData::NUMBER_OF_JOINTS; j++) {
|
||||
handData->QueryTrackedJoint((PXCHandData::JointType)j, jointData);
|
||||
nodes[i][j] = jointData;
|
||||
if (j == PXCHandData::JOINT_WRIST) {
|
||||
JointTracker* wrist = editJointTracker(evalRealSenseJointIndex(rightSide, rootBranchIndex, 1)); // 1 is the index of the wrist joint
|
||||
wrist->editAbsFrame().setTranslation(vec3FromPXCPoint3DF32(jointData.positionWorld));
|
||||
wrist->editAbsFrame().setRotation(quatFromPXCPoint4DF32(jointData.globalOrientation));
|
||||
wrist->updateLocFromAbsTransform(parentJointTracker);
|
||||
wrist->activeFrame();
|
||||
parentJointTracker = wrist;
|
||||
continue;
|
||||
} else if (j == PXCHandData::JOINT_CENTER) {
|
||||
palmJoint = editJointTracker(evalRealSenseJointIndex(rightSide, rootBranchIndex, 0)); // 0 is the index of the palm joint
|
||||
palmJoint->editAbsFrame().setTranslation(vec3FromPXCPoint3DF32(jointData.positionWorld));
|
||||
palmJoint->editAbsFrame().setRotation(quatFromPXCPoint4DF32(jointData.globalOrientation));
|
||||
palmJoint->updateLocFromAbsTransform(parentJointTracker);
|
||||
palmJoint->activeFrame();
|
||||
parentJointTracker = palmJoint;
|
||||
continue;
|
||||
}
|
||||
int finger_index = j - PALMROOT_NUM_JOINTS;
|
||||
int finger = finger_index / FINGER_NUM_JOINTS;
|
||||
int finger_bone = finger_index % FINGER_NUM_JOINTS;
|
||||
JointTracker* ljointTracker = editJointTracker(evalRealSenseJointIndex(rightSide, finger, finger_bone));
|
||||
if (jointData.confidence > 0) {
|
||||
ljointTracker->editAbsFrame().setTranslation(vec3FromPXCPoint3DF32(jointData.positionWorld));
|
||||
ljointTracker->editAbsFrame().setRotation(quatFromPXCPoint4DF32(jointData.globalOrientation));
|
||||
ljointTracker->updateLocFromAbsTransform(parentJointTracker);
|
||||
ljointTracker->activeFrame();
|
||||
}
|
||||
if (finger_bone == (FINGER_NUM_JOINTS - 1)) {
|
||||
parentJointTracker = palmJoint;
|
||||
continue;
|
||||
}
|
||||
parentJointTracker = ljointTracker;
|
||||
}
|
||||
}
|
||||
}
|
||||
_manager->ReleaseFrame();
|
||||
#endif // HAVE_RSSDK
|
||||
}
|
||||
|
||||
void RealSense::loadRSSDKFile() {
|
||||
QString locationDir(QStandardPaths::displayName(QStandardPaths::DesktopLocation));
|
||||
QString fileNameString = QFileDialog::getOpenFileName(qApp->getWindow(), tr("Open RSSDK clip"),
|
||||
locationDir,
|
||||
tr("RSSDK Recordings (*.rssdk)"));
|
||||
if (!fileNameString.isEmpty()) {
|
||||
initSession(true, fileNameString);
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
//
|
||||
// RealSense.h
|
||||
// interface/src/devices
|
||||
//
|
||||
// Created by Thijs Wenker on 12/10/14
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_RealSense_h
|
||||
#define hifi_RealSense_h
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "MotionTracker.h"
|
||||
|
||||
#ifdef HAVE_RSSDK
|
||||
#include <pxcsession.h>
|
||||
#include <pxchandmodule.h>
|
||||
#include <pxchandconfiguration.h>
|
||||
#include <pxcsensemanager.h>
|
||||
#include <pxchanddata.h>
|
||||
#endif
|
||||
|
||||
/// Handles interaction with the RealSense skeleton tracking suit.
|
||||
class RealSense : public QObject, public MotionTracker {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static const Name NAME;
|
||||
|
||||
static void init();
|
||||
static void destroy();
|
||||
|
||||
/// RealSense MotionTracker factory
|
||||
static RealSense* getInstance();
|
||||
|
||||
bool isActive() const { return _active; }
|
||||
|
||||
virtual void update();
|
||||
|
||||
public slots:
|
||||
void loadRSSDKFile();
|
||||
|
||||
protected:
|
||||
RealSense();
|
||||
virtual ~RealSense();
|
||||
|
||||
void initSession(bool playback, QString filename);
|
||||
|
||||
private:
|
||||
#ifdef HAVE_RSSDK
|
||||
PXCSession* _session;
|
||||
PXCSenseManager* _manager;
|
||||
PXCHandModule* _handController;
|
||||
PXCHandData* _handData;
|
||||
PXCHandConfiguration* _config;
|
||||
#endif
|
||||
bool _properlyInitialized;
|
||||
bool _active;
|
||||
};
|
||||
|
||||
#endif // hifi_RealSense_h
|
|
@ -84,13 +84,12 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const {
|
|||
return qApp->getUiSize();
|
||||
}
|
||||
|
||||
controller::InputController::Pointer ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
|
||||
// This is where we retreive the Device Tracker category and then the sub tracker within it
|
||||
controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
|
||||
// This is where we retrieve the Device Tracker category and then the sub tracker within it
|
||||
auto icIt = _inputControllers.find(0);
|
||||
if (icIt != _inputControllers.end()) {
|
||||
return (*icIt).second;
|
||||
}
|
||||
|
||||
return (*icIt).second.get();
|
||||
}
|
||||
|
||||
// Look for device
|
||||
DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
|
||||
|
@ -110,18 +109,24 @@ controller::InputController::Pointer ControllerScriptingInterface::createInputCo
|
|||
controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this);
|
||||
controller::InputController::Key key = inputController->getKey();
|
||||
_inputControllers.insert(InputControllerMap::value_type(key, inputController));
|
||||
return inputController;
|
||||
return inputController.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return controller::InputController::Pointer();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::releaseInputController(controller::InputController::Pointer input) {
|
||||
void ControllerScriptingInterface::releaseInputController(controller::InputController* input) {
|
||||
_inputControllers.erase(input->getKey());
|
||||
}
|
||||
|
||||
void ControllerScriptingInterface::updateInputControllers() {
|
||||
for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
|
||||
(*it).second->update();
|
||||
}
|
||||
}
|
||||
|
||||
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
|
||||
_deviceTrackerId(deviceTrackerId),
|
||||
_subTrackerId(subTrackerId),
|
||||
|
|
|
@ -85,6 +85,8 @@ public:
|
|||
bool isKeyCaptured(const KeyEvent& event) const;
|
||||
bool isJoystickCaptured(int joystickIndex) const;
|
||||
|
||||
void updateInputControllers();
|
||||
|
||||
public slots:
|
||||
|
||||
virtual void captureKeyEvents(const KeyEvent& event);
|
||||
|
@ -96,8 +98,8 @@ public slots:
|
|||
virtual glm::vec2 getViewportDimensions() const;
|
||||
|
||||
/// Factory to create an InputController
|
||||
virtual controller::InputController::Pointer createInputController(const QString& deviceName, const QString& tracker);
|
||||
virtual void releaseInputController(controller::InputController::Pointer input);
|
||||
virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker);
|
||||
virtual void releaseInputController(controller::InputController* input);
|
||||
|
||||
signals:
|
||||
void keyPressEvent(const KeyEvent& event);
|
||||
|
|
|
@ -287,7 +287,7 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int
|
|||
mat4 camMat;
|
||||
_cameraBaseTransform.getMatrix(camMat);
|
||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||
auto headPose = displayPlugin->getHeadPose();
|
||||
auto headPose = displayPlugin->getHeadPose(qApp->getFrameCount());
|
||||
auto eyeToHead = displayPlugin->getEyeToHeadTransform((Eye)eye);
|
||||
camMat = (headPose * eyeToHead) * camMat;
|
||||
batch.setViewportTransform(renderArgs->_viewport);
|
||||
|
@ -416,7 +416,7 @@ bool ApplicationCompositor::calculateRayUICollisionPoint(const glm::vec3& positi
|
|||
glm::vec3 relativeDirection = glm::normalize(inverseOrientation * direction);
|
||||
|
||||
float t;
|
||||
if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getScale(), &t)){
|
||||
if (raySphereIntersect(relativeDirection, relativePosition, _oculusUIRadius * myAvatar->getAvatarScale(), &t)){
|
||||
result = position + direction * t;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -188,9 +188,9 @@ void PreferencesDialog::loadPreferences() {
|
|||
ui.fieldOfViewSpin->setValue(qApp->getFieldOfView());
|
||||
|
||||
ui.leanScaleSpin->setValue(myAvatar->getLeanScale());
|
||||
|
||||
ui.avatarScaleSpin->setValue(myAvatar->getScale());
|
||||
ui.avatarAnimationEdit->setText(myAvatar->getAnimGraphUrl());
|
||||
|
||||
ui.avatarScaleSpin->setValue(myAvatar->getAvatarScale());
|
||||
ui.avatarAnimationEdit->setText(myAvatar->getAnimGraphUrl().toString());
|
||||
|
||||
ui.maxOctreePPSSpin->setValue(qApp->getMaxOctreePacketsPerSecond());
|
||||
|
||||
|
@ -204,6 +204,7 @@ void PreferencesDialog::loadPreferences() {
|
|||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
ui.desktopMinimumFPSSpin->setValue(lodManager->getDesktopLODDecreaseFPS());
|
||||
ui.hmdMinimumFPSSpin->setValue(lodManager->getHMDLODDecreaseFPS());
|
||||
ui.avatarRenderSmallestReasonableHorizon->setValue(1.0f / DependencyManager::get<AvatarManager>()->getRenderDistanceInverseHighLimit());
|
||||
}
|
||||
|
||||
void PreferencesDialog::savePreferences() {
|
||||
|
@ -294,4 +295,5 @@ void PreferencesDialog::savePreferences() {
|
|||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
lodManager->setDesktopLODDecreaseFPS(ui.desktopMinimumFPSSpin->value());
|
||||
lodManager->setHMDLODDecreaseFPS(ui.hmdMinimumFPSSpin->value());
|
||||
DependencyManager::get<AvatarManager>()->setRenderDistanceInverseHighLimit(1.0f / ui.avatarRenderSmallestReasonableHorizon->value());
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <LODManager.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <PerfStat.h>
|
||||
#include <plugins/DisplayPlugin.h>
|
||||
|
||||
#include "BandwidthRecorder.h"
|
||||
#include "Menu.h"
|
||||
|
@ -118,7 +119,12 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(avatarRenderableCount, avatarManager->getNumberInRenderRange());
|
||||
STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating
|
||||
STAT_UPDATE(serverCount, nodeList->size());
|
||||
STAT_UPDATE(framerate, (int)qApp->getFps());
|
||||
STAT_UPDATE(renderrate, (int)qApp->getFps());
|
||||
if (qApp->getActiveDisplayPlugin()) {
|
||||
STAT_UPDATE(presentrate, (int)qApp->getActiveDisplayPlugin()->presentRate());
|
||||
} else {
|
||||
STAT_UPDATE(presentrate, -1);
|
||||
}
|
||||
STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond());
|
||||
STAT_UPDATE(avatarSimrate, (int)qApp->getAvatarSimrate());
|
||||
|
||||
|
|
|
@ -32,7 +32,8 @@ class Stats : public QQuickItem {
|
|||
Q_PROPERTY(float audioPacketlossDownstream READ getAudioPacketLossDownstream)
|
||||
|
||||
STATS_PROPERTY(int, serverCount, 0)
|
||||
STATS_PROPERTY(int, framerate, 0)
|
||||
STATS_PROPERTY(int, renderrate, 0)
|
||||
STATS_PROPERTY(int, presentrate, 0)
|
||||
STATS_PROPERTY(int, simrate, 0)
|
||||
STATS_PROPERTY(int, avatarSimrate, 0)
|
||||
STATS_PROPERTY(int, avatarCount, 0)
|
||||
|
@ -115,7 +116,8 @@ signals:
|
|||
void expandedChanged();
|
||||
void timingExpandedChanged();
|
||||
void serverCountChanged();
|
||||
void framerateChanged();
|
||||
void renderrateChanged();
|
||||
void presentrateChanged();
|
||||
void simrateChanged();
|
||||
void avatarSimrateChanged();
|
||||
void avatarCountChanged();
|
||||
|
|
|
@ -533,15 +533,13 @@ bool Overlays::isLoaded(unsigned int id) {
|
|||
QSizeF Overlays::textSize(unsigned int id, const QString& text) const {
|
||||
Overlay::Pointer thisOverlay = _overlaysHUD[id];
|
||||
if (thisOverlay) {
|
||||
if (typeid(*thisOverlay) == typeid(TextOverlay)) {
|
||||
return std::dynamic_pointer_cast<TextOverlay>(thisOverlay)->textSize(text);
|
||||
if (auto textOverlay = std::dynamic_pointer_cast<TextOverlay>(thisOverlay)) {
|
||||
return textOverlay->textSize(text);
|
||||
}
|
||||
} else {
|
||||
thisOverlay = _overlaysWorld[id];
|
||||
if (thisOverlay) {
|
||||
if (typeid(*thisOverlay) == typeid(Text3DOverlay)) {
|
||||
return std::dynamic_pointer_cast<Text3DOverlay>(thisOverlay)->textSize(text);
|
||||
}
|
||||
if (auto text3dOverlay = std::dynamic_pointer_cast<Text3DOverlay>(thisOverlay)) {
|
||||
return text3dOverlay->textSize(text);
|
||||
}
|
||||
}
|
||||
return QSizeF(0.0f, 0.0f);
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace render {
|
|||
glm::vec3 myAvatarPosition = avatar->getPosition();
|
||||
float angle = glm::degrees(glm::angle(myAvatarRotation));
|
||||
glm::vec3 axis = glm::axis(myAvatarRotation);
|
||||
float myAvatarScale = avatar->getScale();
|
||||
float myAvatarScale = avatar->getAvatarScale();
|
||||
Transform transform = Transform();
|
||||
transform.setTranslation(myAvatarPosition);
|
||||
transform.setRotation(glm::angleAxis(angle, axis));
|
||||
|
|
|
@ -912,6 +912,85 @@
|
|||
</layout>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_111yz">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9yz">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Minimum Avatar Display Distance</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_111yz">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="avatarRenderSmallestReasonableHorizon">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>95</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>32768</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<spacer name="verticalSpacer_8">
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "AnimPose.h"
|
||||
#include <GLMHelpers.h>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
|
||||
glm::quat(),
|
||||
|
@ -18,7 +19,9 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
|
|||
|
||||
AnimPose::AnimPose(const glm::mat4& mat) {
|
||||
scale = extractScale(mat);
|
||||
rot = glmExtractRotation(mat);
|
||||
// quat_cast doesn't work so well with scaled matrices, so cancel it out.
|
||||
glm::mat4 tmp = glm::scale(mat, 1.0f / scale);
|
||||
rot = glm::normalize(glm::quat_cast(tmp));
|
||||
trans = extractTranslation(mat);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,6 @@ link_hifi_libraries(audio)
|
|||
target_include_directories(${TARGET_NAME} PUBLIC "${HIFI_LIBRARY_DIR}/audio/src")
|
||||
|
||||
# have CMake grab externals for us
|
||||
add_dependency_external_projects(gverb)
|
||||
find_package(Gverb REQUIRED)
|
||||
target_link_libraries(${TARGET_NAME} ${GVERB_LIBRARIES})
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${GVERB_INCLUDE_DIRS})
|
||||
|
||||
if (APPLE)
|
||||
find_library(CoreAudio CoreAudio)
|
||||
find_library(CoreFoundation CoreFoundation)
|
||||
|
|
|
@ -33,29 +33,6 @@
|
|||
#include <QtMultimedia/QAudioInput>
|
||||
#include <QtMultimedia/QAudioOutput>
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4273 4305)
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
#include <gverb/gverb.h>
|
||||
#include <gverb/gverbdsp.h>
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <NodeList.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <PositionalAudioStream.h>
|
||||
|
@ -120,7 +97,6 @@ AudioClient::AudioClient() :
|
|||
_audioSourceInjectEnabled(false),
|
||||
_reverb(false),
|
||||
_reverbOptions(&_scriptReverbOptions),
|
||||
_gverb(NULL),
|
||||
_inputToNetworkResampler(NULL),
|
||||
_networkToOutputResampler(NULL),
|
||||
_loopbackResampler(NULL),
|
||||
|
@ -145,9 +121,7 @@ AudioClient::AudioClient() :
|
|||
connect(updateTimer, &QTimer::timeout, this, &AudioClient::checkDevices);
|
||||
updateTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
|
||||
|
||||
// create GVerb filter
|
||||
_gverb = createGverbFilter();
|
||||
configureGverbFilter(_gverb);
|
||||
configureReverb();
|
||||
|
||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||
packetReceiver.registerListener(PacketType::AudioStreamStats, &_stats, "processStreamStatsPacket");
|
||||
|
@ -160,10 +134,6 @@ AudioClient::AudioClient() :
|
|||
|
||||
AudioClient::~AudioClient() {
|
||||
stop();
|
||||
|
||||
if (_gverb) {
|
||||
gverb_free(_gverb);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::reset() {
|
||||
|
@ -173,8 +143,8 @@ void AudioClient::reset() {
|
|||
_toneSource.reset();
|
||||
_sourceGain.reset();
|
||||
_inputGain.reset();
|
||||
|
||||
gverb_flush(_gverb);
|
||||
_sourceReverb.reset();
|
||||
_listenerReverb.reset();
|
||||
}
|
||||
|
||||
void AudioClient::audioMixerKilled() {
|
||||
|
@ -569,27 +539,32 @@ bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) {
|
|||
return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName));
|
||||
}
|
||||
|
||||
ty_gverb* AudioClient::createGverbFilter() {
|
||||
// Initialize a new gverb instance
|
||||
ty_gverb* filter = gverb_new(_outputFormat.sampleRate(), _reverbOptions->getMaxRoomSize(), _reverbOptions->getRoomSize(),
|
||||
_reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(),
|
||||
_reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(),
|
||||
_reverbOptions->getTailLevel());
|
||||
void AudioClient::configureReverb() {
|
||||
ReverbParameters p;
|
||||
_listenerReverb.getParameters(&p);
|
||||
|
||||
return filter;
|
||||
// for now, reuse the gverb parameters
|
||||
p.sampleRate = _outputFormat.sampleRate();
|
||||
p.roomSize = _reverbOptions->getRoomSize();
|
||||
p.reverbTime = _reverbOptions->getReverbTime();
|
||||
p.highGain = -24.0f * (1.0f - _reverbOptions->getDamping());
|
||||
p.bandwidth = 10000.0f * _reverbOptions->getInputBandwidth();
|
||||
p.earlyGain = _reverbOptions->getEarlyLevel();
|
||||
p.lateGain = _reverbOptions->getTailLevel();
|
||||
p.wetDryMix = 100.0f * powf(10.0f, _reverbOptions->getWetLevel() * (1/20.0f));
|
||||
_listenerReverb.setParameters(&p);
|
||||
|
||||
// used for adding self-reverb to loopback audio
|
||||
p.wetDryMix = 100.0f;
|
||||
p.preDelay = 0.0f;
|
||||
p.earlyGain = -96.0f; // disable ER
|
||||
p.lateGain -= 12.0f; // quieter than listener reverb
|
||||
p.lateMixLeft = 0.0f;
|
||||
p.lateMixRight = 0.0f;
|
||||
_sourceReverb.setParameters(&p);
|
||||
}
|
||||
|
||||
void AudioClient::configureGverbFilter(ty_gverb* filter) {
|
||||
// Configure the instance (these functions are not super well named - they actually set several internal variables)
|
||||
gverb_set_roomsize(filter, _reverbOptions->getRoomSize());
|
||||
gverb_set_revtime(filter, _reverbOptions->getReverbTime());
|
||||
gverb_set_damping(filter, _reverbOptions->getDamping());
|
||||
gverb_set_inputbandwidth(filter, _reverbOptions->getInputBandwidth());
|
||||
gverb_set_earlylevel(filter, DB_CO(_reverbOptions->getEarlyLevel()));
|
||||
gverb_set_taillevel(filter, DB_CO(_reverbOptions->getTailLevel()));
|
||||
}
|
||||
|
||||
void AudioClient::updateGverbOptions() {
|
||||
void AudioClient::updateReverbOptions() {
|
||||
bool reverbChanged = false;
|
||||
if (_receivedAudioStream.hasReverb()) {
|
||||
|
||||
|
@ -599,7 +574,7 @@ void AudioClient::updateGverbOptions() {
|
|||
}
|
||||
if (_zoneReverbOptions.getWetLevel() != _receivedAudioStream.getWetLevel()) {
|
||||
_zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel());
|
||||
// Not part of actual filter config, no need to set reverbChanged to true
|
||||
reverbChanged = true;
|
||||
}
|
||||
|
||||
if (_reverbOptions != &_zoneReverbOptions) {
|
||||
|
@ -612,9 +587,7 @@ void AudioClient::updateGverbOptions() {
|
|||
}
|
||||
|
||||
if (reverbChanged) {
|
||||
gverb_free(_gverb);
|
||||
_gverb = createGverbFilter();
|
||||
configureGverbFilter(_gverb);
|
||||
configureReverb();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -622,7 +595,8 @@ void AudioClient::setReverb(bool reverb) {
|
|||
_reverb = reverb;
|
||||
|
||||
if (!_reverb) {
|
||||
gverb_flush(_gverb);
|
||||
_sourceReverb.reset();
|
||||
_listenerReverb.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -642,47 +616,7 @@ void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
|
|||
|
||||
if (_reverbOptions == &_scriptReverbOptions) {
|
||||
// Apply them to the reverb instances
|
||||
gverb_free(_gverb);
|
||||
_gverb = createGverbFilter();
|
||||
configureGverbFilter(_gverb);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reverbAlone, int numSamples,
|
||||
QAudioFormat& audioFormat, bool noEcho) {
|
||||
float wetFraction = DB_CO(_reverbOptions->getWetLevel());
|
||||
float dryFraction = 1.0f - wetFraction;
|
||||
|
||||
float lValue,rValue;
|
||||
for (int sample = 0; sample < numSamples; sample += audioFormat.channelCount()) {
|
||||
// Run GVerb
|
||||
float value = (float)samplesData[sample];
|
||||
gverb_do(gverb, value, &lValue, &rValue);
|
||||
|
||||
// Mix, accounting for clipping, the left and right channels. Ignore the rest.
|
||||
for (int j = sample; j < sample + audioFormat.channelCount(); j++) {
|
||||
if (j == sample) {
|
||||
// left channel
|
||||
int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction),
|
||||
AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE);
|
||||
samplesData[j] = (int16_t)lResult;
|
||||
|
||||
if (noEcho) {
|
||||
reverbAlone[j] = (int16_t)lValue * wetFraction;
|
||||
}
|
||||
} else if (j == (sample + 1)) {
|
||||
// right channel
|
||||
int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction),
|
||||
AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE);
|
||||
samplesData[j] = (int16_t)rResult;
|
||||
|
||||
if (noEcho) {
|
||||
reverbAlone[j] = (int16_t)rValue * wetFraction;
|
||||
}
|
||||
} else {
|
||||
// ignore channels above 2
|
||||
}
|
||||
}
|
||||
configureReverb();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -716,30 +650,28 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
|||
_loopbackResampler = new AudioSRC(_inputFormat.sampleRate(), _outputFormat.sampleRate(), channelCount);
|
||||
}
|
||||
|
||||
static QByteArray reverbAlone; // Intermediary for local reverb with no echo
|
||||
static QByteArray loopBackByteArray;
|
||||
|
||||
int numInputSamples = inputByteArray.size() / sizeof(int16_t);
|
||||
int numLoopbackSamples = numDestinationSamplesRequired(_inputFormat, _outputFormat, numInputSamples);
|
||||
|
||||
reverbAlone.resize(numInputSamples * sizeof(int16_t));
|
||||
loopBackByteArray.resize(numLoopbackSamples * sizeof(int16_t));
|
||||
|
||||
int16_t* inputSamples = reinterpret_cast<int16_t*>(inputByteArray.data());
|
||||
int16_t* reverbAloneSamples = reinterpret_cast<int16_t*>(reverbAlone.data());
|
||||
int16_t* loopbackSamples = reinterpret_cast<int16_t*>(loopBackByteArray.data());
|
||||
|
||||
if (hasReverb) {
|
||||
updateGverbOptions();
|
||||
addReverb(_gverb, inputSamples, reverbAloneSamples, numInputSamples,
|
||||
_inputFormat, !_shouldEchoLocally);
|
||||
}
|
||||
|
||||
possibleResampling(_loopbackResampler,
|
||||
(_shouldEchoLocally) ? inputSamples : reverbAloneSamples, loopbackSamples,
|
||||
inputSamples, loopbackSamples,
|
||||
numInputSamples, numLoopbackSamples,
|
||||
_inputFormat, _outputFormat);
|
||||
|
||||
// apply stereo reverb at the source, to the loopback audio
|
||||
if (!_shouldEchoLocally && hasReverb) {
|
||||
assert(_outputFormat.channelCount() == 2);
|
||||
updateReverbOptions();
|
||||
_sourceReverb.render(loopbackSamples, loopbackSamples, numLoopbackSamples/2);
|
||||
}
|
||||
|
||||
_loopbackOutputDevice->write(loopBackByteArray);
|
||||
}
|
||||
|
||||
|
@ -871,12 +803,20 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr
|
|||
outputBuffer.resize(numDeviceOutputSamples * sizeof(int16_t));
|
||||
|
||||
const int16_t* receivedSamples = reinterpret_cast<const int16_t*>(inputBuffer.data());
|
||||
int16_t* outputSamples = reinterpret_cast<int16_t*>(outputBuffer.data());
|
||||
|
||||
// copy the packet from the RB to the output
|
||||
possibleResampling(_networkToOutputResampler, receivedSamples,
|
||||
reinterpret_cast<int16_t*>(outputBuffer.data()),
|
||||
possibleResampling(_networkToOutputResampler, receivedSamples, outputSamples,
|
||||
numNetworkOutputSamples, numDeviceOutputSamples,
|
||||
_desiredOutputFormat, _outputFormat);
|
||||
|
||||
// apply stereo reverb at the listener, to the received audio
|
||||
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
|
||||
if (hasReverb) {
|
||||
assert(_outputFormat.channelCount() == 2);
|
||||
updateReverbOptions();
|
||||
_listenerReverb.render(outputSamples, outputSamples, numDeviceOutputSamples/2);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioClient::sendMuteEnvironmentPacket() {
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "AudioIOStats.h"
|
||||
#include "AudioNoiseGate.h"
|
||||
#include "AudioSRC.h"
|
||||
#include "AudioReverb.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning( push )
|
||||
|
@ -75,8 +76,6 @@ class QAudioOutput;
|
|||
class QIODevice;
|
||||
|
||||
|
||||
typedef struct ty_gverb ty_gverb;
|
||||
|
||||
class Transform;
|
||||
class NLPacket;
|
||||
|
||||
|
@ -263,7 +262,8 @@ private:
|
|||
AudioEffectOptions _scriptReverbOptions;
|
||||
AudioEffectOptions _zoneReverbOptions;
|
||||
AudioEffectOptions* _reverbOptions;
|
||||
ty_gverb* _gverb;
|
||||
AudioReverb _sourceReverb { AudioConstants::SAMPLE_RATE };
|
||||
AudioReverb _listenerReverb { AudioConstants::SAMPLE_RATE };
|
||||
|
||||
// possible streams needed for resample
|
||||
AudioSRC* _inputToNetworkResampler;
|
||||
|
@ -271,10 +271,8 @@ private:
|
|||
AudioSRC* _loopbackResampler;
|
||||
|
||||
// Adds Reverb
|
||||
ty_gverb* createGverbFilter();
|
||||
void configureGverbFilter(ty_gverb* filter);
|
||||
void updateGverbOptions();
|
||||
void addReverb(ty_gverb* gverb, int16_t* samples, int16_t* reverbAlone, int numSamples, QAudioFormat& format, bool noEcho = false);
|
||||
void configureReverb();
|
||||
void updateReverbOptions();
|
||||
|
||||
void handleLocalEchoAndReverb(QByteArray& inputByteArray);
|
||||
|
||||
|
|
|
@ -28,10 +28,10 @@ AudioEffectOptions::AudioEffectOptions(QScriptValue arguments) :
|
|||
_damping(0.5f),
|
||||
_spread(15.0f),
|
||||
_inputBandwidth(0.75f),
|
||||
_earlyLevel(-22.0f),
|
||||
_tailLevel(-28.0f),
|
||||
_earlyLevel(-12.0f),
|
||||
_tailLevel(-18.0f),
|
||||
_dryLevel(0.0f),
|
||||
_wetLevel(6.0f) {
|
||||
_wetLevel(0.0f) {
|
||||
if (arguments.property(MAX_ROOM_SIZE_HANDLE).isNumber()) {
|
||||
_maxRoomSize = arguments.property(MAX_ROOM_SIZE_HANDLE).toNumber();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <QtScript/QScriptContext>
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include "AudioReverb.h"
|
||||
|
||||
class AudioEffectOptions : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
|
1968
libraries/audio/src/AudioReverb.cpp
Normal file
1968
libraries/audio/src/AudioReverb.cpp
Normal file
File diff suppressed because it is too large
Load diff
76
libraries/audio/src/AudioReverb.h
Normal file
76
libraries/audio/src/AudioReverb.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// AudioReverb.h
|
||||
// libraries/audio/src
|
||||
//
|
||||
// Created by Ken Cooke on 10/11/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
|
||||
#ifndef hifi_AudioReverb_h
|
||||
#define hifi_AudioReverb_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct ReverbParameters {
|
||||
|
||||
float sampleRate; // [24000, 48000] Hz
|
||||
float bandwidth; // [20, 24000] Hz
|
||||
|
||||
float preDelay; // [0, 333] ms
|
||||
float lateDelay; // [0, 166] ms
|
||||
|
||||
float reverbTime; // [0.1, 100] seconds
|
||||
|
||||
float earlyDiffusion; // [0, 100] percent
|
||||
float lateDiffusion; // [0, 100] percent
|
||||
|
||||
float roomSize; // [0, 100] percent
|
||||
float density; // [0, 100] percent
|
||||
|
||||
float bassMult; // [0.1, 10] ratio
|
||||
float bassFreq; // [10, 500] Hz
|
||||
float highGain; // [-24, 0] dB
|
||||
float highFreq; // [1000, 12000] Hz
|
||||
|
||||
float modRate; // [0.1, 10] Hz
|
||||
float modDepth; // [0, 100] percent
|
||||
|
||||
float earlyGain; // [-96, +24] dB
|
||||
float lateGain; // [-96, +24] dB
|
||||
|
||||
float earlyMixLeft; // [0, 100] percent
|
||||
float earlyMixRight; // [0, 100] percent
|
||||
float lateMixLeft; // [0, 100] percent
|
||||
float lateMixRight; // [0, 100] percent
|
||||
|
||||
float wetDryMix; // [0, 100] percent
|
||||
|
||||
} ReverbParameters;
|
||||
|
||||
class ReverbImpl;
|
||||
|
||||
class AudioReverb {
|
||||
public:
|
||||
AudioReverb(float sampleRate);
|
||||
~AudioReverb();
|
||||
|
||||
void setParameters(ReverbParameters *p);
|
||||
void getParameters(ReverbParameters *p);
|
||||
void reset();
|
||||
|
||||
// deinterleaved float input/output (native format)
|
||||
void render(float** inputs, float** outputs, int numFrames);
|
||||
|
||||
// interleaved int16_t input/output
|
||||
void render(const int16_t* input, int16_t* output, int numFrames);
|
||||
|
||||
private:
|
||||
ReverbImpl *_impl;
|
||||
ReverbParameters _params;
|
||||
|
||||
float* _inout[2];
|
||||
void convertInputFromInt16(const int16_t* input, float** outputs, int numFrames);
|
||||
void convertOutputToInt16(float** inputs, int16_t* output, int numFrames);
|
||||
};
|
||||
|
||||
#endif // hifi_AudioReverb_h
|
|
@ -1218,7 +1218,7 @@ static inline float dither() {
|
|||
rz = rz * 69069 + 1;
|
||||
int32_t r0 = rz & 0xffff;
|
||||
int32_t r1 = rz >> 16;
|
||||
return (r0 - r1) * (1/65536.0f);
|
||||
return (int32_t)(r0 - r1) * (1/65536.0f);
|
||||
}
|
||||
|
||||
// convert float to int16_t, interleave stereo
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#ifndef hifi_AudioSRC_h
|
||||
#define hifi_AudioSRC_h
|
||||
|
||||
#include "stdint.h"
|
||||
#include <stdint.h>
|
||||
|
||||
class AudioSRC {
|
||||
|
||||
|
|
|
@ -47,13 +47,8 @@ const QString AvatarData::FRAME_NAME = "com.highfidelity.recording.AvatarData";
|
|||
static std::once_flag frameTypeRegistration;
|
||||
|
||||
AvatarData::AvatarData() :
|
||||
_sessionUUID(),
|
||||
_position(0.0f),
|
||||
SpatiallyNestable(NestableTypes::Avatar, QUuid()),
|
||||
_handPosition(0.0f),
|
||||
_referential(NULL),
|
||||
_bodyYaw(-90.0f),
|
||||
_bodyPitch(0.0f),
|
||||
_bodyRoll(0.0f),
|
||||
_targetScale(1.0f),
|
||||
_handState(0),
|
||||
_keyState(NO_KEY_DOWN),
|
||||
|
@ -72,12 +67,14 @@ AvatarData::AvatarData() :
|
|||
_targetVelocity(0.0f),
|
||||
_localAABox(DEFAULT_LOCAL_AABOX_CORNER, DEFAULT_LOCAL_AABOX_SCALE)
|
||||
{
|
||||
setBodyPitch(0.0f);
|
||||
setBodyYaw(-90.0f);
|
||||
setBodyRoll(0.0f);
|
||||
}
|
||||
|
||||
AvatarData::~AvatarData() {
|
||||
delete _headData;
|
||||
delete _handData;
|
||||
delete _referential;
|
||||
}
|
||||
|
||||
// We cannot have a file-level variable (const or otherwise) in the header if it uses PathUtils, because that references Application, which will not yet initialized.
|
||||
|
@ -90,49 +87,18 @@ const QUrl& AvatarData::defaultFullAvatarModelUrl() {
|
|||
return _defaultFullAvatarModelUrl;
|
||||
}
|
||||
|
||||
const glm::vec3& AvatarData::getPosition() const {
|
||||
if (_referential) {
|
||||
_referential->update();
|
||||
}
|
||||
return _position;
|
||||
}
|
||||
|
||||
void AvatarData::setPosition(const glm::vec3 position, bool overideReferential) {
|
||||
if (!_referential || overideReferential) {
|
||||
_position = position;
|
||||
}
|
||||
}
|
||||
|
||||
glm::quat AvatarData::getOrientation() const {
|
||||
if (_referential) {
|
||||
_referential->update();
|
||||
}
|
||||
|
||||
return glm::quat(glm::radians(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)));
|
||||
}
|
||||
|
||||
void AvatarData::setOrientation(const glm::quat& orientation, bool overideReferential) {
|
||||
if (!_referential || overideReferential) {
|
||||
glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(orientation));
|
||||
_bodyPitch = eulerAngles.x;
|
||||
_bodyYaw = eulerAngles.y;
|
||||
_bodyRoll = eulerAngles.z;
|
||||
}
|
||||
}
|
||||
|
||||
// There are a number of possible strategies for this set of tools through endRender, below.
|
||||
void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) {
|
||||
avatarLock.lock();
|
||||
setPosition(position, true);
|
||||
setOrientation(orientation, true);
|
||||
Transform trans;
|
||||
trans.setTranslation(position);
|
||||
trans.setRotation(orientation);
|
||||
SpatiallyNestable::setTransform(trans);
|
||||
avatarLock.unlock();
|
||||
updateAttitude();
|
||||
}
|
||||
void AvatarData::startCapture() {
|
||||
avatarLock.lock();
|
||||
assert(_nextAllowed);
|
||||
_nextAllowed = false;
|
||||
_nextPosition = getPosition();
|
||||
_nextOrientation = getOrientation();
|
||||
}
|
||||
void AvatarData::endCapture() {
|
||||
avatarLock.unlock();
|
||||
|
@ -145,57 +111,42 @@ void AvatarData::endUpdate() {
|
|||
}
|
||||
void AvatarData::startRenderRun() {
|
||||
// I'd like to get rid of this and just (un)lock at (end-)startRender.
|
||||
// But somehow that causes judder in rotations.
|
||||
// But somehow that causes judder in rotations.
|
||||
avatarLock.lock();
|
||||
}
|
||||
void AvatarData::endRenderRun() {
|
||||
avatarLock.unlock();
|
||||
}
|
||||
void AvatarData::startRender() {
|
||||
glm::vec3 pos = getPosition();
|
||||
glm::quat rot = getOrientation();
|
||||
setPosition(_nextPosition, true);
|
||||
setOrientation(_nextOrientation, true);
|
||||
updateAttitude();
|
||||
_nextPosition = pos;
|
||||
_nextOrientation = rot;
|
||||
}
|
||||
void AvatarData::endRender() {
|
||||
setPosition(_nextPosition, true);
|
||||
setOrientation(_nextOrientation, true);
|
||||
updateAttitude();
|
||||
_nextAllowed = true;
|
||||
}
|
||||
|
||||
float AvatarData::getTargetScale() const {
|
||||
if (_referential) {
|
||||
_referential->update();
|
||||
}
|
||||
|
||||
return _targetScale;
|
||||
}
|
||||
|
||||
void AvatarData::setTargetScale(float targetScale, bool overideReferential) {
|
||||
if (!_referential || overideReferential) {
|
||||
_targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, targetScale));
|
||||
}
|
||||
void AvatarData::setTargetScale(float targetScale) {
|
||||
_targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, targetScale));
|
||||
}
|
||||
|
||||
void AvatarData::setClampedTargetScale(float targetScale, bool overideReferential) {
|
||||
void AvatarData::setClampedTargetScale(float targetScale) {
|
||||
|
||||
targetScale = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
||||
|
||||
setTargetScale(targetScale, overideReferential);
|
||||
setTargetScale(targetScale);
|
||||
qCDebug(avatars) << "Changed scale to " << _targetScale;
|
||||
}
|
||||
|
||||
glm::vec3 AvatarData::getHandPosition() const {
|
||||
return getOrientation() * _handPosition + _position;
|
||||
return getOrientation() * _handPosition + getPosition();
|
||||
}
|
||||
|
||||
void AvatarData::setHandPosition(const glm::vec3& handPosition) {
|
||||
// store relative to position/orientation
|
||||
_handPosition = glm::inverse(getOrientation()) * (handPosition - _position);
|
||||
_handPosition = glm::inverse(getOrientation()) * (handPosition - getPosition());
|
||||
}
|
||||
|
||||
QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
||||
|
@ -216,13 +167,18 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
|||
unsigned char* destinationBuffer = reinterpret_cast<unsigned char*>(avatarDataByteArray.data());
|
||||
unsigned char* startPosition = destinationBuffer;
|
||||
|
||||
memcpy(destinationBuffer, &_position, sizeof(_position));
|
||||
destinationBuffer += sizeof(_position);
|
||||
const glm::vec3& position = getLocalPosition();
|
||||
memcpy(destinationBuffer, &position, sizeof(position));
|
||||
destinationBuffer += sizeof(position);
|
||||
|
||||
// Body rotation (NOTE: This needs to become a quaternion to save two bytes)
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyYaw);
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyPitch);
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _bodyRoll);
|
||||
memcpy(destinationBuffer, &_globalPosition, sizeof(_globalPosition));
|
||||
destinationBuffer += sizeof(_globalPosition);
|
||||
|
||||
// Body rotation
|
||||
glm::vec3 bodyEulerAngles = glm::degrees(safeEulerAngles(getLocalOrientation()));
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, bodyEulerAngles.y);
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, bodyEulerAngles.x);
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, bodyEulerAngles.z);
|
||||
|
||||
// Body scale
|
||||
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _targetScale);
|
||||
|
@ -255,14 +211,18 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
|||
setAtBit(bitItems, IS_EYE_TRACKER_CONNECTED);
|
||||
}
|
||||
// referential state
|
||||
if (_referential != NULL && _referential->isValid()) {
|
||||
SpatiallyNestablePointer parent = getParentPointer();
|
||||
if (parent) {
|
||||
setAtBit(bitItems, HAS_REFERENTIAL);
|
||||
}
|
||||
*destinationBuffer++ = bitItems;
|
||||
|
||||
// Add referential
|
||||
if (_referential != NULL && _referential->isValid()) {
|
||||
destinationBuffer += _referential->packReferential(destinationBuffer);
|
||||
if (parent) {
|
||||
QByteArray referentialAsBytes = parent->getID().toRfc4122();
|
||||
memcpy(destinationBuffer, referentialAsBytes.data(), referentialAsBytes.size());
|
||||
destinationBuffer += referentialAsBytes.size();
|
||||
memcpy(destinationBuffer, &_parentJointIndex, sizeof(_parentJointIndex));
|
||||
destinationBuffer += sizeof(_parentJointIndex);
|
||||
}
|
||||
|
||||
// If it is connected, pack up the data
|
||||
|
@ -496,13 +456,16 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
memcpy(&position, sourceBuffer, sizeof(position));
|
||||
sourceBuffer += sizeof(position);
|
||||
|
||||
memcpy(&_globalPosition, sourceBuffer, sizeof(_globalPosition));
|
||||
sourceBuffer += sizeof(_globalPosition);
|
||||
|
||||
if (glm::isnan(position.x) || glm::isnan(position.y) || glm::isnan(position.z)) {
|
||||
if (shouldLogError(now)) {
|
||||
qCDebug(avatars) << "Discard nan AvatarData::position; displayName = '" << _displayName << "'";
|
||||
}
|
||||
return maxAvailableSize;
|
||||
}
|
||||
setPosition(position);
|
||||
setLocalPosition(position);
|
||||
|
||||
// rotation (NOTE: This needs to become a quaternion to save two bytes)
|
||||
float yaw, pitch, roll;
|
||||
|
@ -515,11 +478,18 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
}
|
||||
return maxAvailableSize;
|
||||
}
|
||||
if (_bodyYaw != yaw || _bodyPitch != pitch || _bodyRoll != roll) {
|
||||
|
||||
// TODO is this safe? will the floats not exactly match?
|
||||
// Andrew says:
|
||||
// Yes, there is a possibility that the transmitted will not quite match the extracted despite being originally
|
||||
// extracted from the exact same quaternion. I followed the code through and it appears the risk is that the
|
||||
// avatar's SkeletonModel might fall into the CPU expensive part of Model::updateClusterMatrices() when otherwise it
|
||||
// would not have required it. However, we know we can update many simultaneously animating avatars, and most
|
||||
// avatars will be moving constantly anyway, so I don't think we need to worry.
|
||||
if (getBodyYaw() != yaw || getBodyPitch() != pitch || getBodyRoll() != roll) {
|
||||
_hasNewJointRotations = true;
|
||||
_bodyYaw = yaw;
|
||||
_bodyPitch = pitch;
|
||||
_bodyRoll = roll;
|
||||
glm::vec3 eulerAngles(pitch, yaw, roll);
|
||||
setLocalOrientation(glm::quat(glm::radians(eulerAngles)));
|
||||
}
|
||||
|
||||
// scale
|
||||
|
@ -581,21 +551,17 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
_headData->_isEyeTrackerConnected = oneAtBit(bitItems, IS_EYE_TRACKER_CONNECTED);
|
||||
bool hasReferential = oneAtBit(bitItems, HAS_REFERENTIAL);
|
||||
|
||||
// Referential
|
||||
if (hasReferential) {
|
||||
Referential* ref = new Referential(sourceBuffer, this);
|
||||
if (_referential == NULL ||
|
||||
ref->version() != _referential->version()) {
|
||||
changeReferential(ref);
|
||||
} else {
|
||||
delete ref;
|
||||
}
|
||||
_referential->update();
|
||||
} else if (_referential != NULL) {
|
||||
changeReferential(NULL);
|
||||
const int sizeOfPackedUuid = 16;
|
||||
QByteArray referentialAsBytes((const char*)sourceBuffer, sizeOfPackedUuid);
|
||||
_parentID = QUuid::fromRfc4122(referentialAsBytes);
|
||||
sourceBuffer += sizeOfPackedUuid;
|
||||
memcpy(&_parentJointIndex, sourceBuffer, sizeof(_parentJointIndex));
|
||||
sourceBuffer += sizeof(_parentJointIndex);
|
||||
} else {
|
||||
_parentID = QUuid();
|
||||
}
|
||||
|
||||
|
||||
if (_headData->_isFaceTrackerConnected) {
|
||||
float leftEyeBlink, rightEyeBlink, averageLoudness, browAudioLift;
|
||||
minPossibleSize += sizeof(leftEyeBlink) + sizeof(rightEyeBlink) + sizeof(averageLoudness) + sizeof(browAudioLift);
|
||||
|
@ -788,19 +754,10 @@ int AvatarData::getReceiveRate() const {
|
|||
return lrint(1.0f / _averageBytesReceived.getEventDeltaAverage());
|
||||
}
|
||||
|
||||
bool AvatarData::hasReferential() {
|
||||
return _referential != NULL;
|
||||
}
|
||||
|
||||
std::shared_ptr<Transform> AvatarData::getRecordingBasis() const {
|
||||
return _recordingBasis;
|
||||
}
|
||||
|
||||
void AvatarData::changeReferential(Referential* ref) {
|
||||
delete _referential;
|
||||
_referential = ref;
|
||||
}
|
||||
|
||||
void AvatarData::setRawJointData(QVector<JointData> data) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setRawJointData", Q_ARG(QVector<JointData>, data));
|
||||
|
@ -1437,14 +1394,6 @@ void AvatarData::clearRecordingBasis() {
|
|||
_recordingBasis.reset();
|
||||
}
|
||||
|
||||
Transform AvatarData::getTransform() const {
|
||||
Transform result;
|
||||
result.setRotation(getOrientation());
|
||||
result.setTranslation(getPosition());
|
||||
result.setScale(getTargetScale());
|
||||
return result;
|
||||
}
|
||||
|
||||
static const QString JSON_AVATAR_BASIS = QStringLiteral("basisTransform");
|
||||
static const QString JSON_AVATAR_RELATIVE = QStringLiteral("relativeTransform");
|
||||
static const QString JSON_AVATAR_JOINT_ARRAY = QStringLiteral("jointArray");
|
||||
|
@ -1495,15 +1444,17 @@ QJsonObject AvatarData::toJson() const {
|
|||
}
|
||||
|
||||
auto recordingBasis = getRecordingBasis();
|
||||
Transform avatarTransform = getTransform();
|
||||
avatarTransform.setScale(getTargetScale());
|
||||
if (recordingBasis) {
|
||||
root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis);
|
||||
// Find the relative transform
|
||||
auto relativeTransform = recordingBasis->relativeTransform(getTransform());
|
||||
auto relativeTransform = recordingBasis->relativeTransform(avatarTransform);
|
||||
if (!relativeTransform.isIdentity()) {
|
||||
root[JSON_AVATAR_RELATIVE] = Transform::toJson(relativeTransform);
|
||||
}
|
||||
} else {
|
||||
root[JSON_AVATAR_RELATIVE] = Transform::toJson(getTransform());
|
||||
root[JSON_AVATAR_RELATIVE] = Transform::toJson(avatarTransform);
|
||||
}
|
||||
|
||||
auto scale = getTargetScale();
|
||||
|
@ -1529,6 +1480,17 @@ QJsonObject AvatarData::toJson() const {
|
|||
}
|
||||
|
||||
void AvatarData::fromJson(const QJsonObject& json) {
|
||||
// The head setOrientation likes to overwrite the avatar orientation,
|
||||
// so lets do the head first
|
||||
// Most head data is relative to the avatar, and needs no basis correction,
|
||||
// but the lookat vector does need correction
|
||||
if (json.contains(JSON_AVATAR_HEAD)) {
|
||||
if (!_headData) {
|
||||
_headData = new HeadData(this);
|
||||
}
|
||||
_headData->fromJson(json[JSON_AVATAR_HEAD].toObject());
|
||||
}
|
||||
|
||||
if (json.contains(JSON_AVATAR_HEAD_MODEL)) {
|
||||
auto faceModelURL = json[JSON_AVATAR_HEAD_MODEL].toString();
|
||||
if (faceModelURL != getFaceModelURL().toString()) {
|
||||
|
@ -1600,15 +1562,6 @@ void AvatarData::fromJson(const QJsonObject& json) {
|
|||
}
|
||||
setRawJointData(jointArray);
|
||||
}
|
||||
|
||||
// Most head data is relative to the avatar, and needs no basis correction,
|
||||
// but the lookat vector does need correction
|
||||
if (json.contains(JSON_AVATAR_HEAD)) {
|
||||
if (!_headData) {
|
||||
_headData = new HeadData(this);
|
||||
}
|
||||
_headData->fromJson(json[JSON_AVATAR_HEAD].toObject());
|
||||
}
|
||||
}
|
||||
|
||||
// Every frame will store both a basis for the recording and a relative transform
|
||||
|
@ -1639,3 +1592,44 @@ void AvatarData::fromFrame(const QByteArray& frameData, AvatarData& result) {
|
|||
#endif
|
||||
result.fromJson(doc.object());
|
||||
}
|
||||
|
||||
float AvatarData::getBodyYaw() const {
|
||||
glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(getOrientation()));
|
||||
return eulerAngles.y;
|
||||
}
|
||||
|
||||
void AvatarData::setBodyYaw(float bodyYaw) {
|
||||
glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(getOrientation()));
|
||||
eulerAngles.y = bodyYaw;
|
||||
setOrientation(glm::quat(glm::radians(eulerAngles)));
|
||||
}
|
||||
|
||||
float AvatarData::getBodyPitch() const {
|
||||
glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(getOrientation()));
|
||||
return eulerAngles.x;
|
||||
}
|
||||
|
||||
void AvatarData::setBodyPitch(float bodyPitch) {
|
||||
glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(getOrientation()));
|
||||
eulerAngles.x = bodyPitch;
|
||||
setOrientation(glm::quat(glm::radians(eulerAngles)));
|
||||
}
|
||||
|
||||
float AvatarData::getBodyRoll() const {
|
||||
glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(getOrientation()));
|
||||
return eulerAngles.z;
|
||||
}
|
||||
|
||||
void AvatarData::setBodyRoll(float bodyRoll) {
|
||||
glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(getOrientation()));
|
||||
eulerAngles.z = bodyRoll;
|
||||
setOrientation(glm::quat(glm::radians(eulerAngles)));
|
||||
}
|
||||
|
||||
void AvatarData::setPosition(const glm::vec3 position) {
|
||||
SpatiallyNestable::setPosition(position);
|
||||
}
|
||||
|
||||
void AvatarData::setOrientation(const glm::quat orientation) {
|
||||
SpatiallyNestable::setOrientation(orientation);
|
||||
}
|
||||
|
|
|
@ -51,12 +51,12 @@ typedef unsigned long long quint64;
|
|||
#include <Node.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
#include <SpatiallyNestable.h>
|
||||
|
||||
#include "AABox.h"
|
||||
#include "HandData.h"
|
||||
#include "HeadData.h"
|
||||
#include "PathUtils.h"
|
||||
#include "Referential.h"
|
||||
|
||||
using AvatarSharedPointer = std::shared_ptr<AvatarData>;
|
||||
using AvatarWeakPointer = std::weak_ptr<AvatarData>;
|
||||
|
@ -135,7 +135,7 @@ class AttachmentData;
|
|||
class Transform;
|
||||
using TransformPointer = std::shared_ptr<Transform>;
|
||||
|
||||
class AvatarData : public QObject {
|
||||
class AvatarData : public QObject, public SpatiallyNestable {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
|
||||
|
@ -177,10 +177,7 @@ public:
|
|||
|
||||
virtual bool isMyAvatar() const { return false; }
|
||||
|
||||
const QUuid& getSessionUUID() const { return _sessionUUID; }
|
||||
|
||||
const glm::vec3& getPosition() const;
|
||||
virtual void setPosition(const glm::vec3 position, bool overideReferential = false);
|
||||
const QUuid& getSessionUUID() const { return getID(); }
|
||||
|
||||
glm::vec3 getHandPosition() const;
|
||||
void setHandPosition(const glm::vec3& handPosition);
|
||||
|
@ -196,16 +193,16 @@ public:
|
|||
/// \return number of bytes parsed
|
||||
virtual int parseDataFromBuffer(const QByteArray& buffer);
|
||||
|
||||
// Body Rotation (degrees)
|
||||
float getBodyYaw() const { return _bodyYaw; }
|
||||
void setBodyYaw(float bodyYaw) { _bodyYaw = bodyYaw; }
|
||||
float getBodyPitch() const { return _bodyPitch; }
|
||||
void setBodyPitch(float bodyPitch) { _bodyPitch = bodyPitch; }
|
||||
float getBodyRoll() const { return _bodyRoll; }
|
||||
void setBodyRoll(float bodyRoll) { _bodyRoll = bodyRoll; }
|
||||
// Body Rotation (degrees)
|
||||
float getBodyYaw() const;
|
||||
void setBodyYaw(float bodyYaw);
|
||||
float getBodyPitch() const;
|
||||
void setBodyPitch(float bodyPitch);
|
||||
float getBodyRoll() const;
|
||||
void setBodyRoll(float bodyRoll);
|
||||
|
||||
glm::quat getOrientation() const;
|
||||
virtual void setOrientation(const glm::quat& orientation, bool overideReferential = false);
|
||||
virtual void setPosition(glm::vec3 position);
|
||||
virtual void setOrientation(glm::quat orientation);
|
||||
|
||||
void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time.
|
||||
void startCapture(); // start/end of the period in which the latest values are about to be captured for camera, etc.
|
||||
|
@ -239,8 +236,8 @@ public:
|
|||
|
||||
// Scale
|
||||
float getTargetScale() const;
|
||||
void setTargetScale(float targetScale, bool overideReferential = false);
|
||||
void setClampedTargetScale(float targetScale, bool overideReferential = false);
|
||||
void setTargetScale(float targetScale);
|
||||
void setClampedTargetScale(float targetScale);
|
||||
|
||||
// Hand State
|
||||
Q_INVOKABLE void setHandState(char s) { _handState = s; }
|
||||
|
@ -326,7 +323,6 @@ public:
|
|||
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }
|
||||
|
||||
const AABox& getLocalAABox() const { return _localAABox; }
|
||||
const Referential* getReferential() const { return _referential; }
|
||||
|
||||
int getUsecsSinceLastUpdate() const { return _averageBytesReceived.getUsecsSinceLastEvent(); }
|
||||
int getAverageBytesReceivedPerSecond() const;
|
||||
|
@ -338,13 +334,14 @@ public:
|
|||
|
||||
bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; }
|
||||
|
||||
Transform getTransform() const;
|
||||
void clearRecordingBasis();
|
||||
TransformPointer getRecordingBasis() const;
|
||||
void setRecordingBasis(TransformPointer recordingBasis = TransformPointer());
|
||||
QJsonObject toJson() const;
|
||||
void fromJson(const QJsonObject& json);
|
||||
|
||||
glm::vec3 getClientGlobalPosition() { return _globalPosition; }
|
||||
|
||||
public slots:
|
||||
void sendAvatarDataPacket();
|
||||
void sendIdentityPacket();
|
||||
|
@ -352,24 +349,10 @@ public slots:
|
|||
|
||||
void setBillboardFromNetworkReply();
|
||||
void setJointMappingsFromNetworkReply();
|
||||
void setSessionUUID(const QUuid& sessionUUID) { _sessionUUID = sessionUUID; }
|
||||
bool hasReferential();
|
||||
void setSessionUUID(const QUuid& sessionUUID) { setID(sessionUUID); }
|
||||
|
||||
protected:
|
||||
QUuid _sessionUUID;
|
||||
glm::vec3 _position = START_LOCATION;
|
||||
glm::vec3 _handPosition;
|
||||
|
||||
Referential* _referential;
|
||||
|
||||
// Body rotation
|
||||
float _bodyYaw; // degrees
|
||||
float _bodyPitch; // degrees
|
||||
float _bodyRoll; // degrees
|
||||
|
||||
glm::vec3 _nextPosition {};
|
||||
glm::quat _nextOrientation {};
|
||||
bool _nextAllowed {true};
|
||||
|
||||
// Body scale
|
||||
float _targetScale;
|
||||
|
@ -411,7 +394,6 @@ protected:
|
|||
|
||||
/// Loads the joint indices, names from the FST file (if any)
|
||||
virtual void updateJointMappings();
|
||||
void changeReferential(Referential* ref);
|
||||
|
||||
glm::vec3 _velocity;
|
||||
glm::vec3 _targetVelocity;
|
||||
|
@ -426,6 +408,11 @@ protected:
|
|||
// During playback, it holds the origin from which to play the relative positions in the clip
|
||||
TransformPointer _recordingBasis;
|
||||
|
||||
// _globalPosition is sent along with localPosition + parent because the avatar-mixer doesn't know
|
||||
// where Entities are located. This is currently only used by the mixer to decide how often to send
|
||||
// updates about one avatar to another.
|
||||
glm::vec3 _globalPosition;
|
||||
|
||||
private:
|
||||
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);
|
||||
static QUrl _defaultFullAvatarModelUrl;
|
||||
|
|
|
@ -65,7 +65,10 @@ AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID,
|
|||
|
||||
AvatarSharedPointer AvatarHashMap::findAvatar(const QUuid& sessionUUID) {
|
||||
QReadLocker locker(&_hashLock);
|
||||
return _avatarHash.value(sessionUUID);
|
||||
if (_avatarHash.contains(sessionUUID)) {
|
||||
return _avatarHash.value(sessionUUID);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AvatarHashMap::processAvatarDataPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
//
|
||||
// Referential.cpp
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/30/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include "AvatarData.h"
|
||||
#include "AvatarLogging.h"
|
||||
#include "Referential.h"
|
||||
|
||||
Referential::Referential(Type type, AvatarData* avatar) :
|
||||
_type(type),
|
||||
_version(0),
|
||||
_isValid(true),
|
||||
_avatar(avatar)
|
||||
{
|
||||
if (_avatar == NULL) {
|
||||
_isValid = false;
|
||||
return;
|
||||
}
|
||||
if (_avatar->hasReferential()) {
|
||||
_version = _avatar->getReferential()->version() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Referential::Referential(const unsigned char*& sourceBuffer, AvatarData* avatar) :
|
||||
_isValid(false),
|
||||
_avatar(avatar)
|
||||
{
|
||||
// Since we can't return how many byte have been read
|
||||
// We take a reference to the pointer as argument and increment the pointer ouself.
|
||||
sourceBuffer += unpackReferential(sourceBuffer);
|
||||
// The actual unpacking to the right referential type happens in Avatar::simulate()
|
||||
// If subclassed, make sure to add a case there to unpack the new referential type correctly
|
||||
}
|
||||
|
||||
Referential::~Referential() {
|
||||
}
|
||||
|
||||
int Referential::packReferential(unsigned char* destinationBuffer) const {
|
||||
const unsigned char* startPosition = destinationBuffer;
|
||||
destinationBuffer += pack(destinationBuffer);
|
||||
|
||||
unsigned char* sizePosition = destinationBuffer++; // Save a spot for the extra data size
|
||||
char size = packExtraData(destinationBuffer);
|
||||
*sizePosition = size; // write extra data size in saved spot here
|
||||
destinationBuffer += size;
|
||||
return destinationBuffer - startPosition;
|
||||
}
|
||||
|
||||
int Referential::unpackReferential(const unsigned char* sourceBuffer) {
|
||||
const unsigned char* startPosition = sourceBuffer;
|
||||
sourceBuffer += unpack(sourceBuffer);
|
||||
char expectedSize = *sourceBuffer++;
|
||||
char bytesRead = unpackExtraData(sourceBuffer, expectedSize);
|
||||
_isValid = (bytesRead == expectedSize);
|
||||
if (!_isValid) {
|
||||
// Will occur if the new instance unpacking is of the wrong type
|
||||
qCDebug(avatars) << "[ERROR] Referential extra data overflow";
|
||||
}
|
||||
sourceBuffer += expectedSize;
|
||||
return sourceBuffer - startPosition;
|
||||
}
|
||||
|
||||
int Referential::pack(unsigned char* destinationBuffer) const {
|
||||
unsigned char* startPosition = destinationBuffer;
|
||||
*destinationBuffer++ = (unsigned char)_type;
|
||||
memcpy(destinationBuffer, &_version, sizeof(_version));
|
||||
destinationBuffer += sizeof(_version);
|
||||
|
||||
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, _translation, 0);
|
||||
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _rotation);
|
||||
return destinationBuffer - startPosition;
|
||||
}
|
||||
|
||||
int Referential::unpack(const unsigned char* sourceBuffer) {
|
||||
const unsigned char* startPosition = sourceBuffer;
|
||||
_type = (Type)*sourceBuffer++;
|
||||
if (_type < 0 || _type >= NUM_TYPES) {
|
||||
_type = UNKNOWN;
|
||||
}
|
||||
memcpy(&_version, sourceBuffer, sizeof(_version));
|
||||
sourceBuffer += sizeof(_version);
|
||||
|
||||
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, _translation, 0);
|
||||
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _rotation);
|
||||
return sourceBuffer - startPosition;
|
||||
}
|
||||
|
||||
int Referential::packExtraData(unsigned char *destinationBuffer) const {
|
||||
// Since we can't interpret that data, just store it in a buffer for later use.
|
||||
memcpy(destinationBuffer, _extraDataBuffer.data(), _extraDataBuffer.size());
|
||||
return _extraDataBuffer.size();
|
||||
}
|
||||
|
||||
|
||||
int Referential::unpackExtraData(const unsigned char* sourceBuffer, int size) {
|
||||
_extraDataBuffer.clear();
|
||||
_extraDataBuffer.append(reinterpret_cast<const char*>(sourceBuffer), size);
|
||||
return size;
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
//
|
||||
// Referential.h
|
||||
//
|
||||
//
|
||||
// Created by Clement on 7/30/14.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_Referential_h
|
||||
#define hifi_Referential_h
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
class AvatarData;
|
||||
|
||||
/// Stores and enforce the relative position of an avatar to a given referential (ie. model, joint, ...)
|
||||
class Referential {
|
||||
public:
|
||||
enum Type {
|
||||
UNKNOWN,
|
||||
MODEL,
|
||||
JOINT,
|
||||
AVATAR,
|
||||
|
||||
NUM_TYPES
|
||||
};
|
||||
|
||||
Referential(const unsigned char*& sourceBuffer, AvatarData* avatar);
|
||||
virtual ~Referential();
|
||||
|
||||
Type type() const { return _type; }
|
||||
quint8 version() const { return _version; }
|
||||
bool isValid() const { return _isValid; }
|
||||
bool hasExtraData() const { return !_extraDataBuffer.isEmpty(); }
|
||||
|
||||
glm::vec3 getTranslation() const { return _translation; }
|
||||
glm::quat getRotation() const { return _rotation; }
|
||||
QByteArray getExtraData() const { return _extraDataBuffer; }
|
||||
|
||||
virtual void update() {}
|
||||
int packReferential(unsigned char* destinationBuffer) const;
|
||||
int unpackReferential(const unsigned char* sourceBuffer);
|
||||
|
||||
protected:
|
||||
Referential(Type type, AvatarData* avatar);
|
||||
|
||||
// packs the base class data
|
||||
int pack(unsigned char* destinationBuffer) const;
|
||||
int unpack(const unsigned char* sourceBuffer);
|
||||
// virtual functions that pack fthe extra data of subclasses (needs to be reimplemented in subclass)
|
||||
virtual int packExtraData(unsigned char* destinationBuffer) const;
|
||||
virtual int unpackExtraData(const unsigned char* sourceBuffer, int size);
|
||||
|
||||
Type _type;
|
||||
quint8 _version;
|
||||
bool _isValid;
|
||||
AvatarData* _avatar;
|
||||
QByteArray _extraDataBuffer;
|
||||
|
||||
glm::vec3 _refPosition; // position of object in world-frame
|
||||
glm::quat _refRotation; // rotation of object in world-frame
|
||||
glm::vec3 _lastRefDimension; // dimension of object when _translation was last computed
|
||||
|
||||
glm::vec3 _translation; // offset of avatar in object local-frame
|
||||
glm::quat _rotation; // rotation of avatar in object local-frame
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_Referential_h
|
|
@ -23,10 +23,10 @@ class ActionEndpoint : public Endpoint {
|
|||
public:
|
||||
ActionEndpoint(const Input& id = Input::INVALID_INPUT) : Endpoint(id) { }
|
||||
|
||||
virtual float peek() const { return _currentValue; }
|
||||
virtual float peek() const override { return _currentValue; }
|
||||
virtual void apply(float newValue, const Pointer& source) override;
|
||||
|
||||
virtual Pose peekPose() const { return _currentPose; }
|
||||
virtual Pose peekPose() const override { return _currentPose; }
|
||||
virtual void apply(const Pose& value, const Pointer& source) override;
|
||||
|
||||
virtual void reset() override;
|
||||
|
|
|
@ -28,9 +28,9 @@ public:
|
|||
virtual Pose pose() override;
|
||||
virtual void apply(const Pose& value, const Pointer& source) override { }
|
||||
|
||||
virtual bool writeable() const { return false; }
|
||||
virtual bool readable() const { return !_read; }
|
||||
virtual void reset() { _read = false; }
|
||||
virtual bool writeable() const override { return false; }
|
||||
virtual bool readable() const override { return !_read; }
|
||||
virtual void reset() override { _read = false; }
|
||||
|
||||
private:
|
||||
bool _read { false };
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
|
||||
virtual float apply(float value) const override;
|
||||
|
||||
virtual bool parseParameters(const QJsonValue& parameters);
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override;
|
||||
|
||||
private:
|
||||
static const float DEFAULT_LAST_EMIT_TIME;
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
virtual float apply(float value) const override {
|
||||
return value * _scale;
|
||||
}
|
||||
virtual bool parseParameters(const QJsonValue& parameters);
|
||||
virtual bool parseParameters(const QJsonValue& parameters) override;
|
||||
|
||||
private:
|
||||
float _scale = 1.0f;
|
||||
|
|
|
@ -30,12 +30,11 @@ const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const {
|
|||
return NAME;
|
||||
}
|
||||
|
||||
std::vector<QAction*> _framerateActions;
|
||||
QAction* _vsyncAction{ nullptr };
|
||||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::activate() {
|
||||
WindowOpenGLDisplayPlugin::activate();
|
||||
|
||||
_framerateActions.clear();
|
||||
_container->addMenuItem(MENU_PATH(), FULLSCREEN,
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), FULLSCREEN,
|
||||
[this](bool clicked) {
|
||||
if (clicked) {
|
||||
_container->setFullscreen(getFullscreenTarget());
|
||||
|
@ -45,26 +44,24 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() {
|
|||
}, true, false);
|
||||
_container->addMenu(FRAMERATE);
|
||||
_framerateActions.push_back(
|
||||
_container->addMenuItem(FRAMERATE, FRAMERATE_UNLIMITED,
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, FRAMERATE, FRAMERATE_UNLIMITED,
|
||||
[this](bool) { updateFramerate(); }, true, true, FRAMERATE));
|
||||
_framerateActions.push_back(
|
||||
_container->addMenuItem(FRAMERATE, FRAMERATE_60,
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, FRAMERATE, FRAMERATE_60,
|
||||
[this](bool) { updateFramerate(); }, true, false, FRAMERATE));
|
||||
_framerateActions.push_back(
|
||||
_container->addMenuItem(FRAMERATE, FRAMERATE_50,
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, FRAMERATE, FRAMERATE_50,
|
||||
[this](bool) { updateFramerate(); }, true, false, FRAMERATE));
|
||||
_framerateActions.push_back(
|
||||
_container->addMenuItem(FRAMERATE, FRAMERATE_40,
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, FRAMERATE, FRAMERATE_40,
|
||||
[this](bool) { updateFramerate(); }, true, false, FRAMERATE));
|
||||
_framerateActions.push_back(
|
||||
_container->addMenuItem(FRAMERATE, FRAMERATE_30,
|
||||
_container->addMenuItem(PluginType::DISPLAY_PLUGIN, FRAMERATE, FRAMERATE_30,
|
||||
[this](bool) { updateFramerate(); }, true, false, FRAMERATE));
|
||||
|
||||
WindowOpenGLDisplayPlugin::activate();
|
||||
|
||||
// Vsync detection happens in the parent class activate, so we need to check after that
|
||||
if (_vsyncSupported) {
|
||||
_vsyncAction = _container->addMenuItem(MENU_PATH(), VSYNC_ON, [this](bool) {}, true, true);
|
||||
_vsyncAction = _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), VSYNC_ON, [this](bool) {}, true, true);
|
||||
} else {
|
||||
_vsyncAction = nullptr;
|
||||
}
|
||||
|
@ -72,22 +69,20 @@ void Basic2DWindowOpenGLDisplayPlugin::activate() {
|
|||
updateFramerate();
|
||||
}
|
||||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::deactivate() {
|
||||
WindowOpenGLDisplayPlugin::deactivate();
|
||||
}
|
||||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {
|
||||
void Basic2DWindowOpenGLDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) {
|
||||
if (_vsyncAction) {
|
||||
bool wantVsync = _vsyncAction->isChecked();
|
||||
bool vsyncEnabed = isVsyncEnabled();
|
||||
if (vsyncEnabed ^ wantVsync) {
|
||||
enableVsync(wantVsync);
|
||||
}
|
||||
_wantVsync = _vsyncAction->isChecked();
|
||||
}
|
||||
|
||||
WindowOpenGLDisplayPlugin::display(sceneTexture, sceneSize);
|
||||
WindowOpenGLDisplayPlugin::submitSceneTexture(frameIndex, sceneTexture, sceneSize);
|
||||
}
|
||||
|
||||
void Basic2DWindowOpenGLDisplayPlugin::internalPresent() {
|
||||
if (_wantVsync != isVsyncEnabled()) {
|
||||
enableVsync(_wantVsync);
|
||||
}
|
||||
WindowOpenGLDisplayPlugin::internalPresent();
|
||||
}
|
||||
|
||||
int Basic2DWindowOpenGLDisplayPlugin::getDesiredInterval() const {
|
||||
static const int THROTTLED_PAINT_TIMER_DELAY_MS = MSECS_PER_SECOND / 15;
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "WindowOpenGLDisplayPlugin.h"
|
||||
|
||||
class QScreen;
|
||||
class QAction;
|
||||
|
||||
class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -17,9 +19,10 @@ public:
|
|||
virtual const QString & getName() const override;
|
||||
|
||||
virtual void activate() override;
|
||||
virtual void deactivate() override;
|
||||
|
||||
virtual void display(GLuint sceneTexture, const glm::uvec2& sceneSize) override;
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
|
||||
|
||||
virtual void internalPresent() override;
|
||||
|
||||
virtual bool isThrottled() const override;
|
||||
|
||||
|
@ -31,6 +34,9 @@ private:
|
|||
void updateFramerate();
|
||||
static const QString NAME;
|
||||
QScreen* getFullscreenTarget();
|
||||
uint32_t _framerateTarget{ 0 };
|
||||
std::vector<QAction*> _framerateActions;
|
||||
QAction* _vsyncAction { nullptr };
|
||||
uint32_t _framerateTarget { 0 };
|
||||
int _fullscreenTarget{ -1 };
|
||||
bool _wantVsync { true };
|
||||
};
|
||||
|
|
|
@ -25,8 +25,8 @@ const QString& DisplayPlugin::MENU_PATH() {
|
|||
DisplayPluginList getDisplayPlugins() {
|
||||
DisplayPlugin* PLUGIN_POOL[] = {
|
||||
new Basic2DWindowOpenGLDisplayPlugin(),
|
||||
#ifdef DEBUG
|
||||
new NullDisplayPlugin(),
|
||||
#ifdef DEBUG
|
||||
#endif
|
||||
|
||||
// Stereo modes
|
||||
|
@ -37,10 +37,10 @@ DisplayPluginList getDisplayPlugins() {
|
|||
new InterleavedStereoDisplayPlugin(),
|
||||
|
||||
// HMDs
|
||||
#ifdef Q_OS_WIN
|
||||
// SteamVR SDK
|
||||
new OpenVrDisplayPlugin(),
|
||||
#endif
|
||||
//#ifdef Q_OS_WIN
|
||||
// // SteamVR SDK
|
||||
// new OpenVrDisplayPlugin(),
|
||||
//#endif
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
//
|
||||
#include "NullDisplayPlugin.h"
|
||||
|
||||
#include <QtGui/QImage>
|
||||
#include <plugins/PluginContainer.h>
|
||||
|
||||
const QString NullDisplayPlugin::NAME("NullDisplayPlugin");
|
||||
|
||||
const QString & NullDisplayPlugin::getName() const {
|
||||
|
@ -23,8 +26,16 @@ bool NullDisplayPlugin::hasFocus() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void NullDisplayPlugin::preRender() {}
|
||||
void NullDisplayPlugin::preDisplay() {}
|
||||
void NullDisplayPlugin::display(uint32_t sceneTexture, const glm::uvec2& sceneSize) {}
|
||||
void NullDisplayPlugin::finishFrame() {}
|
||||
void NullDisplayPlugin::submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) {
|
||||
_container->releaseSceneTexture(sceneTexture);
|
||||
}
|
||||
|
||||
void NullDisplayPlugin::submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) {
|
||||
_container->releaseOverlayTexture(overlayTexture);
|
||||
}
|
||||
|
||||
void NullDisplayPlugin::stop() {}
|
||||
|
||||
QImage NullDisplayPlugin::getScreenshot() const {
|
||||
return QImage();
|
||||
}
|
||||
|
|
|
@ -19,11 +19,9 @@ public:
|
|||
|
||||
virtual glm::uvec2 getRecommendedRenderSize() const override;
|
||||
virtual bool hasFocus() const override;
|
||||
virtual void preRender() override;
|
||||
virtual void preDisplay() override;
|
||||
virtual void display(uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
|
||||
virtual void finishFrame() override;
|
||||
|
||||
virtual void submitSceneTexture(uint32_t frameIndex, uint32_t sceneTexture, const glm::uvec2& sceneSize) override;
|
||||
virtual void submitOverlayTexture(uint32_t overlayTexture, const glm::uvec2& overlaySize) override;
|
||||
virtual QImage getScreenshot() const override;
|
||||
private:
|
||||
static const QString NAME;
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue