mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 11:29:50 +02:00
Merge branch 'master' into ratsteer
This commit is contained in:
commit
1190f1b6cc
40 changed files with 571 additions and 193 deletions
13
BUILD_OSX.md
13
BUILD_OSX.md
|
@ -7,11 +7,18 @@ Please read the [general build guide](BUILD.md) for information on dependencies
|
||||||
|
|
||||||
We no longer require install of qt5 via our [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas). Versions of Qt that are 5.5.x and above provide a mechanism to disable the wireless scanning we previously had a custom patch for.
|
We no longer require install of qt5 via our [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas). Versions of Qt that are 5.5.x and above provide a mechanism to disable the wireless scanning we previously had a custom patch for.
|
||||||
|
|
||||||
###Qt
|
###OpenSSL and Qt
|
||||||
|
|
||||||
Assuming you've installed Qt 5 using the homebrew instructions above, you'll need to set QT_CMAKE_PREFIX_PATH so CMake can find your installation of Qt. For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows.
|
Assuming you've installed OpenSSL or Qt 5 using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR and QT_CMAKE_PREFIX_PATH so CMake can find your installations.
|
||||||
|
For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR:
|
||||||
|
|
||||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake
|
export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2d_1
|
||||||
|
|
||||||
|
For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows.
|
||||||
|
|
||||||
|
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1_2/lib/cmake
|
||||||
|
|
||||||
|
Not that these use the versions from homebrew formulae at the time of this writing, and the version in the path will likely change.
|
||||||
|
|
||||||
###Xcode
|
###Xcode
|
||||||
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.
|
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.
|
||||||
|
|
|
@ -67,7 +67,7 @@ Agent::Agent(NLPacket& packet) :
|
||||||
DependencyManager::set<RecordingScriptingInterface>();
|
DependencyManager::set<RecordingScriptingInterface>();
|
||||||
|
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
|
|
||||||
packetReceiver.registerListenerForTypes(
|
packetReceiver.registerListenerForTypes(
|
||||||
{ PacketType::MixedAudio, PacketType::SilentAudioFrame },
|
{ PacketType::MixedAudio, PacketType::SilentAudioFrame },
|
||||||
this, "handleAudioPacket");
|
this, "handleAudioPacket");
|
||||||
|
@ -86,7 +86,7 @@ void Agent::handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointe
|
||||||
if (packet->getPayloadSize() > statsMessageLength) {
|
if (packet->getPayloadSize() > statsMessageLength) {
|
||||||
// pull out the piggybacked packet and create a new QSharedPointer<NLPacket> for it
|
// pull out the piggybacked packet and create a new QSharedPointer<NLPacket> for it
|
||||||
int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength;
|
int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength;
|
||||||
|
|
||||||
auto buffer = std::unique_ptr<char[]>(new char[piggyBackedSizeWithHeader]);
|
auto buffer = std::unique_ptr<char[]>(new char[piggyBackedSizeWithHeader]);
|
||||||
memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader);
|
memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader);
|
||||||
|
|
||||||
|
@ -126,11 +126,11 @@ void Agent::handleAudioPacket(QSharedPointer<NLPacket> packet) {
|
||||||
const QString AGENT_LOGGING_NAME = "agent";
|
const QString AGENT_LOGGING_NAME = "agent";
|
||||||
|
|
||||||
void Agent::run() {
|
void Agent::run() {
|
||||||
|
|
||||||
// make sure we request our script once the agent connects to the domain
|
// make sure we request our script once the agent connects to the domain
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript);
|
connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript);
|
||||||
|
|
||||||
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
|
ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent);
|
||||||
|
|
||||||
// Setup MessagesClient
|
// Setup MessagesClient
|
||||||
|
@ -140,7 +140,7 @@ void Agent::run() {
|
||||||
messagesClient->moveToThread(messagesThread);
|
messagesClient->moveToThread(messagesThread);
|
||||||
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
|
connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);
|
||||||
messagesThread->start();
|
messagesThread->start();
|
||||||
|
|
||||||
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
||||||
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer
|
NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::EntityServer, NodeType::MessagesMixer, NodeType::AssetServer
|
||||||
});
|
});
|
||||||
|
@ -149,7 +149,7 @@ void Agent::run() {
|
||||||
void Agent::requestScript() {
|
void Agent::requestScript() {
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
disconnect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript);
|
disconnect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, this, &Agent::requestScript);
|
||||||
|
|
||||||
// figure out the URL for the script for this agent assignment
|
// figure out the URL for the script for this agent assignment
|
||||||
QUrl scriptURL;
|
QUrl scriptURL;
|
||||||
if (_payload.isEmpty()) {
|
if (_payload.isEmpty()) {
|
||||||
|
@ -160,24 +160,24 @@ void Agent::requestScript() {
|
||||||
} else {
|
} else {
|
||||||
scriptURL = QUrl(_payload);
|
scriptURL = QUrl(_payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup a network access manager and
|
// setup a network access manager and
|
||||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
|
|
||||||
QNetworkDiskCache* cache = new QNetworkDiskCache();
|
QNetworkDiskCache* cache = new QNetworkDiskCache();
|
||||||
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||||
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache");
|
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "agentCache");
|
||||||
networkAccessManager.setCache(cache);
|
networkAccessManager.setCache(cache);
|
||||||
|
|
||||||
QNetworkRequest networkRequest = QNetworkRequest(scriptURL);
|
QNetworkRequest networkRequest = QNetworkRequest(scriptURL);
|
||||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||||
|
|
||||||
// setup a timeout for script request
|
// setup a timeout for script request
|
||||||
static const int SCRIPT_TIMEOUT_MS = 10000;
|
static const int SCRIPT_TIMEOUT_MS = 10000;
|
||||||
_scriptRequestTimeout = new QTimer(this);
|
_scriptRequestTimeout = new QTimer(this);
|
||||||
connect(_scriptRequestTimeout, &QTimer::timeout, this, &Agent::scriptRequestFinished);
|
connect(_scriptRequestTimeout, &QTimer::timeout, this, &Agent::scriptRequestFinished);
|
||||||
_scriptRequestTimeout->start(SCRIPT_TIMEOUT_MS);
|
_scriptRequestTimeout->start(SCRIPT_TIMEOUT_MS);
|
||||||
|
|
||||||
qDebug() << "Downloading script at" << scriptURL.toString();
|
qDebug() << "Downloading script at" << scriptURL.toString();
|
||||||
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
||||||
connect(reply, &QNetworkReply::finished, this, &Agent::scriptRequestFinished);
|
connect(reply, &QNetworkReply::finished, this, &Agent::scriptRequestFinished);
|
||||||
|
@ -187,11 +187,11 @@ void Agent::scriptRequestFinished() {
|
||||||
auto reply = qobject_cast<QNetworkReply*>(sender());
|
auto reply = qobject_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
_scriptRequestTimeout->stop();
|
_scriptRequestTimeout->stop();
|
||||||
|
|
||||||
if (reply && reply->error() == QNetworkReply::NoError) {
|
if (reply && reply->error() == QNetworkReply::NoError) {
|
||||||
_scriptContents = reply->readAll();
|
_scriptContents = reply->readAll();
|
||||||
qDebug() << "Downloaded script:" << _scriptContents;
|
qDebug() << "Downloaded script:" << _scriptContents;
|
||||||
|
|
||||||
// we could just call executeScript directly - we use a QueuedConnection to allow scriptRequestFinished
|
// we could just call executeScript directly - we use a QueuedConnection to allow scriptRequestFinished
|
||||||
// to return before calling executeScript
|
// to return before calling executeScript
|
||||||
QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(this, "executeScript", Qt::QueuedConnection);
|
||||||
|
@ -202,38 +202,38 @@ void Agent::scriptRequestFinished() {
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Failed to download script - request timed out. Bailing on assignment.";
|
qDebug() << "Failed to download script - request timed out. Bailing on assignment.";
|
||||||
}
|
}
|
||||||
|
|
||||||
setFinished(true);
|
setFinished(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Agent::executeScript() {
|
void Agent::executeScript() {
|
||||||
_scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(_scriptContents, _payload));
|
_scriptEngine = std::unique_ptr<ScriptEngine>(new ScriptEngine(_scriptContents, _payload));
|
||||||
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
|
_scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do
|
||||||
|
|
||||||
// setup an Avatar for the script to use
|
// setup an Avatar for the script to use
|
||||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||||
connect(_scriptEngine.get(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection);
|
connect(_scriptEngine.get(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection);
|
||||||
scriptedAvatar->setForceFaceTrackerConnected(true);
|
scriptedAvatar->setForceFaceTrackerConnected(true);
|
||||||
|
|
||||||
// call model URL setters with empty URLs so our avatar, if user, will have the default models
|
// call model URL setters with empty URLs so our avatar, if user, will have the default models
|
||||||
scriptedAvatar->setFaceModelURL(QUrl());
|
scriptedAvatar->setFaceModelURL(QUrl());
|
||||||
scriptedAvatar->setSkeletonModelURL(QUrl());
|
scriptedAvatar->setSkeletonModelURL(QUrl());
|
||||||
// give this AvatarData object to the script engine
|
// give this AvatarData object to the script engine
|
||||||
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());
|
_scriptEngine->registerGlobalObject("Avatar", scriptedAvatar.data());
|
||||||
|
|
||||||
|
|
||||||
using namespace recording;
|
using namespace recording;
|
||||||
static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME);
|
static const FrameType AVATAR_FRAME_TYPE = Frame::registerFrameType(AvatarData::FRAME_NAME);
|
||||||
// FIXME how to deal with driving multiple avatars locally?
|
// FIXME how to deal with driving multiple avatars locally?
|
||||||
Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) {
|
Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [this, scriptedAvatar](Frame::ConstPointer frame) {
|
||||||
AvatarData::fromFrame(frame->data, *scriptedAvatar);
|
AvatarData::fromFrame(frame->data, *scriptedAvatar);
|
||||||
});
|
});
|
||||||
|
|
||||||
using namespace recording;
|
using namespace recording;
|
||||||
static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::AUDIO_FRAME_NAME);
|
static const FrameType AUDIO_FRAME_TYPE = Frame::registerFrameType(AudioConstants::getAudioFrameName());
|
||||||
Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) {
|
Frame::registerFrameHandler(AUDIO_FRAME_TYPE, [this, &scriptedAvatar](Frame::ConstPointer frame) {
|
||||||
const QByteArray& audio = frame->data;
|
const QByteArray& audio = frame->data;
|
||||||
static quint16 audioSequenceNumber{ 0 };
|
static quint16 audioSequenceNumber{ 0 };
|
||||||
|
@ -242,49 +242,49 @@ void Agent::executeScript() {
|
||||||
audioTransform.setRotation(scriptedAvatar->getOrientation());
|
audioTransform.setRotation(scriptedAvatar->getOrientation());
|
||||||
AbstractAudioInterface::emitAudioPacket(audio.data(), audio.size(), audioSequenceNumber, audioTransform, PacketType::MicrophoneAudioNoEcho);
|
AbstractAudioInterface::emitAudioPacket(audio.data(), audio.size(), audioSequenceNumber, audioTransform, PacketType::MicrophoneAudioNoEcho);
|
||||||
});
|
});
|
||||||
|
|
||||||
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
|
auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
|
||||||
_scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data());
|
_scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data());
|
||||||
|
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket");
|
packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket");
|
||||||
packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar");
|
packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar");
|
||||||
packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket");
|
packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket");
|
||||||
packetReceiver.registerListener(PacketType::AvatarBillboard, avatarHashMap.data(), "processAvatarBillboardPacket");
|
packetReceiver.registerListener(PacketType::AvatarBillboard, avatarHashMap.data(), "processAvatarBillboardPacket");
|
||||||
|
|
||||||
// register ourselves to the script engine
|
// register ourselves to the script engine
|
||||||
_scriptEngine->registerGlobalObject("Agent", this);
|
_scriptEngine->registerGlobalObject("Agent", this);
|
||||||
|
|
||||||
// FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why
|
// FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why
|
||||||
// viewers would need this called.
|
// viewers would need this called.
|
||||||
//_scriptEngine->init(); // must be done before we set up the viewers
|
//_scriptEngine->init(); // must be done before we set up the viewers
|
||||||
|
|
||||||
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
_scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||||
|
|
||||||
QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor);
|
QScriptValue webSocketServerConstructorValue = _scriptEngine->newFunction(WebSocketServerClass::constructor);
|
||||||
_scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
_scriptEngine->globalObject().setProperty("WebSocketServer", webSocketServerConstructorValue);
|
||||||
|
|
||||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||||
|
|
||||||
_scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer);
|
_scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer);
|
||||||
|
|
||||||
// we need to make sure that init has been called for our EntityScriptingInterface
|
// we need to make sure that init has been called for our EntityScriptingInterface
|
||||||
// so that it actually has a jurisdiction listener when we ask it for it next
|
// so that it actually has a jurisdiction listener when we ask it for it next
|
||||||
entityScriptingInterface->init();
|
entityScriptingInterface->init();
|
||||||
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
|
_entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener());
|
||||||
|
|
||||||
_entityViewer.init();
|
_entityViewer.init();
|
||||||
|
|
||||||
entityScriptingInterface->setEntityTree(_entityViewer.getTree());
|
entityScriptingInterface->setEntityTree(_entityViewer.getTree());
|
||||||
|
|
||||||
// wire up our additional agent related processing to the update signal
|
// wire up our additional agent related processing to the update signal
|
||||||
QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio);
|
QObject::connect(_scriptEngine.get(), &ScriptEngine::update, this, &Agent::processAgentAvatarAndAudio);
|
||||||
|
|
||||||
_scriptEngine->run();
|
_scriptEngine->run();
|
||||||
|
|
||||||
Frame::clearFrameHandler(AUDIO_FRAME_TYPE);
|
Frame::clearFrameHandler(AUDIO_FRAME_TYPE);
|
||||||
Frame::clearFrameHandler(AVATAR_FRAME_TYPE);
|
Frame::clearFrameHandler(AVATAR_FRAME_TYPE);
|
||||||
|
|
||||||
setFinished(true);
|
setFinished(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,7 +317,7 @@ void Agent::setIsAvatar(bool isAvatar) {
|
||||||
delete _avatarIdentityTimer;
|
delete _avatarIdentityTimer;
|
||||||
_avatarIdentityTimer = nullptr;
|
_avatarIdentityTimer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_avatarBillboardTimer) {
|
if (_avatarBillboardTimer) {
|
||||||
_avatarBillboardTimer->stop();
|
_avatarBillboardTimer->stop();
|
||||||
delete _avatarBillboardTimer;
|
delete _avatarBillboardTimer;
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
class Agent : public ThreadedAssignment {
|
class Agent : public ThreadedAssignment {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
|
Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar)
|
||||||
Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound)
|
Q_PROPERTY(bool isPlayingAvatarSound READ isPlayingAvatarSound)
|
||||||
Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream)
|
Q_PROPERTY(bool isListeningToAudioStream READ isListeningToAudioStream WRITE setIsListeningToAudioStream)
|
||||||
|
@ -40,7 +40,7 @@ class Agent : public ThreadedAssignment {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Agent(NLPacket& packet);
|
Agent(NLPacket& packet);
|
||||||
|
|
||||||
void setIsAvatar(bool isAvatar);
|
void setIsAvatar(bool isAvatar);
|
||||||
bool isAvatar() const { return _isAvatar; }
|
bool isAvatar() const { return _isAvatar; }
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ public:
|
||||||
QUuid getSessionUUID() const;
|
QUuid getSessionUUID() const;
|
||||||
|
|
||||||
virtual void aboutToFinish();
|
virtual void aboutToFinish();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void run();
|
void run();
|
||||||
void playAvatarSound(Sound* avatarSound) { setAvatarSound(avatarSound); }
|
void playAvatarSound(Sound* avatarSound) { setAvatarSound(avatarSound); }
|
||||||
|
@ -62,7 +62,7 @@ private slots:
|
||||||
void requestScript();
|
void requestScript();
|
||||||
void scriptRequestFinished();
|
void scriptRequestFinished();
|
||||||
void executeScript();
|
void executeScript();
|
||||||
|
|
||||||
void handleAudioPacket(QSharedPointer<NLPacket> packet);
|
void handleAudioPacket(QSharedPointer<NLPacket> packet);
|
||||||
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
void handleOctreePacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||||
void handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
void handleJurisdictionPacket(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
|
||||||
|
@ -72,7 +72,7 @@ private:
|
||||||
std::unique_ptr<ScriptEngine> _scriptEngine;
|
std::unique_ptr<ScriptEngine> _scriptEngine;
|
||||||
EntityEditPacketSender _entityEditSender;
|
EntityEditPacketSender _entityEditSender;
|
||||||
EntityTreeHeadlessViewer _entityViewer;
|
EntityTreeHeadlessViewer _entityViewer;
|
||||||
|
|
||||||
MixedAudioStream _receivedAudioStream;
|
MixedAudioStream _receivedAudioStream;
|
||||||
float _lastReceivedAudioLoudness;
|
float _lastReceivedAudioLoudness;
|
||||||
|
|
||||||
|
|
10
cmake/externals/bullet/CMakeLists.txt
vendored
10
cmake/externals/bullet/CMakeLists.txt
vendored
|
@ -18,8 +18,8 @@ if (WIN32)
|
||||||
ExternalProject_Add(
|
ExternalProject_Add(
|
||||||
${EXTERNAL_NAME}
|
${EXTERNAL_NAME}
|
||||||
# URL https://bullet.googlecode.com/files/bullet-2.82-r2704.zip
|
# URL https://bullet.googlecode.com/files/bullet-2.82-r2704.zip
|
||||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-r2704.zip
|
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.zip
|
||||||
URL_MD5 f5e8914fc9064ad32e0d62d19d33d977
|
URL_MD5 d95b07eb120de7dd7786361c0b5a8d9f
|
||||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 -DUSE_DX11=0
|
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0 -DUSE_DX11=0
|
||||||
LOG_DOWNLOAD 1
|
LOG_DOWNLOAD 1
|
||||||
LOG_CONFIGURE 1
|
LOG_CONFIGURE 1
|
||||||
|
@ -30,8 +30,8 @@ else ()
|
||||||
ExternalProject_Add(
|
ExternalProject_Add(
|
||||||
${EXTERNAL_NAME}
|
${EXTERNAL_NAME}
|
||||||
#URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz
|
#URL http://bullet.googlecode.com/files/bullet-2.82-r2704.tgz
|
||||||
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-r2704.tgz
|
URL http://hifi-public.s3.amazonaws.com/dependencies/bullet-2.82-ccd-fix.tgz
|
||||||
URL_MD5 70b3c8d202dee91a0854b4cbc88173e8
|
URL_MD5 fb140a4983b4109aa1c825a162aa8d64
|
||||||
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0
|
CMAKE_ARGS ${PLATFORM_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DBUILD_EXTRAS=0 -DINSTALL_LIBS=1 -DBUILD_DEMOS=0 -DUSE_GLUT=0
|
||||||
LOG_DOWNLOAD 1
|
LOG_DOWNLOAD 1
|
||||||
LOG_CONFIGURE 1
|
LOG_CONFIGURE 1
|
||||||
|
@ -80,4 +80,4 @@ endif ()
|
||||||
|
|
||||||
if (DEFINED ${EXTERNAL_NAME_UPPER}_DYNAMICS_LIBRARY_RELEASE)
|
if (DEFINED ${EXTERNAL_NAME_UPPER}_DYNAMICS_LIBRARY_RELEASE)
|
||||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/bullet CACHE PATH "Path to bullet include directory")
|
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/bullet CACHE PATH "Path to bullet include directory")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
4
cmake/externals/quazip/CMakeLists.txt
vendored
4
cmake/externals/quazip/CMakeLists.txt
vendored
|
@ -3,12 +3,14 @@ string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||||
cmake_policy(SET CMP0046 OLD)
|
cmake_policy(SET CMP0046 OLD)
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
|
||||||
|
string(REPLACE \\ / QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
|
||||||
ExternalProject_Add(
|
ExternalProject_Add(
|
||||||
${EXTERNAL_NAME}
|
${EXTERNAL_NAME}
|
||||||
URL http://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.6.2.zip
|
URL http://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.6.2.zip
|
||||||
URL_MD5 514851970f1a14d815bdc3ad6267af4d
|
URL_MD5 514851970f1a14d815bdc3ad6267af4d
|
||||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=$ENV{QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=<INSTALL_DIR>/lib -DZLIB_ROOT=${ZLIB_ROOT}
|
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=<INSTALL_DIR>/lib -DZLIB_ROOT=${ZLIB_ROOT}
|
||||||
LOG_DOWNLOAD 1
|
LOG_DOWNLOAD 1
|
||||||
LOG_CONFIGURE 1
|
LOG_CONFIGURE 1
|
||||||
LOG_BUILD 1
|
LOG_BUILD 1
|
||||||
|
|
|
@ -21,7 +21,7 @@ macro(LINK_HIFI_LIBRARIES)
|
||||||
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
|
include_directories("${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src")
|
||||||
|
|
||||||
add_dependencies(${TARGET_NAME} ${HIFI_LIBRARY})
|
add_dependencies(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||||
|
|
||||||
# link the actual library - it is static so don't bubble it up
|
# link the actual library - it is static so don't bubble it up
|
||||||
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
|
target_link_libraries(${TARGET_NAME} ${HIFI_LIBRARY})
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,13 @@ macro(SETUP_HIFI_PROJECT)
|
||||||
# find these Qt modules and link them to our own target
|
# find these Qt modules and link them to our own target
|
||||||
find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED)
|
find_package(Qt5 COMPONENTS ${${TARGET_NAME}_DEPENDENCY_QT_MODULES} REQUIRED)
|
||||||
|
|
||||||
|
# disable /OPT:REF and /OPT:ICF for the Debug builds
|
||||||
|
# This will prevent the following linker warnings
|
||||||
|
# LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification
|
||||||
|
if (WIN32)
|
||||||
|
set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG "/OPT:NOREF /OPT:NOICF")
|
||||||
|
endif()
|
||||||
|
|
||||||
foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES})
|
foreach(QT_MODULE ${${TARGET_NAME}_DEPENDENCY_QT_MODULES})
|
||||||
target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
|
target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
|
@ -14,16 +14,17 @@
|
||||||
// An assignment client script that animates one avatar at random location within 'spread' meters of 'origin'.
|
// An assignment client script that animates one avatar at random location within 'spread' meters of 'origin'.
|
||||||
// In Domain Server Settings, go to scripts and give the url of this script. Press '+', and then 'Save and restart'.
|
// In Domain Server Settings, go to scripts and give the url of this script. Press '+', and then 'Save and restart'.
|
||||||
|
|
||||||
var origin = {x: 500, y: 502, z: 500};
|
var origin = {x: 500, y: 500, z: 500};
|
||||||
var spread = 10; // meters
|
var spread = 20; // meters
|
||||||
var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35};
|
var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35};
|
||||||
Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy
|
Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy
|
||||||
Avatar.displayName = "'Bot";
|
Avatar.displayName = "'Bot";
|
||||||
var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start.
|
var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start.
|
||||||
|
|
||||||
Agent.isAvatar = true;
|
Agent.isAvatar = true;
|
||||||
|
function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2.
|
||||||
Script.setTimeout(function () {
|
Script.setTimeout(function () {
|
||||||
Avatar.position = Vec3.sum(origin, {x: Math.random() * spread, y: 0, z: Math.random() * spread});
|
Avatar.position = Vec3.sum(origin, {x: coord(), y: 0, z: coord()});
|
||||||
print("Starting at", JSON.stringify(Avatar.position));
|
print("Starting at", JSON.stringify(Avatar.position));
|
||||||
Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame);
|
Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame);
|
||||||
}, millisecondsToWaitBeforeStarting);
|
}, millisecondsToWaitBeforeStarting);
|
||||||
|
|
|
@ -37,6 +37,7 @@ var BUMPER_ON_VALUE = 0.5;
|
||||||
var DISTANCE_HOLDING_RADIUS_FACTOR = 5; // multiplied by distance between hand and object
|
var DISTANCE_HOLDING_RADIUS_FACTOR = 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_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 DISTANCE_HOLDING_ROTATION_EXAGGERATION_FACTOR = 2.0; // object rotates this much more than hand did
|
||||||
|
|
||||||
var NO_INTERSECT_COLOR = {
|
var NO_INTERSECT_COLOR = {
|
||||||
red: 10,
|
red: 10,
|
||||||
green: 10,
|
green: 10,
|
||||||
|
@ -86,6 +87,7 @@ var ZERO_VEC = {
|
||||||
y: 0,
|
y: 0,
|
||||||
z: 0
|
z: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}";
|
var NULL_ACTION_ID = "{00000000-0000-0000-000000000000}";
|
||||||
var MSEC_PER_SEC = 1000.0;
|
var MSEC_PER_SEC = 1000.0;
|
||||||
|
|
||||||
|
@ -95,7 +97,8 @@ var ACTION_TTL = 15; // seconds
|
||||||
var ACTION_TTL_REFRESH = 5;
|
var ACTION_TTL_REFRESH = 5;
|
||||||
var PICKS_PER_SECOND_PER_HAND = 5;
|
var PICKS_PER_SECOND_PER_HAND = 5;
|
||||||
var MSECS_PER_SEC = 1000.0;
|
var MSECS_PER_SEC = 1000.0;
|
||||||
var GRABBABLE_PROPERTIES = ["position",
|
var GRABBABLE_PROPERTIES = [
|
||||||
|
"position",
|
||||||
"rotation",
|
"rotation",
|
||||||
"gravity",
|
"gravity",
|
||||||
"ignoreForCollisions",
|
"ignoreForCollisions",
|
||||||
|
@ -104,7 +107,6 @@ var GRABBABLE_PROPERTIES = ["position",
|
||||||
"name"
|
"name"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js
|
var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js
|
||||||
var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js
|
var GRAB_USER_DATA_KEY = "grabKey"; // shared with grab.js
|
||||||
|
|
||||||
|
@ -113,8 +115,6 @@ var DEFAULT_GRABBABLE_DATA = {
|
||||||
invertSolidWhileHeld: false
|
invertSolidWhileHeld: false
|
||||||
};
|
};
|
||||||
|
|
||||||
var disabledHand = 'none';
|
|
||||||
|
|
||||||
|
|
||||||
// states for the state machine
|
// states for the state machine
|
||||||
var STATE_OFF = 0;
|
var STATE_OFF = 0;
|
||||||
|
@ -307,7 +307,14 @@ function MyController(hand) {
|
||||||
position: closePoint,
|
position: closePoint,
|
||||||
linePoints: [ZERO_VEC, farPoint],
|
linePoints: [ZERO_VEC, farPoint],
|
||||||
color: color,
|
color: color,
|
||||||
lifetime: 0.1
|
lifetime: 0.1,
|
||||||
|
collisionsWillMove: false,
|
||||||
|
ignoreForCollisions: true,
|
||||||
|
userData: JSON.stringify({
|
||||||
|
grabbableKey: {
|
||||||
|
grabbable: false
|
||||||
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,7 +329,14 @@ function MyController(hand) {
|
||||||
position: closePoint,
|
position: closePoint,
|
||||||
linePoints: [ZERO_VEC, farPoint],
|
linePoints: [ZERO_VEC, farPoint],
|
||||||
color: color,
|
color: color,
|
||||||
lifetime: LIFETIME
|
lifetime: LIFETIME,
|
||||||
|
collisionsWillMove: false,
|
||||||
|
ignoreForCollisions: true,
|
||||||
|
userData: JSON.stringify({
|
||||||
|
grabbableKey: {
|
||||||
|
grabbable: false
|
||||||
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var age = Entities.getEntityProperties(this.pointer, "age").age;
|
var age = Entities.getEntityProperties(this.pointer, "age").age;
|
||||||
|
@ -396,11 +410,6 @@ function MyController(hand) {
|
||||||
this.search = function() {
|
this.search = function() {
|
||||||
this.grabbedEntity = null;
|
this.grabbedEntity = null;
|
||||||
|
|
||||||
// if this hand is the one that's disabled, we don't want to search for anything at all
|
|
||||||
if (this.hand === disabledHand) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) {
|
if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) {
|
||||||
this.setState(STATE_RELEASE);
|
this.setState(STATE_RELEASE);
|
||||||
return;
|
return;
|
||||||
|
@ -445,17 +454,7 @@ function MyController(hand) {
|
||||||
// the ray is intersecting something we can move.
|
// the ray is intersecting something we can move.
|
||||||
var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection);
|
var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection);
|
||||||
|
|
||||||
//this code will disabled the beam for the opposite hand of the one that grabbed it if the entity says so
|
|
||||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA);
|
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA);
|
||||||
if (grabbableData["turnOffOppositeBeam"]) {
|
|
||||||
if (this.hand === RIGHT_HAND) {
|
|
||||||
disabledHand = LEFT_HAND;
|
|
||||||
} else {
|
|
||||||
disabledHand = RIGHT_HAND;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
disabledHand = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intersection.properties.name == "Grab Debug Entity") {
|
if (intersection.properties.name == "Grab Debug Entity") {
|
||||||
continue;
|
continue;
|
||||||
|
@ -526,7 +525,14 @@ function MyController(hand) {
|
||||||
green: 255,
|
green: 255,
|
||||||
blue: 0
|
blue: 0
|
||||||
},
|
},
|
||||||
lifetime: 0.1
|
lifetime: 0.1,
|
||||||
|
collisionsWillMove: false,
|
||||||
|
ignoreForCollisions: true,
|
||||||
|
userData: JSON.stringify({
|
||||||
|
grabbableKey: {
|
||||||
|
grabbable: false
|
||||||
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,8 +546,7 @@ function MyController(hand) {
|
||||||
if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) {
|
if (typeof grabbableDataForCandidate.grabbable !== 'undefined' && !grabbableDataForCandidate.grabbable) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var propsForCandidate =
|
var propsForCandidate = Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES);
|
||||||
Entities.getEntityProperties(nearbyEntities[i], GRABBABLE_PROPERTIES);
|
|
||||||
|
|
||||||
if (propsForCandidate.type == 'Unknown') {
|
if (propsForCandidate.type == 'Unknown') {
|
||||||
continue;
|
continue;
|
||||||
|
@ -737,15 +742,8 @@ function MyController(hand) {
|
||||||
|
|
||||||
this.nearGrabbing = function() {
|
this.nearGrabbing = function() {
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
|
||||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||||
|
|
||||||
var turnOffOtherHand = grabbableData["turnOffOtherHand"];
|
|
||||||
if (turnOffOtherHand) {
|
|
||||||
//don't activate the second hand grab because the script is handling the second hand logic
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) {
|
if (this.state == STATE_NEAR_GRABBING && this.triggerSmoothedReleased()) {
|
||||||
this.setState(STATE_RELEASE);
|
this.setState(STATE_RELEASE);
|
||||||
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
|
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
|
||||||
|
@ -1094,10 +1092,6 @@ function MyController(hand) {
|
||||||
|
|
||||||
this.release = function() {
|
this.release = function() {
|
||||||
|
|
||||||
if (this.hand !== disabledHand) {
|
|
||||||
//release the disabled hand when we let go with the main one
|
|
||||||
disabledHand = 'none';
|
|
||||||
}
|
|
||||||
this.lineOff();
|
this.lineOff();
|
||||||
|
|
||||||
if (this.grabbedEntity !== null) {
|
if (this.grabbedEntity !== null) {
|
||||||
|
@ -1218,12 +1212,35 @@ mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress);
|
||||||
|
|
||||||
Controller.enableMapping(MAPPING_NAME);
|
Controller.enableMapping(MAPPING_NAME);
|
||||||
|
|
||||||
|
var handToDisable = 'none';
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
rightController.update();
|
if (handToDisable !== LEFT_HAND) {
|
||||||
leftController.update();
|
leftController.update();
|
||||||
|
}
|
||||||
|
if (handToDisable !== RIGHT_HAND) {
|
||||||
|
rightController.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Messages.messageReceived.connect(handleHandDisablerMessages);
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
rightController.cleanup();
|
rightController.cleanup();
|
||||||
leftController.cleanup();
|
leftController.cleanup();
|
||||||
|
|
|
@ -25,8 +25,6 @@ var LAST_FRAME = 15.0; // What is the number of the last frame we want to us
|
||||||
var SMOOTH_FACTOR = 0.75;
|
var SMOOTH_FACTOR = 0.75;
|
||||||
var MAX_FRAMES = 30.0;
|
var MAX_FRAMES = 30.0;
|
||||||
|
|
||||||
var LEFT_HAND_CLICK = Controller.findAction("LEFT_HAND_CLICK");
|
|
||||||
var RIGHT_HAND_CLICK = Controller.findAction("RIGHT_HAND_CLICK");
|
|
||||||
|
|
||||||
var CONTROLLER_DEAD_SPOT = 0.25;
|
var CONTROLLER_DEAD_SPOT = 0.25;
|
||||||
|
|
||||||
|
@ -45,8 +43,8 @@ function normalizeControllerValue(val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.update.connect(function(deltaTime) {
|
Script.update.connect(function(deltaTime) {
|
||||||
var leftTrigger = normalizeControllerValue(Controller.getActionValue(LEFT_HAND_CLICK));
|
var leftTrigger = normalizeControllerValue(Controller.getValue(Controller.Standard.LT));
|
||||||
var rightTrigger = normalizeControllerValue(Controller.getActionValue(RIGHT_HAND_CLICK));
|
var rightTrigger = normalizeControllerValue(Controller.getValue(Controller.Standard.RT));
|
||||||
|
|
||||||
// Average last few trigger values together for a bit of smoothing
|
// Average last few trigger values together for a bit of smoothing
|
||||||
var smoothLeftTrigger = leftTrigger * (1.0 - SMOOTH_FACTOR) + lastLeftTrigger * SMOOTH_FACTOR;
|
var smoothLeftTrigger = leftTrigger * (1.0 - SMOOTH_FACTOR) + lastLeftTrigger * SMOOTH_FACTOR;
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
overlay = null;
|
overlay = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
startRecording: function (entityID) {
|
startRecording: function () {
|
||||||
if (!isAvatarRecording) {
|
if (!isAvatarRecording) {
|
||||||
print("RECORDING STARTED");
|
print("RECORDING STARTED");
|
||||||
Messages.sendMessage(CLIENTS_TO_MASTER_CHANNEL, PARTICIPATING_MESSAGE); //tell to master that I'm participating
|
Messages.sendMessage(CLIENTS_TO_MASTER_CHANNEL, PARTICIPATING_MESSAGE); //tell to master that I'm participating
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
stopRecording: function (entityID) {
|
stopRecording: function () {
|
||||||
if (isAvatarRecording) {
|
if (isAvatarRecording) {
|
||||||
print("RECORDING ENDED");
|
print("RECORDING ENDED");
|
||||||
Recording.stopRecording();
|
Recording.stopRecording();
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
_this.stopRecording();
|
_this.stopRecording();
|
||||||
Messages.unsubscribe(MASTER_TO_CLIENTS_CHANNEL);
|
Messages.unsubscribe(MASTER_TO_CLIENTS_CHANNEL);
|
||||||
Messages.messageReceived.disconnect(receivingMessage);
|
Messages.messageReceived.disconnect(receivingMessage);
|
||||||
if(overlay !== null){
|
if (overlay !== null) {
|
||||||
Overlays.deleteOverlay(overlay);
|
Overlays.deleteOverlay(overlay);
|
||||||
overlay = null;
|
overlay = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,14 @@ var STOP_MESSAGE = "recordingEnded";
|
||||||
var PARTICIPATING_MESSAGE = "participatingToRecording";
|
var PARTICIPATING_MESSAGE = "participatingToRecording";
|
||||||
var TIMEOUT = 20;
|
var TIMEOUT = 20;
|
||||||
|
|
||||||
|
|
||||||
var toolBar = null;
|
var toolBar = null;
|
||||||
var recordIcon;
|
var recordIcon;
|
||||||
var isRecording = false;
|
var isRecording = false;
|
||||||
var performanceJSON = { "avatarClips" : [] };
|
var performanceJSON = { "avatarClips" : [] };
|
||||||
var responsesExpected = 0;
|
var responsesExpected = 0;
|
||||||
|
var readyToPrintInfo = false;
|
||||||
|
var performanceFileURL = null;
|
||||||
var waitingForPerformanceFile = true;
|
var waitingForPerformanceFile = true;
|
||||||
var totalWaitingTime = 0;
|
var totalWaitingTime = 0;
|
||||||
var extension = "txt";
|
var extension = "txt";
|
||||||
|
@ -71,9 +74,9 @@ function mousePressEvent(event) {
|
||||||
print("I'm the master. I want to start recording");
|
print("I'm the master. I want to start recording");
|
||||||
Messages.sendMessage(MASTER_TO_CLIENTS_CHANNEL, START_MESSAGE);
|
Messages.sendMessage(MASTER_TO_CLIENTS_CHANNEL, START_MESSAGE);
|
||||||
isRecording = true;
|
isRecording = true;
|
||||||
|
waitingForPerformanceFile = true;
|
||||||
} else {
|
} else {
|
||||||
print("I want to stop recording");
|
print("I want to stop recording");
|
||||||
waitingForPerformanceFile = true;
|
|
||||||
Script.update.connect(update);
|
Script.update.connect(update);
|
||||||
Messages.sendMessage(MASTER_TO_CLIENTS_CHANNEL, STOP_MESSAGE);
|
Messages.sendMessage(MASTER_TO_CLIENTS_CHANNEL, STOP_MESSAGE);
|
||||||
isRecording = false;
|
isRecording = false;
|
||||||
|
@ -108,29 +111,38 @@ function update(deltaTime) {
|
||||||
}
|
}
|
||||||
//clean things after upload performance file to asset
|
//clean things after upload performance file to asset
|
||||||
waitingForPerformanceFile = false;
|
waitingForPerformanceFile = false;
|
||||||
responsesExpected = 0;
|
|
||||||
totalWaitingTime = 0;
|
totalWaitingTime = 0;
|
||||||
Script.update.disconnect(update);
|
Script.update.disconnect(update);
|
||||||
performanceJSON = { "avatarClips" : [] };
|
|
||||||
}
|
}
|
||||||
|
} else if (readyToPrintInfo == true){
|
||||||
|
Window.prompt("Performance file and clips: ", getUtilityString());
|
||||||
|
responsesExpected = 0;
|
||||||
|
performanceJSON = { "avatarClips" : [] };
|
||||||
|
Script.update.disconnect(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getUtilityString() {
|
||||||
|
var resultString = "JSON:\n" + performanceFileURL + "\n" + responsesExpected + " avatar clips:\n";
|
||||||
|
var avatarClips = performanceJSON.avatarClips;
|
||||||
|
avatarClips.forEach(function(param) {
|
||||||
|
resultString += param + "\n";
|
||||||
|
});
|
||||||
|
return resultString;
|
||||||
|
}
|
||||||
|
|
||||||
function uploadFinished(url){
|
function uploadFinished(url){
|
||||||
//need to print somehow the url here this way the master can copy the url
|
//need to print somehow the url here this way the master can copy the url
|
||||||
print("PERFORMANCE FILE URL: " + url);
|
|
||||||
Assets.downloadData(url, function (data) {
|
|
||||||
printPerformanceJSON(JSON.parse(data));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function printPerformanceJSON(obj) {
|
|
||||||
print("some info:");
|
print("some info:");
|
||||||
print("downloaded performance file from asset and examinating its content...");
|
performanceFileURL = url;
|
||||||
var avatarClips = obj.avatarClips;
|
print("PERFORMANCE FILE URL: " + performanceFileURL);
|
||||||
|
print("number of clips obtained:" + responsesExpected);
|
||||||
|
var avatarClips = performanceJSON.avatarClips;
|
||||||
avatarClips.forEach(function(param) {
|
avatarClips.forEach(function(param) {
|
||||||
print("clip url obtained: " + param);
|
print("clip url obtained: " + param);
|
||||||
});
|
});
|
||||||
|
readyToPrintInfo = true;
|
||||||
|
Script.update.connect(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
|
|
|
@ -361,8 +361,8 @@ function update() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateControllerState() {
|
function updateControllerState() {
|
||||||
rightTriggerValue = Controller.getActionValue(rightHandClick);
|
rightTriggerValue = Controller.getValue(Controller.Standard.RT);
|
||||||
leftTriggerValue = Controller.getActionValue(leftHandClick);
|
leftTriggerValue =Controller.getValue(Controller.Standard.LT);
|
||||||
|
|
||||||
if (rightTriggerValue > TRIGGER_THRESHOLD && !swordHeld) {
|
if (rightTriggerValue > TRIGGER_THRESHOLD && !swordHeld) {
|
||||||
grabSword("right")
|
grabSword("right")
|
||||||
|
|
|
@ -159,7 +159,8 @@ function MyController(hand, triggerAction) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateControllerState = function() {
|
this.updateControllerState = function() {
|
||||||
this.triggerValue = Controller.getActionValue(this.triggerAction);
|
this.triggerValue = Controller.getValue(this.triggerAction);
|
||||||
|
|
||||||
if (this.triggerValue > TRIGGER_ON_VALUE && this.prevTriggerValue <= TRIGGER_ON_VALUE) {
|
if (this.triggerValue > TRIGGER_ON_VALUE && this.prevTriggerValue <= TRIGGER_ON_VALUE) {
|
||||||
this.squeeze();
|
this.squeeze();
|
||||||
} else if (this.triggerValue < TRIGGER_ON_VALUE && this.prevTriggerValue >= TRIGGER_ON_VALUE) {
|
} else if (this.triggerValue < TRIGGER_ON_VALUE && this.prevTriggerValue >= TRIGGER_ON_VALUE) {
|
||||||
|
@ -256,8 +257,8 @@ function MyController(hand, triggerAction) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK"));
|
var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT);
|
||||||
var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK"));
|
var leftController = new MyController(LEFT_HAND, Controller.Standard.LT);
|
||||||
|
|
||||||
Controller.actionEvent.connect(function(action, state) {
|
Controller.actionEvent.connect(function(action, state) {
|
||||||
if (state === 0) {
|
if (state === 0) {
|
||||||
|
|
|
@ -247,4 +247,4 @@ function cleanup() {
|
||||||
|
|
||||||
|
|
||||||
// Uncomment this line to delete whiteboard and all associated entity on script close
|
// Uncomment this line to delete whiteboard and all associated entity on script close
|
||||||
// Script.scriptEnding.connect(cleanup);
|
//Script.scriptEnding.connect(cleanup);
|
||||||
|
|
|
@ -12,10 +12,9 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
|
||||||
Script.include("https://hifi-public.s3.amazonaws.com/scripts/utilities.js");
|
Script.include("../../libraries/utils.js");
|
||||||
|
|
||||||
|
var scriptURL = Script.resolvePath('flashlight.js');
|
||||||
var scriptURL = Script.resolvePath('flashlight.js?123123');
|
|
||||||
|
|
||||||
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
var modelURL = "https://hifi-public.s3.amazonaws.com/models/props/flashlight.fbx";
|
||||||
|
|
||||||
|
|
|
@ -183,11 +183,14 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
changeLightWithTriggerPressure: function(flashLightHand) {
|
changeLightWithTriggerPressure: function(flashLightHand) {
|
||||||
var handClickString = flashLightHand + "_HAND_CLICK";
|
|
||||||
|
|
||||||
var handClick = Controller.findAction(handClickString);
|
if (flashLightHand === 'LEFT') {
|
||||||
|
this.triggerValue = Controller.getValue(Controller.Standard.LT);
|
||||||
|
}
|
||||||
|
if (flashLightHand === 'RIGHT') {
|
||||||
|
this.triggerValue = Controller.getValue(Controller.Standard.RT);
|
||||||
|
|
||||||
this.triggerValue = Controller.getActionValue(handClick);
|
}
|
||||||
|
|
||||||
if (this.triggerValue < DISABLE_LIGHT_THRESHOLD && this.lightOn === true) {
|
if (this.triggerValue < DISABLE_LIGHT_THRESHOLD && this.lightOn === true) {
|
||||||
this.turnLightOff();
|
this.turnLightOff();
|
||||||
|
@ -266,4 +269,4 @@
|
||||||
|
|
||||||
// entity scripts always need to return a newly constructed object of our type
|
// entity scripts always need to return a newly constructed object of our type
|
||||||
return new Flashlight();
|
return new Flashlight();
|
||||||
});
|
});
|
|
@ -100,6 +100,13 @@ else()
|
||||||
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# disable /OPT:REF and /OPT:ICF for the Debug builds
|
||||||
|
# This will prevent the following linker warnings
|
||||||
|
# LINK : warning LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification
|
||||||
|
if (WIN32)
|
||||||
|
set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG "/OPT:NOREF /OPT:NOICF")
|
||||||
|
endif()
|
||||||
|
|
||||||
# link required hifi libraries
|
# link required hifi libraries
|
||||||
link_hifi_libraries(shared octree environment gpu gl procedural model render
|
link_hifi_libraries(shared octree environment gpu gl procedural model render
|
||||||
recording fbx networking model-networking entities avatars
|
recording fbx networking model-networking entities avatars
|
||||||
|
|
|
@ -256,6 +256,12 @@ Item {
|
||||||
visible: root.expanded
|
visible: root.expanded
|
||||||
text: "LOD: " + root.lodStatus;
|
text: "LOD: " + root.lodStatus;
|
||||||
}
|
}
|
||||||
|
Text {
|
||||||
|
color: root.fontColor;
|
||||||
|
font.pixelSize: root.fontSize
|
||||||
|
visible: root.expanded
|
||||||
|
text: "Renderable avatars: " + root.avatarRenderableCount + " w/in " + root.avatarRenderDistance + "m";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,14 +454,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
audioIO->setOrientationGetter([this]{ return getMyAvatar()->getOrientationForAudio(); });
|
audioIO->setOrientationGetter([this]{ return getMyAvatar()->getOrientationForAudio(); });
|
||||||
|
|
||||||
audioIO->moveToThread(audioThread);
|
audioIO->moveToThread(audioThread);
|
||||||
recording::Frame::registerFrameHandler(AudioConstants::AUDIO_FRAME_NAME, [=](recording::Frame::ConstPointer frame) {
|
recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) {
|
||||||
audioIO->handleRecordedAudioInput(frame->data);
|
audioIO->handleRecordedAudioInput(frame->data);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(audioIO.data(), &AudioClient::inputReceived, [](const QByteArray& audio){
|
connect(audioIO.data(), &AudioClient::inputReceived, [](const QByteArray& audio){
|
||||||
static auto recorder = DependencyManager::get<recording::Recorder>();
|
static auto recorder = DependencyManager::get<recording::Recorder>();
|
||||||
if (recorder->isRecording()) {
|
if (recorder->isRecording()) {
|
||||||
static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AudioConstants::AUDIO_FRAME_NAME);
|
static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AudioConstants::getAudioFrameName());
|
||||||
recorder->recordFrame(AUDIO_FRAME_TYPE, audio);
|
recorder->recordFrame(AUDIO_FRAME_TYPE, audio);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1077,8 +1077,10 @@ void Application::paintGL() {
|
||||||
uint64_t now = usecTimestampNow();
|
uint64_t now = usecTimestampNow();
|
||||||
static uint64_t lastPaintBegin{ now };
|
static uint64_t lastPaintBegin{ now };
|
||||||
uint64_t diff = now - lastPaintBegin;
|
uint64_t diff = now - lastPaintBegin;
|
||||||
|
float instantaneousFps = 0.0f;
|
||||||
if (diff != 0) {
|
if (diff != 0) {
|
||||||
_framesPerSecond.updateAverage((float)USECS_PER_SECOND / (float)diff);
|
instantaneousFps = (float)USECS_PER_SECOND / (float)diff;
|
||||||
|
_framesPerSecond.updateAverage(_lastInstantaneousFps);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPaintBegin = now;
|
lastPaintBegin = now;
|
||||||
|
@ -1109,6 +1111,29 @@ void Application::paintGL() {
|
||||||
_inPaint = true;
|
_inPaint = true;
|
||||||
Finally clearFlagLambda([this] { _inPaint = false; });
|
Finally clearFlagLambda([this] { _inPaint = false; });
|
||||||
|
|
||||||
|
// Some LOD-like controls need to know a smoothly varying "potential" frame rate that doesn't
|
||||||
|
// include time waiting for vsync, and which can report a number above target if we've got the headroom.
|
||||||
|
// For example, if we're shooting for 75fps and paintWait is 3.3333ms (= 75% * 13.33ms), our deducedNonVSyncFps
|
||||||
|
// would be 100fps. In principle, a paintWait of zero would have deducedNonVSyncFps=75.
|
||||||
|
// Here we make a guess for deducedNonVSyncFps = 1 / deducedNonVSyncPeriod.
|
||||||
|
//
|
||||||
|
// Time between previous paintGL call and this one, which can vary not only with vSync misses, but also with QT timing.
|
||||||
|
// We're using this as a proxy for the time between vsync and displayEnd, below. (Not exact, but tends to be the same over time.)
|
||||||
|
// This is not the same as update(deltaTime), because the latter attempts to throttle to 60hz and also clamps to 1/4 second.
|
||||||
|
const float actualPeriod = diff / (float)USECS_PER_SECOND; // same as 1/instantaneousFps but easier for compiler to optimize
|
||||||
|
// Note that _lastPaintWait (stored at end of last call) is for the same paint cycle.
|
||||||
|
float deducedNonVSyncPeriod = actualPeriod - _lastPaintWait + _marginForDeducedFramePeriod; // plus a some non-zero time for machinery we can't measure
|
||||||
|
// We don't know how much time to allow for that, but if we went over the target period, we know it's at least the portion
|
||||||
|
// of paintWait up to the next vSync. This gives us enough of a penalty so that when actualPeriod crosses two cycles,
|
||||||
|
// the key part (and not an exagerated part) of _lastPaintWait is accounted for.
|
||||||
|
const float targetPeriod = getTargetFramePeriod();
|
||||||
|
if (_lastPaintWait > EPSILON && actualPeriod > targetPeriod) {
|
||||||
|
// Don't use C++ remainder(). It's authors are mathematically insane.
|
||||||
|
deducedNonVSyncPeriod += fmod(actualPeriod, _lastPaintWait);
|
||||||
|
}
|
||||||
|
_lastDeducedNonVSyncFps = 1.0f / deducedNonVSyncPeriod;
|
||||||
|
_lastInstantaneousFps = instantaneousFps;
|
||||||
|
|
||||||
auto displayPlugin = getActiveDisplayPlugin();
|
auto displayPlugin = getActiveDisplayPlugin();
|
||||||
displayPlugin->preRender();
|
displayPlugin->preRender();
|
||||||
_offscreenContext->makeCurrent();
|
_offscreenContext->makeCurrent();
|
||||||
|
@ -1355,6 +1380,7 @@ void Application::paintGL() {
|
||||||
// Ensure all operations from the previous context are complete before we try to read the fbo
|
// Ensure all operations from the previous context are complete before we try to read the fbo
|
||||||
glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
|
glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
|
||||||
glDeleteSync(sync);
|
glDeleteSync(sync);
|
||||||
|
uint64_t displayStart = usecTimestampNow();
|
||||||
|
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(__FUNCTION__ "/pluginDisplay");
|
PROFILE_RANGE(__FUNCTION__ "/pluginDisplay");
|
||||||
|
@ -1367,6 +1393,10 @@ void Application::paintGL() {
|
||||||
PerformanceTimer perfTimer("bufferSwap");
|
PerformanceTimer perfTimer("bufferSwap");
|
||||||
displayPlugin->finishFrame();
|
displayPlugin->finishFrame();
|
||||||
}
|
}
|
||||||
|
uint64_t displayEnd = usecTimestampNow();
|
||||||
|
const float displayPeriodUsec = (float)(displayEnd - displayStart); // usecs
|
||||||
|
_lastPaintWait = displayPeriodUsec / (float)USECS_PER_SECOND;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -159,6 +159,14 @@ public:
|
||||||
bool isForeground() const { return _isForeground; }
|
bool isForeground() const { return _isForeground; }
|
||||||
|
|
||||||
float getFps() const { return _fps; }
|
float getFps() const { return _fps; }
|
||||||
|
float const HMD_TARGET_FRAME_RATE = 75.0f;
|
||||||
|
float const DESKTOP_TARGET_FRAME_RATE = 60.0f;
|
||||||
|
float getTargetFrameRate() { return isHMDMode() ? HMD_TARGET_FRAME_RATE : DESKTOP_TARGET_FRAME_RATE; }
|
||||||
|
float getTargetFramePeriod() { return isHMDMode() ? 1.0f / HMD_TARGET_FRAME_RATE : 1.0f / DESKTOP_TARGET_FRAME_RATE; } // same as 1/getTargetFrameRate, but w/compile-time division
|
||||||
|
float getLastInstanteousFps() const { return _lastInstantaneousFps; }
|
||||||
|
float getLastPaintWait() const { return _lastPaintWait; };
|
||||||
|
float getLastDeducedNonVSyncFps() const { return _lastDeducedNonVSyncFps; }
|
||||||
|
void setMarginForDeducedFramePeriod(float newValue) { _marginForDeducedFramePeriod = newValue; }
|
||||||
|
|
||||||
float getFieldOfView() { return _fieldOfView.get(); }
|
float getFieldOfView() { return _fieldOfView.get(); }
|
||||||
void setFieldOfView(float fov);
|
void setFieldOfView(float fov);
|
||||||
|
@ -429,6 +437,10 @@ private:
|
||||||
float _fps;
|
float _fps;
|
||||||
QElapsedTimer _timerStart;
|
QElapsedTimer _timerStart;
|
||||||
QElapsedTimer _lastTimeUpdated;
|
QElapsedTimer _lastTimeUpdated;
|
||||||
|
float _lastInstantaneousFps { 0.0f };
|
||||||
|
float _lastPaintWait { 0.0f };
|
||||||
|
float _lastDeducedNonVSyncFps { 0.0f };
|
||||||
|
float _marginForDeducedFramePeriod{ 0.002f }; // 2ms, adjustable
|
||||||
|
|
||||||
ShapeManager _shapeManager;
|
ShapeManager _shapeManager;
|
||||||
PhysicalEntitySimulation _entitySimulation;
|
PhysicalEntitySimulation _entitySimulation;
|
||||||
|
|
|
@ -183,9 +183,31 @@ void Avatar::simulate(float deltaTime) {
|
||||||
if (_shouldRenderBillboard) {
|
if (_shouldRenderBillboard) {
|
||||||
if (getLODDistance() < BILLBOARD_LOD_DISTANCE * (1.0f - BILLBOARD_HYSTERESIS_PROPORTION)) {
|
if (getLODDistance() < BILLBOARD_LOD_DISTANCE * (1.0f - BILLBOARD_HYSTERESIS_PROPORTION)) {
|
||||||
_shouldRenderBillboard = false;
|
_shouldRenderBillboard = false;
|
||||||
|
qCDebug(interfaceapp) << "Unbillboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance();
|
||||||
}
|
}
|
||||||
} else if (getLODDistance() > BILLBOARD_LOD_DISTANCE * (1.0f + BILLBOARD_HYSTERESIS_PROPORTION)) {
|
} else if (getLODDistance() > BILLBOARD_LOD_DISTANCE * (1.0f + BILLBOARD_HYSTERESIS_PROPORTION)) {
|
||||||
_shouldRenderBillboard = true;
|
_shouldRenderBillboard = true;
|
||||||
|
qCDebug(interfaceapp) << "Billboarding" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for LOD" << getLODDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (_shouldSkipRender) {
|
||||||
|
if (distance < renderDistance * (1.0f - SKIP_HYSTERESIS_PROPORTION)) {
|
||||||
|
_shouldSkipRender = false;
|
||||||
|
_skeletonModel.setVisibleInScene(true, qApp->getMain3DScene());
|
||||||
|
if (!isControllerLogging) { // Test for isMyAvatar is prophylactic. Never occurs in current code.
|
||||||
|
qCDebug(interfaceapp) << "Rerendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (distance > renderDistance * (1.0f + SKIP_HYSTERESIS_PROPORTION)) {
|
||||||
|
_shouldSkipRender = true;
|
||||||
|
_skeletonModel.setVisibleInScene(false, qApp->getMain3DScene());
|
||||||
|
if (!isControllerLogging) {
|
||||||
|
qCDebug(interfaceapp) << "Unrendering" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for distance" << renderDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// simple frustum check
|
// simple frustum check
|
||||||
|
@ -198,7 +220,7 @@ void Avatar::simulate(float deltaTime) {
|
||||||
getHand()->simulate(deltaTime, false);
|
getHand()->simulate(deltaTime, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_shouldRenderBillboard && inViewFrustum) {
|
if (!_shouldRenderBillboard && !_shouldSkipRender && inViewFrustum) {
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("skeleton");
|
PerformanceTimer perfTimer("skeleton");
|
||||||
for (int i = 0; i < _jointData.size(); i++) {
|
for (int i = 0; i < _jointData.size(); i++) {
|
||||||
|
|
|
@ -140,6 +140,8 @@ public:
|
||||||
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
||||||
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
||||||
|
|
||||||
|
Q_INVOKABLE bool getShouldRender() const { return !_shouldSkipRender; }
|
||||||
|
|
||||||
/// Scales a world space position vector relative to the avatar position and scale
|
/// Scales a world space position vector relative to the avatar position and scale
|
||||||
/// \param vector position to be scaled. Will store the result
|
/// \param vector position to be scaled. Will store the result
|
||||||
void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
|
void scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const;
|
||||||
|
@ -226,6 +228,7 @@ private:
|
||||||
bool _initialized;
|
bool _initialized;
|
||||||
NetworkTexturePointer _billboardTexture;
|
NetworkTexturePointer _billboardTexture;
|
||||||
bool _shouldRenderBillboard;
|
bool _shouldRenderBillboard;
|
||||||
|
bool _shouldSkipRender { false };
|
||||||
bool _isLookAtTarget;
|
bool _isLookAtTarget;
|
||||||
|
|
||||||
void renderBillboard(RenderArgs* renderArgs);
|
void renderBillboard(RenderArgs* renderArgs);
|
||||||
|
|
|
@ -90,6 +90,21 @@ void AvatarManager::init() {
|
||||||
_myAvatar->addToScene(_myAvatar, scene, pendingChanges);
|
_myAvatar->addToScene(_myAvatar, scene, pendingChanges);
|
||||||
}
|
}
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
|
||||||
|
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.setControlledValueLowLimit(1.0f / (float) TREE_SCALE);
|
||||||
|
// Advice for tuning parameters:
|
||||||
|
// See PIDController.h. There's a section on tuning in the reference.
|
||||||
|
// Turn on logging with the following (or from js with AvatarList.setRenderDistanceControllerHistory("avatar render", 300))
|
||||||
|
//_renderDistanceController.setHistorySize("avatar render", target_fps * 4);
|
||||||
|
// Note that extra logging/hysteresis is turned off in Avatar.cpp when the above logging is on.
|
||||||
|
_renderDistanceController.setKP(0.0008f); // Usually about 0.6 of largest that doesn't oscillate when other parameters 0.
|
||||||
|
_renderDistanceController.setKI(0.0006f); // Big enough to bring us to target with the above KP.
|
||||||
|
_renderDistanceController.setKD(0.000001f); // A touch of kd increases the speed by which we get there.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarManager::updateMyAvatar(float deltaTime) {
|
void AvatarManager::updateMyAvatar(float deltaTime) {
|
||||||
|
@ -123,6 +138,17 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
|
PerformanceWarning warn(showWarnings, "Application::updateAvatars()");
|
||||||
|
|
||||||
PerformanceTimer perfTimer("otherAvatars");
|
PerformanceTimer perfTimer("otherAvatars");
|
||||||
|
|
||||||
|
_renderDistanceController.setMeasuredValueSetpoint(qApp->getTargetFrameRate()); // No problem updating in flight.
|
||||||
|
// The PID controller raises the controlled value when the measured value goes up.
|
||||||
|
// The measured value is frame rate. When the controlled value (1 / render cutoff distance)
|
||||||
|
// goes up, the render cutoff distance gets closer, the number of rendered avatars is less, and frame rate
|
||||||
|
// goes up.
|
||||||
|
const float deduced = qApp->getLastDeducedNonVSyncFps();
|
||||||
|
const float distance = 1.0f / _renderDistanceController.update(deduced, deltaTime);
|
||||||
|
_renderDistanceAverage.updateAverage(distance);
|
||||||
|
_renderDistance = _renderDistanceAverage.getAverage();
|
||||||
|
int renderableCount = 0;
|
||||||
|
|
||||||
// simulate avatars
|
// simulate avatars
|
||||||
auto hashCopy = getHashCopy();
|
auto hashCopy = getHashCopy();
|
||||||
|
@ -141,10 +167,14 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
} else {
|
} else {
|
||||||
avatar->startUpdate();
|
avatar->startUpdate();
|
||||||
avatar->simulate(deltaTime);
|
avatar->simulate(deltaTime);
|
||||||
|
if (avatar->getShouldRender()) {
|
||||||
|
renderableCount++;
|
||||||
|
}
|
||||||
avatar->endUpdate();
|
avatar->endUpdate();
|
||||||
++avatarIterator;
|
++avatarIterator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_renderedAvatarCount = renderableCount;
|
||||||
|
|
||||||
// simulate avatar fades
|
// simulate avatar fades
|
||||||
simulateAvatarFades(deltaTime);
|
simulateAvatarFades(deltaTime);
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include <AvatarHashMap.h>
|
#include <AvatarHashMap.h>
|
||||||
#include <PhysicsEngine.h>
|
#include <PhysicsEngine.h>
|
||||||
|
#include <PIDController.h>
|
||||||
|
#include <SimpleMovingAverage.h>
|
||||||
|
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
#include "AvatarMotionState.h"
|
#include "AvatarMotionState.h"
|
||||||
|
@ -43,6 +45,7 @@ public:
|
||||||
void clearOtherAvatars();
|
void clearOtherAvatars();
|
||||||
|
|
||||||
bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; }
|
bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; }
|
||||||
|
PIDController& getRenderDistanceController() { return _renderDistanceController; }
|
||||||
|
|
||||||
class LocalLight {
|
class LocalLight {
|
||||||
public:
|
public:
|
||||||
|
@ -64,6 +67,17 @@ public:
|
||||||
void handleCollisionEvents(const CollisionEvents& collisionEvents);
|
void handleCollisionEvents(const CollisionEvents& collisionEvents);
|
||||||
|
|
||||||
void updateAvatarPhysicsShape(Avatar* avatar);
|
void updateAvatarPhysicsShape(Avatar* avatar);
|
||||||
|
|
||||||
|
// Expose results and parameter-tuning operations to other systems, such as stats and javascript.
|
||||||
|
Q_INVOKABLE float getRenderDistance() { return _renderDistance; }
|
||||||
|
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); }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; }
|
||||||
|
@ -90,6 +104,10 @@ private:
|
||||||
QVector<AvatarManager::LocalLight> _localLights;
|
QVector<AvatarManager::LocalLight> _localLights;
|
||||||
|
|
||||||
bool _shouldShowReceiveStats = false;
|
bool _shouldShowReceiveStats = false;
|
||||||
|
float _renderDistance { (float) TREE_SCALE };
|
||||||
|
int _renderedAvatarCount { 0 };
|
||||||
|
PIDController _renderDistanceController { };
|
||||||
|
SimpleMovingAverage _renderDistanceAverage { 10 };
|
||||||
|
|
||||||
SetOfAvatarMotionStates _avatarMotionStates;
|
SetOfAvatarMotionStates _avatarMotionStates;
|
||||||
SetOfMotionStates _motionStatesToAdd;
|
SetOfMotionStates _motionStatesToAdd;
|
||||||
|
|
|
@ -196,5 +196,9 @@ QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine*
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebWindowClass::setTitle(const QString& title) {
|
void WebWindowClass::setTitle(const QString& title) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setTitle", Qt::AutoConnection, Q_ARG(QString, title));
|
||||||
|
return;
|
||||||
|
}
|
||||||
_windowWidget->setWindowTitle(title);
|
_windowWidget->setWindowTitle(title);
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,8 @@ void Stats::updateStats(bool force) {
|
||||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||||
// we need to take one avatar out so we don't include ourselves
|
// we need to take one avatar out so we don't include ourselves
|
||||||
STAT_UPDATE(avatarCount, avatarManager->size() - 1);
|
STAT_UPDATE(avatarCount, avatarManager->size() - 1);
|
||||||
|
STAT_UPDATE(avatarRenderableCount, avatarManager->getNumberInRenderRange());
|
||||||
|
STAT_UPDATE(avatarRenderDistance, (int) round(avatarManager->getRenderDistance())); // deliberately truncating
|
||||||
STAT_UPDATE(serverCount, nodeList->size());
|
STAT_UPDATE(serverCount, nodeList->size());
|
||||||
STAT_UPDATE(framerate, (int)qApp->getFps());
|
STAT_UPDATE(framerate, (int)qApp->getFps());
|
||||||
STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond());
|
STAT_UPDATE(simrate, (int)qApp->getAverageSimsPerSecond());
|
||||||
|
|
|
@ -36,6 +36,8 @@ class Stats : public QQuickItem {
|
||||||
STATS_PROPERTY(int, simrate, 0)
|
STATS_PROPERTY(int, simrate, 0)
|
||||||
STATS_PROPERTY(int, avatarSimrate, 0)
|
STATS_PROPERTY(int, avatarSimrate, 0)
|
||||||
STATS_PROPERTY(int, avatarCount, 0)
|
STATS_PROPERTY(int, avatarCount, 0)
|
||||||
|
STATS_PROPERTY(int, avatarRenderableCount, 0)
|
||||||
|
STATS_PROPERTY(int, avatarRenderDistance, 0)
|
||||||
STATS_PROPERTY(int, packetInCount, 0)
|
STATS_PROPERTY(int, packetInCount, 0)
|
||||||
STATS_PROPERTY(int, packetOutCount, 0)
|
STATS_PROPERTY(int, packetOutCount, 0)
|
||||||
STATS_PROPERTY(float, mbpsIn, 0)
|
STATS_PROPERTY(float, mbpsIn, 0)
|
||||||
|
@ -117,6 +119,8 @@ signals:
|
||||||
void simrateChanged();
|
void simrateChanged();
|
||||||
void avatarSimrateChanged();
|
void avatarSimrateChanged();
|
||||||
void avatarCountChanged();
|
void avatarCountChanged();
|
||||||
|
void avatarRenderableCountChanged();
|
||||||
|
void avatarRenderDistanceChanged();
|
||||||
void packetInCountChanged();
|
void packetInCountChanged();
|
||||||
void packetOutCountChanged();
|
void packetOutCountChanged();
|
||||||
void mbpsInChanged();
|
void mbpsInChanged();
|
||||||
|
|
|
@ -49,23 +49,6 @@ const AnimPose& AnimSkeleton::getAbsoluteBindPose(int jointIndex) const {
|
||||||
return _absoluteBindPoses[jointIndex];
|
return _absoluteBindPoses[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimPose AnimSkeleton::getRootAbsoluteBindPoseByChildName(const QString& childName) const {
|
|
||||||
AnimPose pose = AnimPose::identity;
|
|
||||||
int jointIndex = nameToJointIndex(childName);
|
|
||||||
if (jointIndex >= 0) {
|
|
||||||
int numJoints = (int)(_absoluteBindPoses.size());
|
|
||||||
if (jointIndex < numJoints) {
|
|
||||||
int parentIndex = getParentIndex(jointIndex);
|
|
||||||
while (parentIndex != -1 && parentIndex < numJoints) {
|
|
||||||
jointIndex = parentIndex;
|
|
||||||
parentIndex = getParentIndex(jointIndex);
|
|
||||||
}
|
|
||||||
pose = _absoluteBindPoses[jointIndex];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pose;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AnimPose& AnimSkeleton::getRelativeBindPose(int jointIndex) const {
|
const AnimPose& AnimSkeleton::getRelativeBindPose(int jointIndex) const {
|
||||||
return _relativeBindPoses[jointIndex];
|
return _relativeBindPoses[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ public:
|
||||||
|
|
||||||
// absolute pose, not relative to parent
|
// absolute pose, not relative to parent
|
||||||
const AnimPose& getAbsoluteBindPose(int jointIndex) const;
|
const AnimPose& getAbsoluteBindPose(int jointIndex) const;
|
||||||
AnimPose getRootAbsoluteBindPoseByChildName(const QString& childName) const;
|
|
||||||
|
|
||||||
// relative to parent pose
|
// relative to parent pose
|
||||||
const AnimPose& getRelativeBindPose(int jointIndex) const;
|
const AnimPose& getRelativeBindPose(int jointIndex) const;
|
||||||
|
|
|
@ -1306,10 +1306,10 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
|
||||||
|
|
||||||
// TODO: figure out how to obtain the yFlip from where it is actually stored
|
// TODO: figure out how to obtain the yFlip from where it is actually stored
|
||||||
glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
AnimPose rootBindPose = _animSkeleton->getRootAbsoluteBindPoseByChildName("LeftHand");
|
AnimPose hipsBindPose = _animSkeleton->getAbsoluteBindPose(_animSkeleton->nameToJointIndex("Hips"));
|
||||||
if (params.isLeftEnabled) {
|
if (params.isLeftEnabled) {
|
||||||
_animVars.set("leftHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.leftPosition);
|
_animVars.set("leftHandPosition", hipsBindPose.trans + hipsBindPose.rot * yFlipHACK * params.leftPosition);
|
||||||
_animVars.set("leftHandRotation", rootBindPose.rot * yFlipHACK * params.leftOrientation);
|
_animVars.set("leftHandRotation", hipsBindPose.rot * yFlipHACK * params.leftOrientation);
|
||||||
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
|
_animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||||
} else {
|
} else {
|
||||||
_animVars.unset("leftHandPosition");
|
_animVars.unset("leftHandPosition");
|
||||||
|
@ -1317,8 +1317,8 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) {
|
||||||
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
_animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition);
|
||||||
}
|
}
|
||||||
if (params.isRightEnabled) {
|
if (params.isRightEnabled) {
|
||||||
_animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition);
|
_animVars.set("rightHandPosition", hipsBindPose.trans + hipsBindPose.rot * yFlipHACK * params.rightPosition);
|
||||||
_animVars.set("rightHandRotation", rootBindPose.rot * yFlipHACK * params.rightOrientation);
|
_animVars.set("rightHandRotation", hipsBindPose.rot * yFlipHACK * params.rightOrientation);
|
||||||
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
|
_animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition);
|
||||||
} else {
|
} else {
|
||||||
_animVars.unset("rightHandPosition");
|
_animVars.unset("rightHandPosition");
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
|
|
||||||
namespace AudioConstants {
|
namespace AudioConstants {
|
||||||
const int SAMPLE_RATE = 24000;
|
const int SAMPLE_RATE = 24000;
|
||||||
|
|
||||||
typedef int16_t AudioSample;
|
typedef int16_t AudioSample;
|
||||||
|
|
||||||
static const char* AUDIO_FRAME_NAME = "com.highfidelity.recording.Audio";
|
inline const char* getAudioFrameName() { return "com.highfidelity.recording.Audio"; }
|
||||||
|
|
||||||
const int NETWORK_FRAME_BYTES_STEREO = 1024;
|
const int NETWORK_FRAME_BYTES_STEREO = 1024;
|
||||||
const int NETWORK_FRAME_SAMPLES_STEREO = NETWORK_FRAME_BYTES_STEREO / sizeof(AudioSample);
|
const int NETWORK_FRAME_SAMPLES_STEREO = NETWORK_FRAME_BYTES_STEREO / sizeof(AudioSample);
|
||||||
|
|
|
@ -31,6 +31,7 @@ EntityItemPointer RenderablePolyLineEntityItem::factory(const EntityItemID& enti
|
||||||
RenderablePolyLineEntityItem::RenderablePolyLineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
RenderablePolyLineEntityItem::RenderablePolyLineEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||||
PolyLineEntityItem(entityItemID, properties) {
|
PolyLineEntityItem(entityItemID, properties) {
|
||||||
_numVertices = 0;
|
_numVertices = 0;
|
||||||
|
_vertices = QVector<glm::vec3>(0.0f);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,13 +115,56 @@ void RenderablePolyLineEntityItem::updateGeometry() {
|
||||||
_numVertices += 2;
|
_numVertices += 2;
|
||||||
}
|
}
|
||||||
_pointsChanged = false;
|
_pointsChanged = false;
|
||||||
|
_normalsChanged = false;
|
||||||
|
_strokeWidthsChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderablePolyLineEntityItem::updateVertices() {
|
||||||
|
// Calculate the minimum vector size out of normals, points, and stroke widths
|
||||||
|
int minVectorSize = _normals.size();
|
||||||
|
if (_points.size() < minVectorSize) {
|
||||||
|
minVectorSize = _points.size();
|
||||||
|
}
|
||||||
|
if (_strokeWidths.size() < minVectorSize) {
|
||||||
|
minVectorSize = _strokeWidths.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
_vertices.clear();
|
||||||
|
glm::vec3 v1, v2, tangent, binormal, point;
|
||||||
|
|
||||||
|
int finalIndex = minVectorSize - 1;
|
||||||
|
for (int i = 0; i < finalIndex; i++) {
|
||||||
|
float width = _strokeWidths.at(i);
|
||||||
|
point = _points.at(i);
|
||||||
|
|
||||||
|
tangent = _points.at(i);
|
||||||
|
|
||||||
|
tangent = _points.at(i + 1) - point;
|
||||||
|
glm::vec3 normal = _normals.at(i);
|
||||||
|
binormal = glm::normalize(glm::cross(tangent, normal)) * width;
|
||||||
|
|
||||||
|
// Check to make sure binormal is not a NAN. If it is, don't add to vertices vector
|
||||||
|
if (binormal.x != binormal.x) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
v1 = point + binormal;
|
||||||
|
v2 = point - binormal;
|
||||||
|
_vertices << v1 << v2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For last point we can assume binormals are the same since it represents the last two vertices of quad
|
||||||
|
point = _points.at(finalIndex);
|
||||||
|
v1 = point + binormal;
|
||||||
|
v2 = point - binormal;
|
||||||
|
_vertices << v1 << v2;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RenderablePolyLineEntityItem::render(RenderArgs* args) {
|
void RenderablePolyLineEntityItem::render(RenderArgs* args) {
|
||||||
QWriteLocker lock(&_quadReadWriteLock);
|
QWriteLocker lock(&_quadReadWriteLock);
|
||||||
if (_points.size() < 2 || _normals.size () < 2 || _vertices.size() < 2) {
|
if (_points.size() < 2 || _normals.size () < 2 || _strokeWidths.size() < 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +183,8 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) {
|
||||||
Q_ASSERT(getType() == EntityTypes::PolyLine);
|
Q_ASSERT(getType() == EntityTypes::PolyLine);
|
||||||
|
|
||||||
Q_ASSERT(args->_batch);
|
Q_ASSERT(args->_batch);
|
||||||
if (_pointsChanged) {
|
if (_pointsChanged || _strokeWidthsChanged || _normalsChanged) {
|
||||||
|
updateVertices();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,10 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updateGeometry();
|
void updateGeometry();
|
||||||
|
void updateVertices();
|
||||||
gpu::BufferPointer _verticesBuffer;
|
gpu::BufferPointer _verticesBuffer;
|
||||||
unsigned int _numVertices;
|
unsigned int _numVertices;
|
||||||
|
QVector<glm::vec3> _vertices;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,9 @@ PolyLineEntityItem::PolyLineEntityItem(const EntityItemID& entityItemID, const E
|
||||||
EntityItem(entityItemID),
|
EntityItem(entityItemID),
|
||||||
_lineWidth(DEFAULT_LINE_WIDTH),
|
_lineWidth(DEFAULT_LINE_WIDTH),
|
||||||
_pointsChanged(true),
|
_pointsChanged(true),
|
||||||
|
_normalsChanged(true),
|
||||||
|
_strokeWidthsChanged(true),
|
||||||
_points(QVector<glm::vec3>(0.0f)),
|
_points(QVector<glm::vec3>(0.0f)),
|
||||||
_vertices(QVector<glm::vec3>(0.0f)),
|
|
||||||
_normals(QVector<glm::vec3>(0.0f)),
|
_normals(QVector<glm::vec3>(0.0f)),
|
||||||
_strokeWidths(QVector<float>(0.0f)),
|
_strokeWidths(QVector<float>(0.0f)),
|
||||||
_textures("")
|
_textures("")
|
||||||
|
@ -106,47 +107,13 @@ bool PolyLineEntityItem::appendPoint(const glm::vec3& point) {
|
||||||
|
|
||||||
bool PolyLineEntityItem::setStrokeWidths(const QVector<float>& strokeWidths) {
|
bool PolyLineEntityItem::setStrokeWidths(const QVector<float>& strokeWidths) {
|
||||||
_strokeWidths = strokeWidths;
|
_strokeWidths = strokeWidths;
|
||||||
|
_strokeWidthsChanged = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PolyLineEntityItem::setNormals(const QVector<glm::vec3>& normals) {
|
bool PolyLineEntityItem::setNormals(const QVector<glm::vec3>& normals) {
|
||||||
_normals = normals;
|
_normals = normals;
|
||||||
if (_points.size() < 2 || _normals.size() < 2 || _strokeWidths.size() < 2) {
|
_normalsChanged = true;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int minVectorSize = _normals.size();
|
|
||||||
if (_points.size() < minVectorSize) {
|
|
||||||
minVectorSize = _points.size();
|
|
||||||
}
|
|
||||||
if (_strokeWidths.size() < minVectorSize) {
|
|
||||||
minVectorSize = _strokeWidths.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
_vertices.clear();
|
|
||||||
glm::vec3 v1, v2, tangent, binormal, point;
|
|
||||||
|
|
||||||
int finalIndex = minVectorSize -1;
|
|
||||||
for (int i = 0; i < finalIndex; i++) {
|
|
||||||
float width = _strokeWidths.at(i);
|
|
||||||
point = _points.at(i);
|
|
||||||
|
|
||||||
tangent = _points.at(i + 1) - point;
|
|
||||||
glm::vec3 normal = normals.at(i);
|
|
||||||
binormal = glm::normalize(glm::cross(tangent, normal)) * width;
|
|
||||||
|
|
||||||
//This checks to make sure binormal is not a NAN
|
|
||||||
assert(binormal.x == binormal.x);
|
|
||||||
v1 = point + binormal;
|
|
||||||
v2 = point - binormal;
|
|
||||||
_vertices << v1 << v2;
|
|
||||||
}
|
|
||||||
//for last point we can just assume binormals are same since it represents last two vertices of quad
|
|
||||||
point = _points.at(finalIndex);
|
|
||||||
v1 = point + binormal;
|
|
||||||
v2 = point - binormal;
|
|
||||||
_vertices << v1 << v2;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,8 +93,9 @@ class PolyLineEntityItem : public EntityItem {
|
||||||
rgbColor _color;
|
rgbColor _color;
|
||||||
float _lineWidth;
|
float _lineWidth;
|
||||||
bool _pointsChanged;
|
bool _pointsChanged;
|
||||||
|
bool _normalsChanged;
|
||||||
|
bool _strokeWidthsChanged;
|
||||||
QVector<glm::vec3> _points;
|
QVector<glm::vec3> _points;
|
||||||
QVector<glm::vec3> _vertices;
|
|
||||||
QVector<glm::vec3> _normals;
|
QVector<glm::vec3> _normals;
|
||||||
QVector<float> _strokeWidths;
|
QVector<float> _strokeWidths;
|
||||||
QString _textures;
|
QString _textures;
|
||||||
|
|
|
@ -123,6 +123,31 @@ void ObjectMotionState::setMotionType(MotionType motionType) {
|
||||||
_motionType = motionType;
|
_motionType = motionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the Continuous Collision Detection (CCD) configuration settings of our RigidBody so that
|
||||||
|
// CCD will be enabled automatically when its speed surpasses a certain threshold.
|
||||||
|
void ObjectMotionState::updateCCDConfiguration() {
|
||||||
|
if (_body) {
|
||||||
|
if (_shape) {
|
||||||
|
// If this object moves faster than its bounding radius * RADIUS_MOTION_THRESHOLD_MULTIPLIER,
|
||||||
|
// CCD will be enabled for this object.
|
||||||
|
const auto RADIUS_MOTION_THRESHOLD_MULTIPLIER = 0.5f;
|
||||||
|
|
||||||
|
btVector3 center;
|
||||||
|
btScalar radius;
|
||||||
|
_shape->getBoundingSphere(center, radius);
|
||||||
|
_body->setCcdMotionThreshold(radius * RADIUS_MOTION_THRESHOLD_MULTIPLIER);
|
||||||
|
|
||||||
|
// TODO: Ideally the swept sphere radius would be contained by the object. Using the bounding sphere
|
||||||
|
// radius works well for spherical objects, but may cause issues with other shapes. For arbitrary
|
||||||
|
// objects we may want to consider a different approach, such as grouping rigid bodies together.
|
||||||
|
_body->setCcdSweptSphereRadius(radius);
|
||||||
|
} else {
|
||||||
|
// Disable CCD
|
||||||
|
_body->setCcdMotionThreshold(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectMotionState::setRigidBody(btRigidBody* body) {
|
void ObjectMotionState::setRigidBody(btRigidBody* body) {
|
||||||
// give the body a (void*) back-pointer to this ObjectMotionState
|
// give the body a (void*) back-pointer to this ObjectMotionState
|
||||||
if (_body != body) {
|
if (_body != body) {
|
||||||
|
@ -133,6 +158,7 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) {
|
||||||
if (_body) {
|
if (_body) {
|
||||||
_body->setUserPointer(this);
|
_body->setUserPointer(this);
|
||||||
}
|
}
|
||||||
|
updateCCDConfiguration();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,6 +258,8 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine*
|
||||||
if (_shape != newShape) {
|
if (_shape != newShape) {
|
||||||
_shape = newShape;
|
_shape = newShape;
|
||||||
_body->setCollisionShape(_shape);
|
_body->setCollisionShape(_shape);
|
||||||
|
|
||||||
|
updateCCDConfiguration();
|
||||||
} else {
|
} else {
|
||||||
// huh... the shape didn't actually change, so we clear the DIRTY_SHAPE flag
|
// huh... the shape didn't actually change, so we clear the DIRTY_SHAPE flag
|
||||||
flags &= ~Simulation::DIRTY_SHAPE;
|
flags &= ~Simulation::DIRTY_SHAPE;
|
||||||
|
|
|
@ -151,6 +151,7 @@ protected:
|
||||||
virtual bool isReadyToComputeShape() = 0;
|
virtual bool isReadyToComputeShape() = 0;
|
||||||
virtual btCollisionShape* computeNewShape() = 0;
|
virtual btCollisionShape* computeNewShape() = 0;
|
||||||
void setMotionType(MotionType motionType);
|
void setMotionType(MotionType motionType);
|
||||||
|
void updateCCDConfiguration();
|
||||||
|
|
||||||
// clearObjectBackPointer() overrrides should call the base method, then actually clear the object back pointer.
|
// clearObjectBackPointer() overrrides should call the base method, then actually clear the object back pointer.
|
||||||
virtual void clearObjectBackPointer() { _type = MOTIONSTATE_TYPE_INVALID; }
|
virtual void clearObjectBackPointer() { _type = MOTIONSTATE_TYPE_INVALID; }
|
||||||
|
|
78
libraries/shared/src/PIDController.cpp
Normal file
78
libraries/shared/src/PIDController.cpp
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// PIDController.cpp
|
||||||
|
// libraries/shared/src
|
||||||
|
//
|
||||||
|
// Created by Howard Stearns 11/13/15.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <QDebug>
|
||||||
|
#include "SharedLogging.h"
|
||||||
|
#include "PIDController.h"
|
||||||
|
|
||||||
|
float PIDController::update(float measuredValue, float dt, bool resetAccumulator) {
|
||||||
|
const float error = getMeasuredValueSetpoint() - measuredValue; // Sign is the direction we want measuredValue to go. Positive means go higher.
|
||||||
|
|
||||||
|
const float p = getKP() * error; // term is Proportional to error
|
||||||
|
|
||||||
|
const float accumulatedError = glm::clamp(error * dt + (resetAccumulator ? 0 : _lastAccumulation), // integrate error
|
||||||
|
getAccumulatedValueLowLimit(), // but clamp by anti-windup limits
|
||||||
|
getAccumulatedValueHighLimit());
|
||||||
|
const float i = getKI() * accumulatedError; // term is Integral of error
|
||||||
|
|
||||||
|
const float changeInError = (error - _lastError) / dt; // positive value denotes increasing deficit
|
||||||
|
const float d = getKD() * changeInError; // term is Derivative of Error
|
||||||
|
|
||||||
|
const float computedValue = glm::clamp(p + i + d,
|
||||||
|
getControlledValueLowLimit(),
|
||||||
|
getControlledValueHighLimit());
|
||||||
|
|
||||||
|
if (getIsLogging()) { // if logging/reporting
|
||||||
|
updateHistory(measuredValue, dt, error, accumulatedError, changeInError, p, i, d, computedValue);
|
||||||
|
}
|
||||||
|
Q_ASSERT(!isnan(computedValue));
|
||||||
|
|
||||||
|
// update state for next time
|
||||||
|
_lastError = error;
|
||||||
|
_lastAccumulation = accumulatedError;
|
||||||
|
return computedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just for logging/reporting. Used when picking/verifying the operational parameters.
|
||||||
|
void PIDController::updateHistory(float measuredValue, float dt, float error, float accumulatedError, float changeInError, float p, float i, float d, float computedValue) {
|
||||||
|
// Don't report each update(), as the I/O messes with the results a lot.
|
||||||
|
// Instead, add to history, and then dump out at once when full.
|
||||||
|
// Typically, the first few values reported in each batch should be ignored.
|
||||||
|
const int n = _history.size();
|
||||||
|
_history.resize(n + 1);
|
||||||
|
Row& next = _history[n];
|
||||||
|
next.measured = measuredValue;
|
||||||
|
next.dt = dt;
|
||||||
|
next.error = error;
|
||||||
|
next.accumulated = accumulatedError;
|
||||||
|
next.changed = changeInError;
|
||||||
|
next.p = p;
|
||||||
|
next.i = i;
|
||||||
|
next.d = d;
|
||||||
|
next.computed = computedValue;
|
||||||
|
if (_history.size() == _history.capacity()) { // report when buffer is full
|
||||||
|
reportHistory();
|
||||||
|
_history.resize(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void PIDController::reportHistory() {
|
||||||
|
qCDebug(shared) << _label << "measured dt FIXME || error accumulated changed || p i d controlled";
|
||||||
|
for (int i = 0; i < _history.size(); i++) {
|
||||||
|
Row& row = _history[i];
|
||||||
|
qCDebug(shared) << row.measured << row.dt <<
|
||||||
|
"||" << row.error << row.accumulated << row.changed <<
|
||||||
|
"||" << row.p << row.i << row.d << row.computed << 1.0f/row.computed;
|
||||||
|
}
|
||||||
|
qCDebug(shared) << "Limits: setpoint" << getMeasuredValueSetpoint() << "accumulate" << getAccumulatedValueLowLimit() << getAccumulatedValueHighLimit() <<
|
||||||
|
"controlled" << getControlledValueLowLimit() << getControlledValueHighLimit() <<
|
||||||
|
"kp/ki/kd" << getKP() << getKI() << getKD();
|
||||||
|
}
|
89
libraries/shared/src/PIDController.h
Normal file
89
libraries/shared/src/PIDController.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
//
|
||||||
|
// PIDController.h
|
||||||
|
// libraries/shared/src
|
||||||
|
//
|
||||||
|
// Given a measure of system performance (such as frame rate, where bigger denotes more system work),
|
||||||
|
// compute a value that the system can take as input to control the amount of work done (such as an 1/LOD-distance,
|
||||||
|
// where bigger tends to give a higher measured system performance value). The controller's job is to compute a
|
||||||
|
// controlled value such that the measured value stays near the specified setpoint, even as system load changes.
|
||||||
|
// See http://www.wetmachine.com/inventing-the-future/mostly-reliable-performance-of-software-processes-by-dynamic-control-of-quality-parameters/
|
||||||
|
//
|
||||||
|
// Created by Howard Stearns 11/13/15.
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_PIDController_h
|
||||||
|
#define hifi_PIDController_h
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
// Although our coding standard shuns abbreviations, the control systems literature uniformly uses p, i, d, and dt rather than
|
||||||
|
// proportionalTerm, integralTerm, derivativeTerm, and deltaTime. Here we will be consistent with the literature.
|
||||||
|
class PIDController {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// These are the main interfaces:
|
||||||
|
void setMeasuredValueSetpoint(float newValue) { _measuredValueSetpoint = newValue; }
|
||||||
|
float update(float measuredValue, float dt, bool resetAccumulator = false); // returns the new computedValue
|
||||||
|
void setHistorySize(QString label = QString(""), int size = 0) { _history.reserve(size); _history.resize(0); _label = label; } // non-empty does logging
|
||||||
|
|
||||||
|
bool getIsLogging() { return _history.capacity(); }
|
||||||
|
float getMeasuredValueSetpoint() const { return _measuredValueSetpoint; }
|
||||||
|
// In normal operation (where we can easily reach setpoint), controlledValue is typcially pinned at max.
|
||||||
|
// Defaults to [0, max float], but for 1/LODdistance, it might be, say, [0, 0.2 or 0.1]
|
||||||
|
float getControlledValueLowLimit() const { return _controlledValueLowLimit; }
|
||||||
|
float getControlledValueHighLimit() const { return _controlledValueHighLimit; }
|
||||||
|
float getAntiWindupFactor() const { return _antiWindupFactor; } // default 10
|
||||||
|
float getKP() const { return _kp; } // proportional to error. See comment above class.
|
||||||
|
float getKI() const { return _ki; } // to time integral of error
|
||||||
|
float getKD() const { return _kd; } // to time derivative of error
|
||||||
|
float getAccumulatedValueHighLimit() const { return getAntiWindupFactor() * getMeasuredValueSetpoint(); }
|
||||||
|
float getAccumulatedValueLowLimit() const { return -getAntiWindupFactor() * getMeasuredValueSetpoint(); }
|
||||||
|
|
||||||
|
// There are several values that rarely change and might be thought of as "constants", but which do change during tuning, debugging, or other
|
||||||
|
// special-but-expected circumstances. Thus the instance vars are not const.
|
||||||
|
void setControlledValueLowLimit(float newValue) { _controlledValueLowLimit = newValue; }
|
||||||
|
void setControlledValueHighLimit(float newValue) { _controlledValueHighLimit = newValue; }
|
||||||
|
void setAntiWindupFactor(float newValue) { _antiWindupFactor = newValue; }
|
||||||
|
void setKP(float newValue) { _kp = newValue; }
|
||||||
|
void setKI(float newValue) { _ki = newValue; }
|
||||||
|
void setKD(float newValue) { _kd = newValue; }
|
||||||
|
|
||||||
|
class Row { // one row of accumulated history, used only for logging (if at all)
|
||||||
|
public:
|
||||||
|
float measured;
|
||||||
|
float dt;
|
||||||
|
float error;
|
||||||
|
float accumulated;
|
||||||
|
float changed;
|
||||||
|
float p;
|
||||||
|
float i;
|
||||||
|
float d;
|
||||||
|
float computed;
|
||||||
|
};
|
||||||
|
protected:
|
||||||
|
void reportHistory();
|
||||||
|
void updateHistory(float measured, float dt, float error, float accumulatedError, float changeInErro, float p, float i, float d, float computedValue);
|
||||||
|
float _measuredValueSetpoint { 0.0f };
|
||||||
|
float _controlledValueLowLimit { 0.0f };
|
||||||
|
float _controlledValueHighLimit { std::numeric_limits<float>::max() };
|
||||||
|
float _antiWindupFactor { 10.0f };
|
||||||
|
float _kp { 0.0f };
|
||||||
|
float _ki { 0.0f };
|
||||||
|
float _kd { 0.0f };
|
||||||
|
|
||||||
|
// Controller operating state
|
||||||
|
float _lastError{ 0.0f };
|
||||||
|
float _lastAccumulation{ 0.0f };
|
||||||
|
|
||||||
|
// reporting
|
||||||
|
QVector<Row> _history{};
|
||||||
|
QString _label{ "" };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_PIDController_h
|
Loading…
Reference in a new issue