Merge branch 'master' of https://github.com/highfidelity/hifi into baseball

This commit is contained in:
Atlante45 2015-12-04 15:18:25 -08:00
commit 1d498af76e
276 changed files with 6560 additions and 4785 deletions

View file

@ -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

View 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;
}

View 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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}

View file

@ -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; }

View file

@ -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...

View file

@ -17,7 +17,6 @@
#include <atomic>
#include <GenericThread.h>
#include <OctreeElementBag.h>
#include "OctreeQueryNode.h"

View file

@ -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) {

View file

@ -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

View file

@ -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 ()

View file

@ -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 ()

View file

@ -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)

View file

@ -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)

View 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;
})();

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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();

View file

@ -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';
}
}
}

View file

@ -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) {

View file

@ -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);
}
}
}

View file

@ -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));
}

View file

@ -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]);

View file

@ -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">

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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
}
})
});

View file

@ -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() {

View file

@ -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);
},

View file

@ -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);

View 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);

View file

@ -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")

View file

@ -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

View file

@ -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.

View file

@ -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" }

View file

@ -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;

View file

@ -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();
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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;
};

View 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);
}

View 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

View file

@ -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,

View file

@ -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";

View file

@ -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
}

View file

@ -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

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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;

View file

@ -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);

View file

@ -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; }

View file

@ -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();

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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.

View file

@ -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 {

View file

@ -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
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -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),

View file

@ -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);

View file

@ -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;
}

View file

@ -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());
}

View file

@ -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());

View file

@ -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();

View file

@ -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);

View file

@ -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));

View file

@ -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">

View file

@ -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);
}

View file

@ -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)

View file

@ -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() {

View file

@ -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);

View file

@ -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();
}

View file

@ -15,6 +15,8 @@
#include <QtScript/QScriptContext>
#include <QtScript/QScriptEngine>
#include "AudioReverb.h"
class AudioEffectOptions : public QObject {
Q_OBJECT

File diff suppressed because it is too large Load diff

View 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

View file

@ -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

View file

@ -12,7 +12,7 @@
#ifndef hifi_AudioSRC_h
#define hifi_AudioSRC_h
#include "stdint.h"
#include <stdint.h>
class AudioSRC {

View file

@ -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);
}

View file

@ -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;

View file

@ -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) {

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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 };

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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 };
};

View file

@ -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
};

View file

@ -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();
}

View file

@ -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