mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
Merge branch 'master' of github.com:highfidelity/hifi into 22007-hifiQtBuildv2
This commit is contained in:
commit
cd276d2745
116 changed files with 3267 additions and 927 deletions
|
@ -155,6 +155,12 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
|
|||
void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
||||
const SlaveSharedData& slaveSharedData,
|
||||
Node& sendingNode) {
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message.getBytesLeftToRead() < qint64(sizeof(AvatarTraits::TraitVersion))) {
|
||||
qWarning() << "Refusing to process malformed traits packet from" << message.getSenderSockAddr();
|
||||
return;
|
||||
}
|
||||
|
||||
// pull the trait version from the message
|
||||
AvatarTraits::TraitVersion packetTraitVersion;
|
||||
message.readPrimitive(&packetTraitVersion);
|
||||
|
@ -164,10 +170,22 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
while (message.getBytesLeftToRead() > 0) {
|
||||
// for each trait in the packet, apply it if the trait version is newer than what we have
|
||||
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message.getBytesLeftToRead() < qint64(sizeof(AvatarTraits::TraitType))) {
|
||||
qWarning() << "Refusing to process malformed traits packet from" << message.getSenderSockAddr();
|
||||
return;
|
||||
}
|
||||
|
||||
AvatarTraits::TraitType traitType;
|
||||
message.readPrimitive(&traitType);
|
||||
|
||||
if (AvatarTraits::isSimpleTrait(traitType)) {
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message.getBytesLeftToRead() < qint64(sizeof(AvatarTraits::TraitWireSize))) {
|
||||
qWarning() << "Refusing to process malformed traits packet from" << message.getSenderSockAddr();
|
||||
return;
|
||||
}
|
||||
|
||||
AvatarTraits::TraitWireSize traitSize;
|
||||
message.readPrimitive(&traitSize);
|
||||
|
||||
|
@ -179,7 +197,6 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
if (packetTraitVersion > _lastReceivedTraitVersions[traitType]) {
|
||||
_avatar->processTrait(traitType, message.read(traitSize));
|
||||
_lastReceivedTraitVersions[traitType] = packetTraitVersion;
|
||||
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
// special handling for skeleton model URL, since we need to make sure it is in the whitelist
|
||||
checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion);
|
||||
|
@ -190,13 +207,15 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
message.seek(message.getPosition() + traitSize);
|
||||
}
|
||||
} else {
|
||||
AvatarTraits::TraitInstanceID instanceID = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
if (message.getBytesLeftToRead() == 0) {
|
||||
qWarning() << "Received an instanced trait with no size from" << message.getSenderSockAddr();
|
||||
break;
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message.getBytesLeftToRead() < qint64(NUM_BYTES_RFC4122_UUID +
|
||||
sizeof(AvatarTraits::TraitWireSize))) {
|
||||
qWarning() << "Refusing to process malformed traits packet from" << message.getSenderSockAddr();
|
||||
return;
|
||||
}
|
||||
|
||||
AvatarTraits::TraitInstanceID instanceID = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
AvatarTraits::TraitWireSize traitSize;
|
||||
message.readPrimitive(&traitSize);
|
||||
|
||||
|
|
|
@ -62,8 +62,8 @@
|
|||
* @property {boolean} lookAtSnappingEnabled=true - <code>true</code> if the avatar's eyes snap to look at another avatar's
|
||||
* eyes when the other avatar is in the line of sight and also has <code>lookAtSnappingEnabled == true</code>.
|
||||
* @property {string} skeletonModelURL - The avatar's FST file.
|
||||
* @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.<br />
|
||||
* <strong>Deprecated:</strong> Use avatar entities instead.
|
||||
* @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed. Use avatar entities instead.</p>
|
||||
* @property {string[]} jointNames - The list of joints in the current avatar model. <em>Read-only.</em>
|
||||
* @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. <em>Read-only.</em>
|
||||
* @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the
|
||||
|
|
|
@ -56,7 +56,8 @@ public slots:
|
|||
/**jsdoc
|
||||
* @function EntityViewer.setKeyholeRadius
|
||||
* @param {number} radius
|
||||
* @deprecated Use {@link EntityViewer.setCenterRadius|setCenterRadius} instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use {@link EntityViewer.setCenterRadius|setCenterRadius}
|
||||
* instead.
|
||||
*/
|
||||
void setKeyholeRadius(float radius) { _hasViewFrustum = true; _viewFrustum.setCenterRadius(radius); } // TODO: remove this legacy support
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 299 KiB |
|
@ -115,7 +115,6 @@ void DomainMetadata::securityChanged(bool send) {
|
|||
auto& state = *static_cast<QVariantMap*>(_metadata[DESCRIPTORS].data());
|
||||
|
||||
const QString RESTRICTION_OPEN = "open";
|
||||
const QString RESTRICTION_ANON = "anon";
|
||||
const QString RESTRICTION_HIFI = "hifi";
|
||||
const QString RESTRICTION_ACL = "acl";
|
||||
|
||||
|
@ -127,7 +126,7 @@ void DomainMetadata::securityChanged(bool send) {
|
|||
bool hasHifiAccess = settingsManager.getStandardPermissionsForName(NodePermissions::standardNameLoggedIn).can(
|
||||
NodePermissions::Permission::canConnectToDomain);
|
||||
if (hasAnonymousAccess) {
|
||||
restriction = hasHifiAccess ? RESTRICTION_OPEN : RESTRICTION_ANON;
|
||||
restriction = RESTRICTION_OPEN;
|
||||
} else if (hasHifiAccess) {
|
||||
restriction = RESTRICTION_HIFI;
|
||||
} else {
|
||||
|
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 39 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 30 KiB |
BIN
interface/resources/avatar/animations/idleWS.fbx
Normal file
BIN
interface/resources/avatar/animations/idleWS.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idleWS_all.fbx
Normal file
BIN
interface/resources/avatar/animations/idleWS_all.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_LFF_all.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_LFF_all.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_RFF_all.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_RFF_all.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_lookaround01.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_lookaround01.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_armstretch.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_armstretch.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_bigstretch.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_bigstretch.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_checkwatch.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_checkwatch.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_headtilt.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_headtilt.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_lookaround.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_lookaround.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_neckstretch.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_neckstretch.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_slownod.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_slownod.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/talk04.fbx
Normal file
BIN
interface/resources/avatar/animations/talk04.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/talk_righthand.fbx
Normal file
BIN
interface/resources/avatar/animations/talk_righthand.fbx
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -57,6 +57,14 @@ Item {
|
|||
StatText {
|
||||
text: "Avatars: " + root.avatarCount
|
||||
}
|
||||
StatText {
|
||||
visible: true
|
||||
text: "Refresh: " + root.refreshRateRegime + " - " + root.refreshRateTarget
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded
|
||||
text:" " + root.refreshRateMode + " - " + root.uxMode;
|
||||
}
|
||||
StatText {
|
||||
text: "Game Rate: " + root.gameLoopRate
|
||||
}
|
||||
|
|
|
@ -254,7 +254,7 @@ Rectangle {
|
|||
switchWidth: root.switchWidth;
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
labelTextOn: qsTr("Warn when muted in HMD");
|
||||
labelTextOn: qsTr("HMD Mute Warning");
|
||||
labelTextSize: 16;
|
||||
backgroundOnColor: "#E3E3E3";
|
||||
checked: AudioScriptingInterface.warnWhenMuted;
|
||||
|
|
|
@ -150,6 +150,7 @@
|
|||
#include <Preferences.h>
|
||||
#include <display-plugins/CompositorHelper.h>
|
||||
#include <display-plugins/hmd/HmdDisplayPlugin.h>
|
||||
#include <display-plugins/RefreshRateController.h>
|
||||
#include <trackers/EyeTracker.h>
|
||||
#include <avatars-renderer/ScriptAvatar.h>
|
||||
#include <RenderableEntityItem.h>
|
||||
|
@ -192,6 +193,7 @@
|
|||
#include "scripting/WalletScriptingInterface.h"
|
||||
#include "scripting/TTSScriptingInterface.h"
|
||||
#include "scripting/KeyboardScriptingInterface.h"
|
||||
#include "scripting/RefreshRateScriptingInterface.h"
|
||||
|
||||
|
||||
|
||||
|
@ -820,7 +822,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
audioDLLPath += "/audioWin7";
|
||||
}
|
||||
QCoreApplication::addLibraryPath(audioDLLPath);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
|
||||
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
|
||||
|
@ -1813,6 +1815,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
});
|
||||
|
||||
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::STARTUP);
|
||||
|
||||
// Setup the _keyboardMouseDevice, _touchscreenDevice, _touchscreenVirtualPadDevice and the user input mapper with the default bindings
|
||||
userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice());
|
||||
// if the _touchscreenDevice is not supported it will not be registered
|
||||
|
@ -2621,6 +2625,8 @@ void Application::onAboutToQuit() {
|
|||
_aboutToQuit = true;
|
||||
|
||||
cleanupBeforeQuit();
|
||||
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::SHUTDOWN);
|
||||
}
|
||||
|
||||
void Application::cleanupBeforeQuit() {
|
||||
|
@ -3230,6 +3236,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
|
|||
|
||||
surfaceContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||
surfaceContext->setContextProperty("RefreshRate", new RefreshRateScriptingInterface());
|
||||
_fileDownload = new FileScriptingInterface(engine);
|
||||
surfaceContext->setContextProperty("File", _fileDownload);
|
||||
connect(_fileDownload, &FileScriptingInterface::unzipResult, this, &Application::handleUnzip);
|
||||
|
@ -3378,6 +3385,7 @@ void Application::setupQmlSurface(QQmlContext* surfaceContext, bool setAdditiona
|
|||
|
||||
surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||
surfaceContext->setContextProperty("RefreshRate", new RefreshRateScriptingInterface());
|
||||
|
||||
surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
|
||||
|
@ -4049,6 +4057,9 @@ bool Application::event(QEvent* event) {
|
|||
case QEvent::KeyRelease:
|
||||
keyReleaseEvent(static_cast<QKeyEvent*>(event));
|
||||
return true;
|
||||
case QEvent::FocusIn:
|
||||
focusInEvent(static_cast<QFocusEvent*>(event));
|
||||
return true;
|
||||
case QEvent::FocusOut:
|
||||
focusOutEvent(static_cast<QFocusEvent*>(event));
|
||||
return true;
|
||||
|
@ -4110,6 +4121,12 @@ bool Application::eventFilter(QObject* object, QEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::WindowStateChange) {
|
||||
if (getWindow()->windowState() == Qt::WindowMinimized) {
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::MINIMIZED);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -4396,6 +4413,13 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
|
|||
|
||||
}
|
||||
|
||||
void Application::focusInEvent(QFocusEvent* event) {
|
||||
if (!_aboutToQuit && _startUpFinished) {
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Application::focusOutEvent(QFocusEvent* event) {
|
||||
auto inputPlugins = PluginManager::getInstance()->getInputPlugins();
|
||||
foreach(auto inputPlugin, inputPlugins) {
|
||||
|
@ -4404,6 +4428,9 @@ void Application::focusOutEvent(QFocusEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!_aboutToQuit && _startUpFinished) {
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::UNFOCUS);
|
||||
}
|
||||
// FIXME spacemouse code still needs cleanup
|
||||
#if 0
|
||||
//SpacemouseDevice::getInstance().focusOutEvent();
|
||||
|
@ -5571,6 +5598,8 @@ void Application::resumeAfterLoginDialogActionTaken() {
|
|||
menu->getMenu("Developer")->setVisible(_developerMenuVisible);
|
||||
_myCamera.setMode(_previousCameraMode);
|
||||
cameraModeChanged();
|
||||
_startUpFinished = true;
|
||||
getRefreshRateManager().setRefreshRateRegime(RefreshRateManager::RefreshRateRegime::RUNNING);
|
||||
}
|
||||
|
||||
void Application::loadAvatarScripts(const QVector<QString>& urls) {
|
||||
|
@ -7353,6 +7382,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerGlobalObject("LODManager", DependencyManager::get<LODManager>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("Keyboard", DependencyManager::get<KeyboardScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("RefreshRate", new RefreshRateScriptingInterface);
|
||||
|
||||
scriptEngine->registerGlobalObject("Paths", DependencyManager::get<PathUtils>().data());
|
||||
|
||||
|
@ -8753,6 +8783,7 @@ void Application::updateDisplayMode() {
|
|||
auto displayPlugins = getDisplayPlugins();
|
||||
|
||||
// Default to the first item on the list, in case none of the menu items match
|
||||
|
||||
DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0);
|
||||
auto menu = getPrimaryMenu();
|
||||
if (menu) {
|
||||
|
@ -8842,6 +8873,14 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
|
|||
if (desktop) {
|
||||
desktop->setProperty("repositionLocked", wasRepositionLocked);
|
||||
}
|
||||
|
||||
RefreshRateManager& refreshRateManager = getRefreshRateManager();
|
||||
refreshRateManager.setRefreshRateOperator(OpenGLDisplayPlugin::getRefreshRateOperator());
|
||||
bool isHmd = newDisplayPlugin->isHmd();
|
||||
RefreshRateManager::UXMode uxMode = isHmd ? RefreshRateManager::UXMode::HMD :
|
||||
RefreshRateManager::UXMode::DESKTOP;
|
||||
|
||||
refreshRateManager.setUXMode(uxMode);
|
||||
}
|
||||
|
||||
bool isHmd = _displayPlugin->isHmd();
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "gpu/Context.h"
|
||||
#include "LoginStateManager.h"
|
||||
#include "Menu.h"
|
||||
#include "RefreshRateManager.h"
|
||||
#include "octree/OctreePacketProcessor.h"
|
||||
#include "render/Engine.h"
|
||||
#include "scripting/ControllerScriptingInterface.h"
|
||||
|
@ -203,6 +204,7 @@ public:
|
|||
CompositorHelper& getApplicationCompositor() const;
|
||||
|
||||
Overlays& getOverlays() { return _overlays; }
|
||||
RefreshRateManager& getRefreshRateManager() { return _refreshRateManager; }
|
||||
|
||||
size_t getRenderFrameCount() const { return _graphicsEngine.getRenderFrameCount(); }
|
||||
float getRenderLoopRate() const { return _graphicsEngine.getRenderLoopRate(); }
|
||||
|
@ -476,6 +478,7 @@ public slots:
|
|||
|
||||
QString getGraphicsCardType();
|
||||
|
||||
bool gpuTextureMemSizeStable();
|
||||
void showUrlHandler(const QUrl& url);
|
||||
|
||||
private slots:
|
||||
|
@ -572,7 +575,6 @@ private:
|
|||
bool importFromZIP(const QString& filePath);
|
||||
bool importImage(const QString& urlString);
|
||||
|
||||
bool gpuTextureMemSizeStable();
|
||||
int processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode);
|
||||
void trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket);
|
||||
|
||||
|
@ -723,6 +725,7 @@ private:
|
|||
QUuid _loginDialogID;
|
||||
QUuid _avatarInputsBarID;
|
||||
LoginStateManager _loginStateManager;
|
||||
RefreshRateManager _refreshRateManager;
|
||||
|
||||
quint64 _lastFaceTrackerUpdate;
|
||||
|
||||
|
@ -820,5 +823,6 @@ private:
|
|||
|
||||
bool _resumeAfterLoginDialogActionTaken_WasPostponed { false };
|
||||
bool _resumeAfterLoginDialogActionTaken_SafeToRun { false };
|
||||
bool _startUpFinished { false };
|
||||
};
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -420,9 +420,21 @@ Menu::Menu() {
|
|||
MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution);
|
||||
QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu);
|
||||
resolutionGroup->setExclusive(true);
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, false));
|
||||
#else
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true));
|
||||
#endif
|
||||
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionTwoThird, 0, false));
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, true));
|
||||
#else
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, false));
|
||||
#endif
|
||||
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
|
||||
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
|
||||
|
||||
|
@ -613,6 +625,8 @@ Menu::Menu() {
|
|||
avatar.get(), SLOT(setEnableDebugDrawAnimPose(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawPosition, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawPosition(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawOtherSkeletons, 0, false,
|
||||
avatarManager.data(), SLOT(setEnableDebugDrawOtherSkeletons(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true,
|
||||
avatar.get(), SLOT(setEnableMeshVisible(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace MenuOption {
|
|||
const QString AnimDebugDrawBaseOfSupport = "Debug Draw Base of Support";
|
||||
const QString AnimDebugDrawDefaultPose = "Debug Draw Default Pose";
|
||||
const QString AnimDebugDrawPosition= "Debug Draw Position";
|
||||
const QString AnimDebugDrawOtherSkeletons = "Debug Draw Other Skeletons";
|
||||
const QString AskToResetSettings = "Ask To Reset Settings on Start";
|
||||
const QString AssetMigration = "ATP Asset Migration";
|
||||
const QString AssetServer = "Asset Browser";
|
||||
|
|
149
interface/src/RefreshRateManager.cpp
Normal file
149
interface/src/RefreshRateManager.cpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
//
|
||||
// RefreshRateManager.cpp
|
||||
// interface/src/
|
||||
//
|
||||
// Created by Dante Ruiz on 2019-04-15.
|
||||
// Copyright 2019 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 "RefreshRateManager.h"
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
||||
|
||||
#include <Application.h>
|
||||
|
||||
#include <display-plugins/hmd/HmdDisplayPlugin.h>
|
||||
|
||||
static const int HMD_TARGET_RATE = 90;
|
||||
|
||||
static const std::array<std::string, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> REFRESH_RATE_PROFILE_TO_STRING =
|
||||
{ { "Eco", "Interactive", "Realtime" } };
|
||||
|
||||
static const std::array<std::string, RefreshRateManager::RefreshRateRegime::REGIME_NUM> REFRESH_RATE_REGIME_TO_STRING =
|
||||
{ { "Running", "Unfocus", "Minimized", "StartUp", "ShutDown" } };
|
||||
|
||||
static const std::array<std::string, RefreshRateManager::UXMode::UX_NUM> UX_MODE_TO_STRING =
|
||||
{ { "Desktop", "HMD" } };
|
||||
|
||||
static const std::map<std::string, RefreshRateManager::RefreshRateProfile> REFRESH_RATE_PROFILE_FROM_STRING =
|
||||
{ { "Eco", RefreshRateManager::RefreshRateProfile::ECO },
|
||||
{ "Interactive", RefreshRateManager::RefreshRateProfile::INTERACTIVE },
|
||||
{ "Realtime", RefreshRateManager::RefreshRateProfile::REALTIME } };
|
||||
|
||||
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> RUNNING_REGIME_PROFILES =
|
||||
{ { 5, 20, 60 } };
|
||||
|
||||
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> UNFOCUS_REGIME_PROFILES =
|
||||
{ { 5, 5, 10 } };
|
||||
|
||||
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> MINIMIZED_REGIME_PROFILE =
|
||||
{ { 2, 2, 2 } };
|
||||
|
||||
static const std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM> START_AND_SHUTDOWN_REGIME_PROFILES =
|
||||
{ { 30, 30, 30 } };
|
||||
|
||||
static const std::array<std::array<int, RefreshRateManager::RefreshRateProfile::PROFILE_NUM>, RefreshRateManager::RefreshRateRegime::REGIME_NUM> REFRESH_RATE_REGIMES =
|
||||
{ { RUNNING_REGIME_PROFILES, UNFOCUS_REGIME_PROFILES, MINIMIZED_REGIME_PROFILE,
|
||||
START_AND_SHUTDOWN_REGIME_PROFILES, START_AND_SHUTDOWN_REGIME_PROFILES } };
|
||||
|
||||
|
||||
std::string RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile refreshRateProfile) {
|
||||
return REFRESH_RATE_PROFILE_TO_STRING.at(refreshRateProfile);
|
||||
}
|
||||
|
||||
RefreshRateManager::RefreshRateProfile RefreshRateManager::refreshRateProfileFromString(std::string refreshRateProfile) {
|
||||
return REFRESH_RATE_PROFILE_FROM_STRING.at(refreshRateProfile);
|
||||
}
|
||||
|
||||
std::string RefreshRateManager::refreshRateRegimeToString(RefreshRateManager::RefreshRateRegime refreshRateRegime) {
|
||||
return REFRESH_RATE_REGIME_TO_STRING.at(refreshRateRegime);
|
||||
}
|
||||
|
||||
std::string RefreshRateManager::uxModeToString(RefreshRateManager::RefreshRateManager::UXMode uxMode) {
|
||||
return UX_MODE_TO_STRING.at(uxMode);
|
||||
}
|
||||
|
||||
RefreshRateManager::RefreshRateManager() {
|
||||
_refreshRateProfile = (RefreshRateManager::RefreshRateProfile) _refreshRateMode.get();
|
||||
}
|
||||
|
||||
void RefreshRateManager::setRefreshRateProfile(RefreshRateManager::RefreshRateProfile refreshRateProfile) {
|
||||
if (_refreshRateProfile != refreshRateProfile) {
|
||||
_refreshRateModeLock.withWriteLock([&] {
|
||||
_refreshRateProfile = refreshRateProfile;
|
||||
_refreshRateMode.set((int) refreshRateProfile);
|
||||
});
|
||||
updateRefreshRateController();
|
||||
}
|
||||
}
|
||||
|
||||
RefreshRateManager::RefreshRateProfile RefreshRateManager::getRefreshRateProfile() const {
|
||||
RefreshRateManager::RefreshRateProfile profile = RefreshRateManager::RefreshRateProfile::REALTIME;
|
||||
|
||||
if (getUXMode() != RefreshRateManager::UXMode::HMD) {
|
||||
profile =(RefreshRateManager::RefreshRateProfile) _refreshRateModeLock.resultWithReadLock<int>([&] {
|
||||
return _refreshRateMode.get();
|
||||
});
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
RefreshRateManager::RefreshRateRegime RefreshRateManager::getRefreshRateRegime() const {
|
||||
return getUXMode() == RefreshRateManager::UXMode::HMD ? RefreshRateManager::RefreshRateRegime::RUNNING :
|
||||
_refreshRateRegime;
|
||||
}
|
||||
|
||||
void RefreshRateManager::setRefreshRateRegime(RefreshRateManager::RefreshRateRegime refreshRateRegime) {
|
||||
if (_refreshRateRegime != refreshRateRegime) {
|
||||
_refreshRateRegime = refreshRateRegime;
|
||||
updateRefreshRateController();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RefreshRateManager::setUXMode(RefreshRateManager::UXMode uxMode) {
|
||||
if (_uxMode != uxMode) {
|
||||
_uxMode = uxMode;
|
||||
updateRefreshRateController();
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshRateManager::updateRefreshRateController() const {
|
||||
if (_refreshRateOperator) {
|
||||
int targetRefreshRate;
|
||||
if (_uxMode == RefreshRateManager::UXMode::DESKTOP) {
|
||||
if (_refreshRateRegime == RefreshRateManager::RefreshRateRegime::RUNNING &&
|
||||
_refreshRateProfile == RefreshRateManager::RefreshRateProfile::INTERACTIVE) {
|
||||
targetRefreshRate = getInteractiveRefreshRate();
|
||||
} else {
|
||||
targetRefreshRate = REFRESH_RATE_REGIMES[_refreshRateRegime][_refreshRateProfile];
|
||||
}
|
||||
} else {
|
||||
targetRefreshRate = HMD_TARGET_RATE;
|
||||
}
|
||||
|
||||
_refreshRateOperator(targetRefreshRate);
|
||||
_activeRefreshRate = targetRefreshRate;
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshRateManager::setInteractiveRefreshRate(int refreshRate) {
|
||||
_refreshRateLock.withWriteLock([&] {
|
||||
_interactiveRefreshRate.set(refreshRate);
|
||||
});
|
||||
updateRefreshRateController();
|
||||
}
|
||||
|
||||
|
||||
int RefreshRateManager::getInteractiveRefreshRate() const {
|
||||
return _refreshRateLock.resultWithReadLock<int>([&] {
|
||||
return _interactiveRefreshRate.get();
|
||||
});
|
||||
}
|
83
interface/src/RefreshRateManager.h
Normal file
83
interface/src/RefreshRateManager.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// RefreshRateManager.h
|
||||
// interface/src/
|
||||
//
|
||||
// Created by Dante Ruiz on 2019-04-15.
|
||||
// Copyright 2019 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_RefreshRateManager_h
|
||||
#define hifi_RefreshRateManager_h
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <SettingHandle.h>
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
|
||||
class RefreshRateManager {
|
||||
public:
|
||||
enum RefreshRateProfile {
|
||||
ECO = 0,
|
||||
INTERACTIVE,
|
||||
REALTIME,
|
||||
PROFILE_NUM
|
||||
};
|
||||
|
||||
enum RefreshRateRegime {
|
||||
RUNNING = 0,
|
||||
UNFOCUS,
|
||||
MINIMIZED,
|
||||
STARTUP,
|
||||
SHUTDOWN,
|
||||
REGIME_NUM
|
||||
};
|
||||
|
||||
enum UXMode {
|
||||
DESKTOP = 0,
|
||||
HMD,
|
||||
UX_NUM
|
||||
};
|
||||
|
||||
RefreshRateManager();
|
||||
~RefreshRateManager() = default;
|
||||
|
||||
void setRefreshRateProfile(RefreshRateProfile refreshRateProfile);
|
||||
RefreshRateProfile getRefreshRateProfile() const;
|
||||
|
||||
void setRefreshRateRegime(RefreshRateRegime refreshRateRegime);
|
||||
RefreshRateRegime getRefreshRateRegime() const;
|
||||
|
||||
void setUXMode(UXMode uxMode);
|
||||
UXMode getUXMode() const { return _uxMode; }
|
||||
|
||||
void setRefreshRateOperator(std::function<void(int)> refreshRateOperator) { _refreshRateOperator = refreshRateOperator; }
|
||||
int getActiveRefreshRate() const { return _activeRefreshRate; }
|
||||
void updateRefreshRateController() const;
|
||||
void setInteractiveRefreshRate(int refreshRate);
|
||||
int getInteractiveRefreshRate() const;
|
||||
|
||||
static std::string refreshRateProfileToString(RefreshRateProfile refreshRateProfile);
|
||||
static RefreshRateProfile refreshRateProfileFromString(std::string refreshRateProfile);
|
||||
static std::string uxModeToString(UXMode uxMode);
|
||||
static std::string refreshRateRegimeToString(RefreshRateRegime refreshRateRegime);
|
||||
|
||||
private:
|
||||
mutable ReadWriteLockable _refreshRateLock;
|
||||
mutable ReadWriteLockable _refreshRateModeLock;
|
||||
|
||||
mutable int _activeRefreshRate { 20 };
|
||||
RefreshRateProfile _refreshRateProfile { RefreshRateProfile::INTERACTIVE};
|
||||
RefreshRateRegime _refreshRateRegime { RefreshRateRegime::STARTUP };
|
||||
UXMode _uxMode;
|
||||
|
||||
Setting::Handle<int> _interactiveRefreshRate { "interactiveRefreshRate", 20};
|
||||
Setting::Handle<int> _refreshRateMode { "refreshRateProfile", INTERACTIVE };
|
||||
|
||||
std::function<void(int)> _refreshRateOperator { nullptr };
|
||||
};
|
||||
|
||||
#endif
|
|
@ -120,6 +120,8 @@ void AvatarManager::init() {
|
|||
_myAvatar->addToScene(_myAvatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
setEnableDebugDrawOtherSkeletons(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawOtherSkeletons));
|
||||
}
|
||||
|
||||
void AvatarManager::setSpace(workload::SpacePointer& space ) {
|
||||
|
@ -334,9 +336,14 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
if (avatar->getSkeletonModel()->isLoaded() && avatar->getWorkloadRegion() == workload::Region::R1) {
|
||||
_myAvatar->addAvatarHandsToFlow(avatar);
|
||||
}
|
||||
if (_drawOtherAvatarSkeletons) {
|
||||
avatar->debugJointData();
|
||||
}
|
||||
avatar->setEnableMeshVisible(!_drawOtherAvatarSkeletons);
|
||||
avatar->updateRenderItem(renderTransaction);
|
||||
avatar->updateSpaceProxy(workloadTransaction);
|
||||
avatar->setLastRenderUpdateTime(startTime);
|
||||
|
||||
} else {
|
||||
// we've spent our time budget for this priority bucket
|
||||
// let's deal with the reminding avatars if this pass and BREAK from the for loop
|
||||
|
@ -942,7 +949,8 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV
|
|||
* It is unique among all avatars present in the domain at the time.
|
||||
* @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the
|
||||
* domain.
|
||||
* @property {boolean} isReplicated - <strong>Deprecated.</strong>
|
||||
* @property {boolean} isReplicated - <span class="important">Deprecated: This property is deprecated and will be
|
||||
* removed.</span>
|
||||
* @property {Vec3} position - The position of the avatar.
|
||||
* @property {number} palOrbOffset - The vertical offset from the avatar's position that an overlay orb should be displayed at.
|
||||
*/
|
||||
|
|
|
@ -262,6 +262,15 @@ public slots:
|
|||
*/
|
||||
void updateAvatarRenderStatus(bool shouldRenderAvatars);
|
||||
|
||||
/**jsdoc
|
||||
* Displays other avatars skeletons debug graphics.
|
||||
* @function AvatarManager.setEnableDebugDrawOtherSkeletons
|
||||
* @param {boolean} enabled - <code>true</code> to show the debug graphics, <code>false</code> to hide.
|
||||
*/
|
||||
void setEnableDebugDrawOtherSkeletons(bool isEnabled) {
|
||||
_drawOtherAvatarSkeletons = isEnabled;
|
||||
}
|
||||
|
||||
protected:
|
||||
AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) override;
|
||||
|
||||
|
@ -299,6 +308,7 @@ private:
|
|||
workload::SpacePointer _space;
|
||||
|
||||
AvatarTransit::TransitConfig _transitConfig;
|
||||
bool _drawOtherAvatarSkeletons { false };
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarManager_h
|
||||
|
|
|
@ -168,6 +168,7 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_displayNameSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "displayName", ""),
|
||||
_collisionSoundURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "collisionSoundURL", QUrl(_collisionSoundURL)),
|
||||
_useSnapTurnSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "useSnapTurn", _useSnapTurn),
|
||||
_hoverWhenUnsupportedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hoverWhenUnsupported", _hoverWhenUnsupported),
|
||||
_userHeightSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userHeight", DEFAULT_AVATAR_HEIGHT),
|
||||
_flyingHMDSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "flyingHMD", _flyingPrefHMD),
|
||||
_movementReferenceSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "movementReference", _movementReference),
|
||||
|
@ -948,6 +949,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
|||
bool collisionlessAllowed = zoneInteractionProperties.second;
|
||||
_characterController.setZoneFlyingAllowed(zoneAllowsFlying || !isPhysicsEnabled);
|
||||
_characterController.setComfortFlyingAllowed(_enableFlying);
|
||||
_characterController.setHoverWhenUnsupported(_hoverWhenUnsupported);
|
||||
_characterController.setCollisionlessAllowed(collisionlessAllowed);
|
||||
}
|
||||
|
||||
|
@ -1041,11 +1043,15 @@ void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeV
|
|||
assert(QThread::currentThread() == thread());
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
controller::Pose controllerPose = userInputMapper->getPoseState(poseKey);
|
||||
Transform transform;
|
||||
transform.setTranslation(controllerPose.getTranslation());
|
||||
transform.setRotation(controllerPose.getRotation());
|
||||
glm::mat4 controllerMatrix = transform.getMatrix();
|
||||
matrixCache.set(controllerMatrix);
|
||||
if (controllerPose.isValid()) {
|
||||
Transform transform;
|
||||
transform.setTranslation(controllerPose.getTranslation());
|
||||
transform.setRotation(controllerPose.getRotation());
|
||||
glm::mat4 controllerMatrix = transform.getMatrix();
|
||||
matrixCache.set(controllerMatrix);
|
||||
} else {
|
||||
matrixCache.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
// best called at end of main loop, after physics.
|
||||
|
@ -1305,6 +1311,7 @@ void MyAvatar::saveData() {
|
|||
_displayNameSetting.set(_displayName);
|
||||
_collisionSoundURLSetting.set(_collisionSoundURL);
|
||||
_useSnapTurnSetting.set(_useSnapTurn);
|
||||
_hoverWhenUnsupportedSetting.set(_hoverWhenUnsupported);
|
||||
_userHeightSetting.set(getUserHeight());
|
||||
_flyingHMDSetting.set(getFlyingHMDPref());
|
||||
_movementReferenceSetting.set(getMovementReference());
|
||||
|
@ -1909,6 +1916,7 @@ void MyAvatar::loadData() {
|
|||
setDisplayName(_displayNameSetting.get());
|
||||
setCollisionSoundURL(_collisionSoundURLSetting.get(QUrl(DEFAULT_AVATAR_COLLISION_SOUND_URL)).toString());
|
||||
setSnapTurn(_useSnapTurnSetting.get());
|
||||
setHoverWhenUnsupported(_hoverWhenUnsupportedSetting.get());
|
||||
setDominantHand(_dominantHandSetting.get(DOMINANT_RIGHT_HAND).toLower());
|
||||
setStrafeEnabled(_strafeEnabledSetting.get(DEFAULT_STRAFE_ENABLED));
|
||||
setHmdAvatarAlignmentType(_hmdAvatarAlignmentTypeSetting.get(DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE).toLower());
|
||||
|
|
|
@ -116,8 +116,8 @@ class MyAvatar : public Avatar {
|
|||
* @property {boolean} lookAtSnappingEnabled=true - <code>true</code> if the avatar's eyes snap to look at another avatar's
|
||||
* eyes when the other avatar is in the line of sight and also has <code>lookAtSnappingEnabled == true</code>.
|
||||
* @property {string} skeletonModelURL - The avatar's FST file.
|
||||
* @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.<br />
|
||||
* <strong>Deprecated:</strong> Use avatar entities instead.
|
||||
* @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed. Use avatar entities instead.</p>
|
||||
* @property {string[]} jointNames - The list of joints in the current avatar model. <em>Read-only.</em>
|
||||
* @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. <em>Read-only.</em>
|
||||
* @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the
|
||||
|
@ -198,7 +198,7 @@ class MyAvatar : public Avatar {
|
|||
* @property {Pose} rightHandTipPose - The right hand's pose as determined by the hand controllers, relative to the avatar,
|
||||
* with the position adjusted by 0.3m along the direction of the palm. <em>Read-only.</em>
|
||||
*
|
||||
* @property {number} energy - <strong>Deprecated:</strong> This property will be removed from the API.
|
||||
* @property {number} energy - <span class="important">Deprecated: This property will be removed.</span>
|
||||
* @property {boolean} isAway - <code>true</code> if your avatar is away (i.e., inactive), <code>false</code> if it is
|
||||
* active.
|
||||
*
|
||||
|
@ -213,8 +213,9 @@ class MyAvatar : public Avatar {
|
|||
* was set <code>false</code> because the zone may disallow collisionless avatars.
|
||||
* @property {boolean} otherAvatarsCollisionsEnabled - Set to <code>true</code> to enable the avatar to collide with other
|
||||
* avatars, <code>false</code> to disable collisions with other avatars.
|
||||
* @property {boolean} characterControllerEnabled - Synonym of <code>collisionsEnabled</code>.<br />
|
||||
* <strong>Deprecated:</strong> Use <code>collisionsEnabled</code> instead.
|
||||
* @property {boolean} characterControllerEnabled - Synonym of <code>collisionsEnabled</code>.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed. Use <code>collisionsEnabled</code>
|
||||
* instead.</p>
|
||||
* @property {boolean} useAdvancedMovementControls - Returns and sets the value of the Interface setting, Settings >
|
||||
* Controls > Walking. Note: Setting the value has no effect unless Interface is restarted.
|
||||
* @property {boolean} showPlayArea - Returns and sets the value of the Interface setting, Settings > Controls > Show room
|
||||
|
@ -797,6 +798,18 @@ public:
|
|||
* @param {number} index
|
||||
*/
|
||||
Q_INVOKABLE void setControlScheme(int index) { _controlSchemeIndex = (index >= 0 && index <= 2) ? index : 0; }
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.hoverWhenUnsupported
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool hoverWhenUnsupported() const { return _hoverWhenUnsupported; }
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setHoverWhenUnsupported
|
||||
* @param {boolean} on
|
||||
*/
|
||||
Q_INVOKABLE void setHoverWhenUnsupported(bool on) { _hoverWhenUnsupported = on; }
|
||||
|
||||
/**jsdoc
|
||||
* Sets the avatar's dominant hand.
|
||||
* @function MyAvatar.setDominantHand
|
||||
|
@ -1557,14 +1570,14 @@ public:
|
|||
* @function MyAvatar.setCharacterControllerEnabled
|
||||
* @param {boolean} enabled - <code>true</code> to enable the avatar to collide with entities, <code>false</code> to
|
||||
* disable.
|
||||
* @deprecated Use {@link MyAvatar.setCollisionsEnabled} instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use {@link MyAvatar.setCollisionsEnabled} instead.
|
||||
*/
|
||||
Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); // deprecated
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.getCharacterControllerEnabled
|
||||
* @returns {boolean} <code>true</code> if the avatar will currently collide with entities, <code>false</code> if it won't.
|
||||
* @deprecated Use {@link MyAvatar.getCollisionsEnabled} instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use {@link MyAvatar.getCollisionsEnabled} instead.
|
||||
*/
|
||||
Q_INVOKABLE bool getCharacterControllerEnabled(); // deprecated
|
||||
|
||||
|
@ -1910,7 +1923,7 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.clearScaleRestriction
|
||||
* @deprecated This function is deprecated and will be removed from the API.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
void clearScaleRestriction();
|
||||
|
||||
|
@ -1919,7 +1932,8 @@ public slots:
|
|||
* Adds a thrust to your avatar's current thrust to be applied for a short while.
|
||||
* @function MyAvatar.addThrust
|
||||
* @param {Vec3} thrust - The thrust direction and magnitude.
|
||||
* @deprecated Use {@link MyAvatar|MyAvatar.motorVelocity} and related properties instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use {@link MyAvatar|MyAvatar.motorVelocity} and related
|
||||
* properties instead.
|
||||
*/
|
||||
// Set/Get update the thrust that will move the avatar around
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
|
@ -1928,7 +1942,8 @@ public slots:
|
|||
* Gets the thrust currently being applied to your avatar.
|
||||
* @function MyAvatar.getThrust
|
||||
* @returns {Vec3} The thrust currently being applied to your avatar.
|
||||
* @deprecated Use {@link MyAvatar|MyAvatar.motorVelocity} and related properties instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use {@link MyAvatar|MyAvatar.motorVelocity} and related
|
||||
* properties instead.
|
||||
*/
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
|
||||
|
@ -1936,7 +1951,8 @@ public slots:
|
|||
* Sets the thrust to be applied to your avatar for a short while.
|
||||
* @function MyAvatar.setThrust
|
||||
* @param {Vec3} thrust - The thrust direction and magnitude.
|
||||
* @deprecated Use {@link MyAvatar|MyAvatar.motorVelocity} and related properties instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use {@link MyAvatar|MyAvatar.motorVelocity} and related
|
||||
* properties instead.
|
||||
*/
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
|
||||
|
@ -2281,7 +2297,7 @@ signals:
|
|||
* {@link MyAvatar.setAttachmentData|setAttachmentData}.
|
||||
* @function MyAvatar.attachmentsChanged
|
||||
* @returns {Signal}
|
||||
* @deprecated Use avatar entities instead.
|
||||
* @deprecated This signal is deprecated and will be removed. Use avatar entities instead.
|
||||
*/
|
||||
void attachmentsChanged();
|
||||
|
||||
|
@ -2480,6 +2496,7 @@ private:
|
|||
ThreadSafeValueCache<QUrl> _prefOverrideAnimGraphUrl;
|
||||
QUrl _fstAnimGraphOverrideUrl;
|
||||
bool _useSnapTurn { true };
|
||||
bool _hoverWhenUnsupported{ true };
|
||||
ThreadSafeValueCache<QString> _dominantHand { DOMINANT_RIGHT_HAND };
|
||||
ThreadSafeValueCache<QString> _hmdAvatarAlignmentType { DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE };
|
||||
ThreadSafeValueCache<bool> _strafeEnabled{ DEFAULT_STRAFE_ENABLED };
|
||||
|
@ -2675,6 +2692,7 @@ private:
|
|||
Setting::Handle<QString> _displayNameSetting;
|
||||
Setting::Handle<QUrl> _collisionSoundURLSetting;
|
||||
Setting::Handle<bool> _useSnapTurnSetting;
|
||||
Setting::Handle<bool> _hoverWhenUnsupportedSetting;
|
||||
Setting::Handle<float> _userHeightSetting;
|
||||
Setting::Handle<bool> _flyingHMDSetting;
|
||||
Setting::Handle<int> _movementReferenceSetting;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "Application.h"
|
||||
#include "AvatarMotionState.h"
|
||||
#include "DetailedMotionState.h"
|
||||
#include "DebugDraw.h"
|
||||
|
||||
const float DISPLAYNAME_FADE_TIME = 0.5f;
|
||||
const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
|
||||
|
@ -358,6 +359,58 @@ void OtherAvatar::simulate(float deltaTime, bool inView) {
|
|||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::debugJointData() const {
|
||||
// Get a copy of the joint data
|
||||
auto jointData = getJointData();
|
||||
auto skeletonData = getSkeletonData();
|
||||
if ((int)skeletonData.size() == jointData.size() && jointData.size() != 0) {
|
||||
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
const vec4 LIGHT_RED(1.0f, 0.5f, 0.5f, 1.0f);
|
||||
const vec4 LIGHT_GREEN(0.5f, 1.0f, 0.5f, 1.0f);
|
||||
const vec4 LIGHT_BLUE(0.5f, 0.5f, 1.0f, 1.0f);
|
||||
const vec4 GREY(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
const vec4 WHITE(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
const float AXIS_LENGTH = 0.1f;
|
||||
|
||||
AnimPoseVec absoluteJointPoses;
|
||||
AnimPose rigToAvatar = AnimPose(Quaternions::Y_180 * getWorldOrientation(), getWorldPosition());
|
||||
bool drawBones = false;
|
||||
for (int i = 0; i < jointData.size(); i++) {
|
||||
float jointScale = skeletonData[i].defaultScale * getTargetScale() * METERS_PER_CENTIMETER;
|
||||
auto absoluteRotation = jointData[i].rotationIsDefaultPose ? skeletonData[i].defaultRotation : jointData[i].rotation;
|
||||
auto localJointTranslation = jointScale * (jointData[i].translationIsDefaultPose ? skeletonData[i].defaultTranslation : jointData[i].translation);
|
||||
bool isHips = skeletonData[i].jointName == "Hips";
|
||||
if (isHips) {
|
||||
localJointTranslation = glm::vec3(0.0f);
|
||||
drawBones = true;
|
||||
}
|
||||
AnimPose absoluteParentPose;
|
||||
int parentIndex = skeletonData[i].parentIndex;
|
||||
if (parentIndex != -1 && parentIndex < (int)absoluteJointPoses.size()) {
|
||||
absoluteParentPose = absoluteJointPoses[parentIndex];
|
||||
}
|
||||
AnimPose absoluteJointPose = AnimPose(absoluteRotation, absoluteParentPose.trans() + absoluteParentPose.rot() * localJointTranslation);
|
||||
auto jointPose = rigToAvatar * absoluteJointPose;
|
||||
auto parentPose = rigToAvatar * absoluteParentPose;
|
||||
if (drawBones) {
|
||||
glm::vec3 xAxis = jointPose.rot() * Vectors::UNIT_X;
|
||||
glm::vec3 yAxis = jointPose.rot() * Vectors::UNIT_Y;
|
||||
glm::vec3 zAxis = jointPose.rot() * Vectors::UNIT_Z;
|
||||
|
||||
DebugDraw::getInstance().drawRay(jointPose.trans(), jointPose.trans() + AXIS_LENGTH * xAxis, jointData[i].rotationIsDefaultPose ? LIGHT_RED : RED);
|
||||
DebugDraw::getInstance().drawRay(jointPose.trans(), jointPose.trans() + AXIS_LENGTH * yAxis, jointData[i].rotationIsDefaultPose ? LIGHT_GREEN : GREEN);
|
||||
DebugDraw::getInstance().drawRay(jointPose.trans(), jointPose.trans() + AXIS_LENGTH * zAxis, jointData[i].rotationIsDefaultPose ? LIGHT_BLUE : BLUE);
|
||||
if (!isHips) {
|
||||
DebugDraw::getInstance().drawRay(jointPose.trans(), parentPose.trans(), jointData[i].translationIsDefaultPose ? WHITE : GREY);
|
||||
}
|
||||
}
|
||||
absoluteJointPoses.push_back(absoluteJointPose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::handleChangedAvatarEntityData() {
|
||||
PerformanceTimer perfTimer("attachments");
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
void setCollisionWithOtherAvatarsFlags() override;
|
||||
|
||||
void simulate(float deltaTime, bool inView) override;
|
||||
|
||||
void debugJointData() const;
|
||||
friend AvatarManager;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -40,25 +40,40 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
|||
* @hifi-server-entity
|
||||
* @hifi-assignment-client
|
||||
*
|
||||
* @property {boolean} muted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
|
||||
* @property {boolean} mutedDesktop - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
|
||||
* @property {boolean} muted - <code>true</code> if the audio input is muted for the current user context (desktop or HMD),
|
||||
* otherwise <code>false</code>.
|
||||
* @property {boolean} mutedDesktop - <code>true</code> if desktop audio input is muted, otherwise <code>false</code>.
|
||||
* @property {boolean} mutedHMD - <code>true</code> if the HMD input is muted, otherwise <code>false</code>.
|
||||
* @property {boolean} warnWhenMuted - <code>true</code> if the "muted" warning is enabled, otherwise <code>false</code>.
|
||||
* When enabled, if you speak while your microphone is muted, "muted" is displayed on the screen as a warning.
|
||||
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
|
||||
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
|
||||
* above the noise floor.
|
||||
* @property {number} inputVolume - Adjusts the volume of the input audio, range <code>0.0</code> – <code>1.0</code>.
|
||||
* If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some
|
||||
* devices, and others might only support values of <code>0.0</code> and <code>1.0</code>.
|
||||
* @property {number} inputLevel - The loudness of the audio input, range <code>0.0</code> (no sound) –
|
||||
* <code>1.0</code> (the onset of clipping). <em>Read-only.</em>
|
||||
* @property {boolean} clipping - <code>true</code> if the audio input is clipping, otherwise <code>false</code>.
|
||||
* @property {number} inputVolume - Adjusts the volume of the input audio; range <code>0.0</code> – <code>1.0</code>.
|
||||
* If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some
|
||||
* devices, and others might only support values of <code>0.0</code> and <code>1.0</code>.
|
||||
* @property {boolean} isStereoInput - <code>true</code> if the input audio is being used in stereo, otherwise
|
||||
* <code>false</code>. Some devices do not support stereo, in which case the value is always <code>false</code>.
|
||||
* @property {string} context - The current context of the audio: either <code>"Desktop"</code> or <code>"HMD"</code>.
|
||||
* <em>Read-only.</em>
|
||||
* @property {object} devices <em>Read-only.</em> <strong>Deprecated:</strong> This property is deprecated and will be
|
||||
* removed.
|
||||
* @property {boolean} isSoloing <em>Read-only.</em> <code>true</code> if any nodes are soloed.
|
||||
* @property {Uuid[]} soloList <em>Read-only.</em> Get the list of currently soloed node UUIDs.
|
||||
* @property {object} devices - <em>Read-only.</em>
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed.
|
||||
* @property {boolean} pushToTalk - <code>true</code> if push-to-talk is enabled for the current user context (desktop or
|
||||
* HMD), otherwise <code>false</code>.
|
||||
* @property {boolean} pushToTalkDesktop - <code>true</code> if desktop push-to-talk is enabled, otherwise
|
||||
* <code>false</code>.
|
||||
* @property {boolean} pushToTalkHMD - <code>true</code> if HMD push-to-talk is enabled, otherwise <code>false</code>.
|
||||
* @property {boolean} pushingToTalk - <code>true</code> if the user is currently pushing-to-talk, otherwise
|
||||
* <code>false</code>.
|
||||
*
|
||||
* @comment The following properties are from AudioScriptingInterface.h.
|
||||
* @property {boolean} isStereoInput - <code>true</code> if the input audio is being used in stereo, otherwise
|
||||
* <code>false</code>. Some devices do not support stereo, in which case the value is always <code>false</code>.
|
||||
* @property {boolean} isSoloing - <code>true</code> if currently audio soloing, i.e., playing audio from only specific
|
||||
* avatars. <em>Read-only.</em>
|
||||
* @property {Uuid[]} soloList - The list of currently soloed avatar IDs. Empty list if not currently audio soloing.
|
||||
* <em>Read-only.</em>
|
||||
*/
|
||||
|
||||
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
|
||||
|
@ -117,23 +132,23 @@ public:
|
|||
|
||||
/**jsdoc
|
||||
* @function Audio.setInputDevice
|
||||
* @param {object} device
|
||||
* @param {boolean} isHMD
|
||||
* @param {object} device - Device.
|
||||
* @param {boolean} isHMD - Is HMD.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
|
||||
/**jsdoc
|
||||
* @function Audio.setOutputDevice
|
||||
* @param {object} device
|
||||
* @param {boolean} isHMD
|
||||
* @param {object} device - Device.
|
||||
* @param {boolean} isHMD - Is HMD.
|
||||
* @deprecated This function is deprecated and will be removed.
|
||||
*/
|
||||
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
|
||||
|
||||
/**jsdoc
|
||||
* Enable or disable reverberation. Reverberation is done by the client, on the post-mix audio. The reverberation options
|
||||
* come from either the domain's audio zone if used — configured on the server — or as scripted by
|
||||
* Enables or disables reverberation. Reverberation is done by the client on the post-mix audio. The reverberation options
|
||||
* come from either the domain's audio zone configured on the server or settings scripted by
|
||||
* {@link Audio.setReverbOptions|setReverbOptions}.
|
||||
* @function Audio.setReverb
|
||||
* @param {boolean} enable - <code>true</code> to enable reverberation, <code>false</code> to disable.
|
||||
|
@ -165,69 +180,71 @@ public:
|
|||
Q_INVOKABLE void setReverb(bool enable);
|
||||
|
||||
/**jsdoc
|
||||
* Configure reverberation options. Use {@link Audio.setReverb|setReverb} to enable or disable reverberation.
|
||||
* Configures reverberation options. Use {@link Audio.setReverb|setReverb} to enable or disable reverberation.
|
||||
* @function Audio.setReverbOptions
|
||||
* @param {AudioEffectOptions} options - The reverberation options.
|
||||
*/
|
||||
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);
|
||||
|
||||
/**jsdoc
|
||||
* Sets the avatar gain at the server.
|
||||
* Units are Decibels (dB)
|
||||
* Sets the gain (relative volume) that avatars' voices are played at. This gain is used at the server.
|
||||
* @function Audio.setAvatarGain
|
||||
* @param {number} gain (in dB)
|
||||
*/
|
||||
* @param {number} gain - Avatar gain (dB) at the server.
|
||||
*/
|
||||
Q_INVOKABLE void setAvatarGain(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the avatar gain at the server.
|
||||
* Gets the gain (relative volume) that avatars' voices are played at. This gain is used at the server.
|
||||
* @function Audio.getAvatarGain
|
||||
* @returns {number} gain (in dB)
|
||||
*/
|
||||
* @returns {number} Avatar gain (dB) at the server.
|
||||
* @example <caption>Report current audio gain settings.</caption>
|
||||
* // 0 value = normal volume; -ve value = quieter; +ve value = louder.
|
||||
* print("Avatar gain: " + Audio.getAvatarGain());
|
||||
* print("Environment server gain: " + Audio.getInjectorGain());
|
||||
* print("Environment local gain: " + Audio.getLocalInjectorGain());
|
||||
* print("System gain: " + Audio.getSystemInjectorGain());
|
||||
*/
|
||||
Q_INVOKABLE float getAvatarGain();
|
||||
|
||||
/**jsdoc
|
||||
* Sets the injector gain at the server.
|
||||
* Units are Decibels (dB)
|
||||
* Sets the gain (relative volume) that environment sounds from the server are played at.
|
||||
* @function Audio.setInjectorGain
|
||||
* @param {number} gain (in dB)
|
||||
*/
|
||||
* @param {number} gain - Injector gain (dB) at the server.
|
||||
*/
|
||||
Q_INVOKABLE void setInjectorGain(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the injector gain at the server.
|
||||
* Gets the gain (relative volume) that environment sounds from the server are played at.
|
||||
* @function Audio.getInjectorGain
|
||||
* @returns {number} gain (in dB)
|
||||
*/
|
||||
* @returns {number} Injector gain (dB) at the server.
|
||||
*/
|
||||
Q_INVOKABLE float getInjectorGain();
|
||||
|
||||
/**jsdoc
|
||||
* Sets the local injector gain in the client.
|
||||
* Units are Decibels (dB)
|
||||
* Sets the gain (relative volume) that environment sounds from the client are played at.
|
||||
* @function Audio.setLocalInjectorGain
|
||||
* @param {number} gain (in dB)
|
||||
*/
|
||||
* @param {number} gain - Injector gain (dB) in the client.
|
||||
*/
|
||||
Q_INVOKABLE void setLocalInjectorGain(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the local injector gain in the client.
|
||||
* Gets the gain (relative volume) that environment sounds from the client are played at.
|
||||
* @function Audio.getLocalInjectorGain
|
||||
* @returns {number} gain (in dB)
|
||||
*/
|
||||
* @returns {number} Injector gain (dB) in the client.
|
||||
*/
|
||||
Q_INVOKABLE float getLocalInjectorGain();
|
||||
|
||||
/**jsdoc
|
||||
* Sets the injector gain for system sounds.
|
||||
* Units are Decibels (dB)
|
||||
* Sets the gain (relative volume) that system sounds are played at.
|
||||
* @function Audio.setSystemInjectorGain
|
||||
* @param {number} gain (in dB)
|
||||
*/
|
||||
* @param {number} gain - Injector gain (dB) in the client.
|
||||
*/
|
||||
Q_INVOKABLE void setSystemInjectorGain(float gain);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the injector gain for system sounds.
|
||||
* Gets the gain (relative volume) that system sounds are played at.
|
||||
* @function Audio.getSystemInjectorGain
|
||||
* @returns {number} gain (in dB)
|
||||
* @returns {number} Injector gain (dB) in the client.
|
||||
*/
|
||||
Q_INVOKABLE float getSystemInjectorGain();
|
||||
|
||||
|
@ -253,13 +270,13 @@ public:
|
|||
Q_INVOKABLE bool startRecording(const QString& filename);
|
||||
|
||||
/**jsdoc
|
||||
* Finish making an audio recording started with {@link Audio.startRecording|startRecording}.
|
||||
* Finishes making an audio recording started with {@link Audio.startRecording|startRecording}.
|
||||
* @function Audio.stopRecording
|
||||
*/
|
||||
Q_INVOKABLE void stopRecording();
|
||||
|
||||
/**jsdoc
|
||||
* Check whether an audio recording is currently being made.
|
||||
* Checks whether an audio recording is currently being made.
|
||||
* @function Audio.getRecording
|
||||
* @returns {boolean} <code>true</code> if an audio recording is currently being made, otherwise <code>false</code>.
|
||||
*/
|
||||
|
@ -275,9 +292,10 @@ signals:
|
|||
void nop();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the audio input is muted or unmuted.
|
||||
* Triggered when the audio input is muted or unmuted for the current context (desktop or HMD).
|
||||
* @function Audio.mutedChanged
|
||||
* @param {boolean} isMuted - <code>true</code> if the audio input is muted, otherwise <code>false</code>.
|
||||
* @param {boolean} isMuted - <code>true</code> if the audio input is muted for the current context (desktop or HMD),
|
||||
* otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report when audio input is muted or unmuted</caption>
|
||||
* Audio.mutedChanged.connect(function (isMuted) {
|
||||
|
@ -287,47 +305,55 @@ signals:
|
|||
void mutedChanged(bool isMuted);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when desktop audio input is muted or unmuted.
|
||||
* @function Audio.mutedDesktopChanged
|
||||
* @param {boolean} isMuted - <code>true</code> if the audio input is muted for desktop mode, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
* Triggered when desktop audio input is muted or unmuted.
|
||||
* @function Audio.mutedDesektopChanged
|
||||
* @param {boolean} isMuted - <code>true</code> if desktop audio input is muted, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report when desktop muting changes.</caption>
|
||||
* Audio.mutedDesktopChanged.connect(function (isMuted) {
|
||||
* print("Desktop muted: " + isMuted);
|
||||
* });
|
||||
*/
|
||||
void mutedDesktopChanged(bool isMuted);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when HMD audio input is muted or unmuted.
|
||||
* @function Audio.mutedHMDChanged
|
||||
* @param {boolean} isMuted - <code>true</code> if the audio input is muted for HMD mode, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
* Triggered when HMD audio input is muted or unmuted.
|
||||
* @function Audio.mutedHMDChanged
|
||||
* @param {boolean} isMuted - <code>true</code> if HMD audio input is muted, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void mutedHMDChanged(bool isMuted);
|
||||
|
||||
/**
|
||||
* Triggered when Push-to-Talk has been enabled or disabled.
|
||||
* @function Audio.pushToTalkChanged
|
||||
* @param {boolean} enabled - <code>true</code> if Push-to-Talk is enabled, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
/**jsdoc
|
||||
* Triggered when push-to-talk is enabled or disabled for the current context (desktop or HMD).
|
||||
* @function Audio.pushToTalkChanged
|
||||
* @param {boolean} enabled - <code>true</code> if push-to-talk is enabled, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Report when push-to-talk changes.</caption>
|
||||
* Audio.pushToTalkChanged.connect(function (enabled) {
|
||||
* print("Push to talk: " + (enabled ? "on" : "off"));
|
||||
* });
|
||||
*/
|
||||
void pushToTalkChanged(bool enabled);
|
||||
|
||||
/**
|
||||
* Triggered when Push-to-Talk has been enabled or disabled for desktop mode.
|
||||
* @function Audio.pushToTalkDesktopChanged
|
||||
* @param {boolean} enabled - <code>true</code> if Push-to-Talk is emabled for Desktop mode, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
/**jsdoc
|
||||
* Triggered when push-to-talk is enabled or disabled for desktop mode.
|
||||
* @function Audio.pushToTalkDesktopChanged
|
||||
* @param {boolean} enabled - <code>true</code> if push-to-talk is enabled for desktop mode, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void pushToTalkDesktopChanged(bool enabled);
|
||||
|
||||
/**
|
||||
* Triggered when Push-to-Talk has been enabled or disabled for HMD mode.
|
||||
* @function Audio.pushToTalkHMDChanged
|
||||
* @param {boolean} enabled - <code>true</code> if Push-to-Talk is emabled for HMD mode, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
/**jsdoc
|
||||
* Triggered when push-to-talk is enabled or disabled for HMD mode.
|
||||
* @function Audio.pushToTalkHMDChanged
|
||||
* @param {boolean} enabled - <code>true</code> if push-to-talk is enabled for HMD mode, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void pushToTalkHMDChanged(bool enabled);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the audio input noise reduction is enabled or disabled.
|
||||
* Triggered when audio input noise reduction is enabled or disabled.
|
||||
* @function Audio.noiseReductionChanged
|
||||
* @param {boolean} isEnabled - <code>true</code> if audio input noise reduction is enabled, otherwise <code>false</code>.
|
||||
* @returns {Signal}
|
||||
|
@ -346,8 +372,8 @@ signals:
|
|||
* Triggered when the input audio volume changes.
|
||||
* @function Audio.inputVolumeChanged
|
||||
* @param {number} volume - The requested volume to be applied to the audio input, range <code>0.0</code> –
|
||||
* <code>1.0</code>. The resulting value of <code>Audio.inputVolume</code> depends on the capabilities of the device:
|
||||
* for example, the volume can't be changed on some devices, and others might only support values of <code>0.0</code>
|
||||
* <code>1.0</code>. The resulting value of <code>Audio.inputVolume</code> depends on the capabilities of the device.
|
||||
* For example, the volume can't be changed on some devices, while others might only support values of <code>0.0</code>
|
||||
* and <code>1.0</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
@ -379,11 +405,11 @@ signals:
|
|||
void contextChanged(const QString& context);
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when pushing to talk.
|
||||
* @function Audio.pushingToTalkChanged
|
||||
* @param {boolean} talking - <code>true</code> if broadcasting with PTT, <code>false</code> otherwise.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
* Triggered when the user starts or stops push-to-talk.
|
||||
* @function Audio.pushingToTalkChanged
|
||||
* @param {boolean} talking - <code>true</code> if started push-to-talk, <code>false</code> if stopped push-to-talk.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void pushingToTalkChanged(bool talking);
|
||||
|
||||
public slots:
|
||||
|
|
|
@ -284,7 +284,7 @@ public slots:
|
|||
* Disable default Interface actions for a joystick.
|
||||
* @function Controller.captureJoystick
|
||||
* @param {number} joystickID - The integer ID of the joystick.
|
||||
* @deprecated This function no longer has any effect.
|
||||
* @deprecated This function is deprecated and will be removed. It no longer has any effect.
|
||||
*/
|
||||
virtual void captureJoystick(int joystickIndex);
|
||||
|
||||
|
@ -293,7 +293,7 @@ public slots:
|
|||
* {@link Controller.captureJoystick|captureJoystick}.
|
||||
* @function Controller.releaseJoystick
|
||||
* @param {number} joystickID - The integer ID of the joystick.
|
||||
* @deprecated This function no longer has any effect.
|
||||
* @deprecated This function is deprecated and will be removed. It no longer has any effect.
|
||||
*/
|
||||
virtual void releaseJoystick(int joystickIndex);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
//
|
||||
// HMDScriptingInterface.h
|
||||
// interface/src/scripting
|
||||
//
|
||||
|
@ -25,7 +25,7 @@ class QScriptEngine;
|
|||
#include <QReadWriteLock>
|
||||
|
||||
/**jsdoc
|
||||
* The HMD API provides access to the HMD used in VR display mode.
|
||||
* The <code>HMD</code> API provides access to the HMD used in VR display mode.
|
||||
*
|
||||
* @namespace HMD
|
||||
*
|
||||
|
@ -87,7 +87,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
|
|||
public:
|
||||
|
||||
/**jsdoc
|
||||
* Calculate the intersection of a ray with the HUD overlay.
|
||||
* Calculates the intersection of a ray with the HUD overlay.
|
||||
* @function HMD.calculateRayUICollisionPoint
|
||||
* @param {Vec3} position - The origin of the ray.
|
||||
* @param {Vec3} direction - The direction of the ray.
|
||||
|
@ -115,7 +115,7 @@ public:
|
|||
glm::vec3 calculateParabolaUICollisionPoint(const glm::vec3& position, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the 2D HUD overlay coordinates of a 3D point on the HUD overlay.
|
||||
* Gets the 2D HUD overlay coordinates of a 3D point on the HUD overlay.
|
||||
* 2D HUD overlay coordinates are pixels with the origin at the top left of the overlay.
|
||||
* @function HMD.overlayFromWorldPoint
|
||||
* @param {Vec3} position - The point on the HUD overlay in world coordinates.
|
||||
|
@ -141,7 +141,7 @@ public:
|
|||
Q_INVOKABLE glm::vec2 overlayFromWorldPoint(const glm::vec3& position) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the 3D world coordinates of a 2D point on the HUD overlay.
|
||||
* Gets the 3D world coordinates of a 2D point on the HUD overlay.
|
||||
* 2D HUD overlay coordinates are pixels with the origin at the top left of the overlay.
|
||||
* @function HMD.worldPointFromOverlay
|
||||
* @param {Vec2} coordinates - The point on the HUD overlay in HUD coordinates.
|
||||
|
@ -150,7 +150,7 @@ public:
|
|||
Q_INVOKABLE glm::vec3 worldPointFromOverlay(const glm::vec2& overlay) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the 2D point on the HUD overlay represented by given spherical coordinates.
|
||||
* Gets the 2D point on the HUD overlay represented by given spherical coordinates.
|
||||
* 2D HUD overlay coordinates are pixels with the origin at the top left of the overlay.
|
||||
* Spherical coordinates are polar coordinates in radians with <code>{ x: 0, y: 0 }</code> being the center of the HUD
|
||||
* overlay.
|
||||
|
@ -161,7 +161,7 @@ public:
|
|||
Q_INVOKABLE glm::vec2 sphericalToOverlay(const glm::vec2 & sphericalPos) const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the spherical coordinates of a 2D point on the HUD overlay.
|
||||
* Gets the spherical coordinates of a 2D point on the HUD overlay.
|
||||
* 2D HUD overlay coordinates are pixels with the origin at the top left of the overlay.
|
||||
* Spherical coordinates are polar coordinates in radians with <code>{ x: 0, y: 0 }</code> being the center of the HUD
|
||||
* overlay.
|
||||
|
@ -172,21 +172,21 @@ public:
|
|||
Q_INVOKABLE glm::vec2 overlayToSpherical(const glm::vec2 & overlayPos) const;
|
||||
|
||||
/**jsdoc
|
||||
* Recenter the HMD HUD to the current HMD position and orientation.
|
||||
* Recenters the HMD HUD to the current HMD position and orientation.
|
||||
* @function HMD.centerUI
|
||||
*/
|
||||
Q_INVOKABLE void centerUI();
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* Get the name of the HMD audio input device.
|
||||
* Gets the name of the HMD audio input device.
|
||||
* @function HMD.preferredAudioInput
|
||||
* @returns {string} The name of the HMD audio input device if in HMD mode, otherwise an empty string.
|
||||
*/
|
||||
Q_INVOKABLE QString preferredAudioInput() const;
|
||||
|
||||
/**jsdoc
|
||||
* Get the name of the HMD audio output device.
|
||||
* Gets the name of the HMD audio output device.
|
||||
* @function HMD.preferredAudioOutput
|
||||
* @returns {string} The name of the HMD audio output device if in HMD mode, otherwise an empty string.
|
||||
*/
|
||||
|
@ -194,10 +194,10 @@ public:
|
|||
|
||||
|
||||
/**jsdoc
|
||||
* Check whether there is an HMD available.
|
||||
* Checks whether there is an HMD available.
|
||||
* @function HMD.isHMDAvailable
|
||||
* @param {string} [name=""] - The name of the HMD to check for, e.g., <code>"Oculus Rift"</code>. The name is the same as
|
||||
* may be displayed in Interface's "Display" menu. If no name is specified then any HMD matches.
|
||||
* may be displayed in Interface's "Display" menu. If no name is specified, then any HMD matches.
|
||||
* @returns {boolean} <code>true</code> if an HMD of the specified <code>name</code> is available, otherwise
|
||||
* <code>false</code>.
|
||||
* @example <caption>Report on HMD availability.</caption>
|
||||
|
@ -208,10 +208,10 @@ public:
|
|||
Q_INVOKABLE bool isHMDAvailable(const QString& name = "");
|
||||
|
||||
/**jsdoc
|
||||
* Check whether there is an HMD head controller available.
|
||||
* Checks whether there is an HMD head controller available.
|
||||
* @function HMD.isHeadControllerAvailable
|
||||
* @param {string} [name=""] - The name of the HMD head controller to check for, e.g., <code>"Oculus"</code>. If no name is
|
||||
* specified then any HMD head controller matches.
|
||||
* specified, then any HMD head controller matches.
|
||||
* @returns {boolean} <code>true</code> if an HMD head controller of the specified <code>name</code> is available,
|
||||
* otherwise <code>false</code>.
|
||||
* @example <caption>Report HMD head controller availability.</caption>
|
||||
|
@ -222,10 +222,10 @@ public:
|
|||
Q_INVOKABLE bool isHeadControllerAvailable(const QString& name = "");
|
||||
|
||||
/**jsdoc
|
||||
* Check whether there are HMD hand controllers available.
|
||||
* Checks whether there are HMD hand controllers available.
|
||||
* @function HMD.isHandControllerAvailable
|
||||
* @param {string} [name=""] - The name of the HMD hand controller to check for, e.g., <code>"Oculus"</code>. If no name is
|
||||
* specified then any HMD hand controller matches.
|
||||
* specified, then any HMD hand controller matches.
|
||||
* @returns {boolean} <code>true</code> if an HMD hand controller of the specified <code>name</code> is available,
|
||||
* otherwise <code>false</code>.
|
||||
* @example <caption>Report HMD hand controller availability.</caption>
|
||||
|
@ -236,7 +236,7 @@ public:
|
|||
Q_INVOKABLE bool isHandControllerAvailable(const QString& name = "");
|
||||
|
||||
/**jsdoc
|
||||
* Check whether there are specific HMD controllers available.
|
||||
* Checks whether there are specific HMD controllers available.
|
||||
* @function HMD.isSubdeviceContainingNameAvailable
|
||||
* @param {string} name - The name of the HMD controller to check for, e.g., <code>"OculusTouch"</code>.
|
||||
* @returns {boolean} <code>true</code> if an HMD controller with a name containing the specified <code>name</code> is
|
||||
|
@ -248,7 +248,7 @@ public:
|
|||
Q_INVOKABLE bool isSubdeviceContainingNameAvailable(const QString& name);
|
||||
|
||||
/**jsdoc
|
||||
* Signal that models of the HMD hand controllers being used should be displayed. The models are displayed at their actual,
|
||||
* Signals that models of the HMD hand controllers being used should be displayed. The models are displayed at their actual,
|
||||
* real-world locations.
|
||||
* @function HMD.requestShowHandControllers
|
||||
* @example <caption>Show your hand controllers for 10 seconds.</caption>
|
||||
|
@ -260,14 +260,14 @@ public:
|
|||
Q_INVOKABLE void requestShowHandControllers();
|
||||
|
||||
/**jsdoc
|
||||
* Signal that it is no longer necessary to display models of the HMD hand controllers being used. If no other scripts
|
||||
* Signals that it is no longer necessary to display models of the HMD hand controllers being used. If no other scripts
|
||||
* want the models displayed then they are no longer displayed.
|
||||
* @function HMD.requestHideHandControllers
|
||||
*/
|
||||
Q_INVOKABLE void requestHideHandControllers();
|
||||
|
||||
/**jsdoc
|
||||
* Check whether any script wants models of the HMD hand controllers displayed. Requests are made and canceled using
|
||||
* Checks whether any script wants models of the HMD hand controllers displayed. Requests are made and canceled using
|
||||
* {@link HMD.requestShowHandControllers|requestShowHandControllers} and
|
||||
* {@link HMD.requestHideHandControllers|requestHideHandControllers}.
|
||||
* @function HMD.shouldShowHandControllers
|
||||
|
@ -292,8 +292,8 @@ public:
|
|||
|
||||
|
||||
/**jsdoc
|
||||
* Suppress the activation of the HMD-provided keyboard, if any. Successful calls should be balanced with a call to
|
||||
* {@link HMD.unspressKeyboard|unspressKeyboard} within a reasonable amount of time.
|
||||
* Suppresses the activation of the HMD-provided keyboard, if any. Successful calls should be balanced with a call to
|
||||
* {@link HMD.unsuppressKeyboard|unsuppressKeyboard} within a reasonable amount of time.
|
||||
* @function HMD.suppressKeyboard
|
||||
* @returns {boolean} <code>true</code> if the current HMD provides a keyboard and it was successfully suppressed (e.g., it
|
||||
* isn't being displayed), otherwise <code>false</code>.
|
||||
|
@ -307,14 +307,14 @@ public:
|
|||
Q_INVOKABLE bool suppressKeyboard();
|
||||
|
||||
/**jsdoc
|
||||
* Unsuppress the activation of the HMD-provided keyboard, if any.
|
||||
* Unsuppresses the activation of the HMD-provided keyboard, if any.
|
||||
* @function HMD.unsuppressKeyboard
|
||||
*/
|
||||
/// Enable the keyboard following a suppressKeyboard call
|
||||
Q_INVOKABLE void unsuppressKeyboard();
|
||||
|
||||
/**jsdoc
|
||||
* Check whether the HMD-provided keyboard, if any, is visible.
|
||||
* Checks whether the HMD-provided keyboard, if any, is visible.
|
||||
* @function HMD.isKeyboardVisible
|
||||
* @returns {boolean} <code>true</code> if the current HMD provides a keyboard and it is visible, otherwise
|
||||
* <code>false</code>.
|
||||
|
@ -377,7 +377,19 @@ signals:
|
|||
|
||||
public:
|
||||
HMDScriptingInterface();
|
||||
|
||||
/**jsdoc
|
||||
* Gets the position on the HUD overlay that your HMD is looking at, in HUD coordinates.
|
||||
* @function HMD.getHUDLookAtPosition2D
|
||||
* @returns {Vec2} The position on the HUD overlay that your HMD is looking at, in pixels.
|
||||
*/
|
||||
static QScriptValue getHUDLookAtPosition2D(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the position on the HUD overlay that your HMD is looking at, in world coordinates.
|
||||
* @function HMD.getHUDLookAtPosition3D
|
||||
* @returns {Vec3} The position on the HUD overlay the your HMD is looking at, in world coordinates.
|
||||
*/
|
||||
static QScriptValue getHUDLookAtPosition3D(QScriptContext* context, QScriptEngine* engine);
|
||||
|
||||
bool isMounted() const override;
|
||||
|
|
|
@ -18,18 +18,15 @@
|
|||
class MenuItemProperties;
|
||||
|
||||
/**jsdoc
|
||||
* The Menu API provides access to the menu that is displayed at the top of the window
|
||||
* on a user's desktop and in the tablet when the "MENU" button is pressed.
|
||||
*
|
||||
* <p />
|
||||
* The <code>Menu</code> API provides access to the menu that is displayed at the top of the window on a user's desktop and in
|
||||
* the tablet when the "MENU" button is pressed.
|
||||
*
|
||||
* <h3>Groupings</h3>
|
||||
*
|
||||
* A "grouping" provides a way to group a set of menus or menu items together so
|
||||
* that they can all be set visible or invisible as a group.
|
||||
* There are two available groups: <code>"Advanced"</code> and <code>"Developer"</code>.
|
||||
* These groupings can be toggled in the "Settings" menu.
|
||||
* If a menu item doesn't belong to a group it is always displayed.
|
||||
* <p>A "grouping" provides a way to group a set of menus or menu items together so that they can all be set visible or invisible
|
||||
* as a group.</p> There is currently only one available group: <code>"Developer"</code>. This grouping can be toggled in the
|
||||
* "Settings" menu.</p>
|
||||
* <p>If a menu item doesn't belong to a group, it is always displayed.</p>
|
||||
*
|
||||
* @namespace Menu
|
||||
*
|
||||
|
@ -60,22 +57,23 @@ private slots:
|
|||
|
||||
public slots:
|
||||
/**jsdoc
|
||||
* Add a new top-level menu.
|
||||
* Adds a new top-level menu.
|
||||
* @function Menu.addMenu
|
||||
* @param {string} menuName - Name that will be displayed for the menu. Nested menus can be described using the ">" symbol.
|
||||
* @param {string} menuName - Name that will be displayed for the menu. Nested menus can be specified using the
|
||||
* <code>">"</code> character.
|
||||
* @param {string} [grouping] - Name of the grouping, if any, to add this menu to.
|
||||
*
|
||||
* @example <caption>Add a menu and a nested submenu.</caption>
|
||||
* Menu.addMenu("Test Menu");
|
||||
* Menu.addMenu("Test Menu > Test Sub Menu");
|
||||
*
|
||||
* @example <caption>Add a menu to the Settings menu that is only visible if Settings > Advanced is enabled.</caption>
|
||||
* Menu.addMenu("Settings > Test Grouping Menu", "Advanced");
|
||||
* @example <caption>Add a menu to the Settings menu that is only visible if Settings > Developer is enabled.</caption>
|
||||
* Menu.addMenu("Settings > Test Grouping Menu", "Developer");
|
||||
*/
|
||||
void addMenu(const QString& menuName, const QString& grouping = QString());
|
||||
|
||||
/**jsdoc
|
||||
* Remove a top-level menu.
|
||||
* Removes a top-level menu.
|
||||
* @function Menu.removeMenu
|
||||
* @param {string} menuName - Name of the menu to remove.
|
||||
* @example <caption>Remove a menu and nested submenu.</caption>
|
||||
|
@ -85,9 +83,9 @@ public slots:
|
|||
void removeMenu(const QString& menuName);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether a top-level menu exists.
|
||||
* Checks whether a top-level menu exists.
|
||||
* @function Menu.menuExists
|
||||
* @param {string} menuName - Name of the menu to check for existence.
|
||||
* @param {string} menuName - Name of the menu to check exists.
|
||||
* @returns {boolean} <code>true</code> if the menu exists, otherwise <code>false</code>.
|
||||
* @example <caption>Check if the "Developer" menu exists.</caption>
|
||||
* if (Menu.menuExists("Developer")) {
|
||||
|
@ -97,46 +95,45 @@ public slots:
|
|||
bool menuExists(const QString& menuName);
|
||||
|
||||
/**jsdoc
|
||||
* Add a separator with an unclickable label below it. The separator will be placed at the bottom of the menu.
|
||||
* If you want to add a separator at a specific point in the menu, use {@link Menu.addMenuItem} with
|
||||
* {@link Menu.MenuItemProperties} instead.
|
||||
* Adds a separator with an unclickable label below it. The separator will be placed at the bottom of the menu. To add a
|
||||
* separator at a specific point in the menu, use {@link Menu.addMenuItem} with {@link Menu.MenuItemProperties} instead.
|
||||
* @function Menu.addSeparator
|
||||
* @param {string} menuName - Name of the menu to add a separator to.
|
||||
* @param {string} menuName - Name of the menu to add the separator to.
|
||||
* @param {string} separatorName - Name of the separator that will be displayed as the label below the separator line.
|
||||
* @example <caption>Add a separator.</caption>
|
||||
* Menu.addSeparator("Developer","Test Separator");
|
||||
* Menu.addSeparator("Developer", "Test Separator");
|
||||
*/
|
||||
void addSeparator(const QString& menuName, const QString& separatorName);
|
||||
|
||||
/**jsdoc
|
||||
* Remove a separator from a menu.
|
||||
* Removes a separator from a menu.
|
||||
* @function Menu.removeSeparator
|
||||
* @param {string} menuName - Name of the menu to remove the separator from.
|
||||
* @param {string} separatorName - Name of the separator to remove.
|
||||
* @example <caption>Remove a separator.</caption>
|
||||
* Menu.removeSeparator("Developer","Test Separator");
|
||||
* Menu.removeSeparator("Developer", "Test Separator");
|
||||
*/
|
||||
void removeSeparator(const QString& menuName, const QString& separatorName);
|
||||
|
||||
/**jsdoc
|
||||
* Add a new menu item to a menu.
|
||||
* Adds a new menu item to a menu. The menu item is specified using {@link Menu.MenuItemProperties}.
|
||||
* @function Menu.addMenuItem
|
||||
* @param {Menu.MenuItemProperties} properties - Properties of the menu item to create.
|
||||
* @example <caption>Add a menu item using {@link Menu.MenuItemProperties}.</caption>
|
||||
* @example <caption>Add a menu item at a particular position in the "Developer" menu.</caption>
|
||||
* Menu.addMenuItem({
|
||||
* menuName: "Developer",
|
||||
* menuItemName: "Test",
|
||||
* afterItem: "Log",
|
||||
* shortcutKey: "Ctrl+Shift+T",
|
||||
* grouping: "Advanced"
|
||||
* shortcutKey: "Ctrl+Shift+T"
|
||||
* });
|
||||
*/
|
||||
void addMenuItem(const MenuItemProperties& properties);
|
||||
|
||||
/**jsdoc
|
||||
* Add a new menu item to a menu. The new item is added at the end of the menu.
|
||||
* Adds a new menu item to a menu. The new item is added at the end of the menu.
|
||||
* @function Menu.addMenuItem
|
||||
* @param {string} menuName - Name of the menu to add a menu item to.
|
||||
* @variation 0
|
||||
* @param {string} menuName - Name of the menu to add the menu item to.
|
||||
* @param {string} menuItem - Name of the menu item. This is what will be displayed in the menu.
|
||||
* @param {string} [shortcutKey] A shortcut key that can be used to trigger the menu item.
|
||||
* @example <caption>Add a menu item to the end of the "Developer" menu.</caption>
|
||||
|
@ -146,16 +143,17 @@ public slots:
|
|||
void addMenuItem(const QString& menuName, const QString& menuitem);
|
||||
|
||||
/**jsdoc
|
||||
* Remove a menu item from a menu.
|
||||
* Removes a menu item from a menu.
|
||||
* @function Menu.removeMenuItem
|
||||
* @param {string} menuName - Name of the menu to remove a menu item from.
|
||||
* @param {string} menuItem - Name of the menu item to remove.
|
||||
* @example <caption>Remove a menu item from the "Developer" menu.</caption>
|
||||
* Menu.removeMenuItem("Developer", "Test");
|
||||
*/
|
||||
void removeMenuItem(const QString& menuName, const QString& menuitem);
|
||||
|
||||
/**jsdoc
|
||||
* Check if a menu item exists.
|
||||
* Checks whether a menu item exists.
|
||||
* @function Menu.menuItemExists
|
||||
* @param {string} menuName - Name of the menu that the menu item is in.
|
||||
* @param {string} menuItem - Name of the menu item to check for existence of.
|
||||
|
@ -168,66 +166,66 @@ public slots:
|
|||
bool menuItemExists(const QString& menuName, const QString& menuitem);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether a checkable menu item is checked.
|
||||
* Checks whether a checkable menu item is checked.
|
||||
* @function Menu.isOptionChecked
|
||||
* @param {string} menuOption - The name of the menu item.
|
||||
* @returns {boolean} <code>true</code> if the option is checked, otherwise <code>false</code>.
|
||||
* @example <caption>Report whether the Settings > Advanced menu item is turned on.</caption>
|
||||
* print(Menu.isOptionChecked("Advanced Menus")); // true or false
|
||||
* @example <caption>Report whether the Settings > Developer menu item is turned on.</caption>
|
||||
* print("Developer menu showing: " + Menu.isOptionChecked("Developer Menu"));
|
||||
*/
|
||||
bool isOptionChecked(const QString& menuOption);
|
||||
|
||||
/**jsdoc
|
||||
* Set a checkable menu item as checked or unchecked.
|
||||
* Sets a checkable menu item as checked or unchecked.
|
||||
* @function Menu.setIsOptionChecked
|
||||
* @param {string} menuOption - The name of the menu item to modify.
|
||||
* @param {boolean} isChecked - If <code>true</code>, the menu item will be checked, otherwise it will not be checked.
|
||||
* @example <caption>Turn on Settings > Advanced Menus.</caption>
|
||||
* Menu.setIsOptionChecked("Advanced Menus", true);
|
||||
* print(Menu.isOptionChecked("Advanced Menus")); // true
|
||||
* @example <caption>Turn on Settings > Developer Menu.</caption>
|
||||
* Menu.setIsOptionChecked("Developer Menu", true);
|
||||
* print("Developer menu showing: " + Menu.isOptionChecked("Developer Menu"));
|
||||
*/
|
||||
void setIsOptionChecked(const QString& menuOption, bool isChecked);
|
||||
|
||||
/**jsdoc
|
||||
* Trigger the menu item as if the user clicked on it.
|
||||
* Triggers a menu item as if the user clicked on it.
|
||||
* @function Menu.triggerOption
|
||||
* @param {string} menuOption - The name of the menu item to trigger.
|
||||
* @example <caption>Open the help window.</caption>
|
||||
* Menu.triggerOption('Help...');
|
||||
* @example <caption>Open the Asset Browser dialog.</caption>
|
||||
* Menu.triggerOption('Asset Browser');
|
||||
*/
|
||||
void triggerOption(const QString& menuOption);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether a menu or menu item is enabled. If disabled, the item is grayed out and unusable.
|
||||
* Checks whether a menu or menu item is enabled. If disabled, the item is grayed out and unusable.
|
||||
* Menus are enabled by default.
|
||||
* @function Menu.isMenuEnabled
|
||||
* @param {string} menuName The name of the menu or menu item to check.
|
||||
* @returns {boolean} <code>true</code> if the menu is enabled, otherwise <code>false</code>.
|
||||
* @example <caption>Report with the Settings > Advanced Menus menu item is enabled.</caption>
|
||||
* print(Menu.isMenuEnabled("Settings > Advanced Menus")); // true or false
|
||||
* @example <caption>Report whether the Settings > Developer Menu item is enabled.</caption>
|
||||
* print("Developer Menu item enabled: " + Menu.isMenuEnabled("Settings > Developer Menu"));
|
||||
*/
|
||||
bool isMenuEnabled(const QString& menuName);
|
||||
|
||||
/**jsdoc
|
||||
* Set a menu or menu item to be enabled or disabled. If disabled, the item is grayed out and unusable.
|
||||
* Sets a menu or menu item to be enabled or disabled. If disabled, the item is grayed out and unusable.
|
||||
* @function Menu.setMenuEnabled
|
||||
* @param {string} menuName - The name of the menu or menu item to modify.
|
||||
* @param {boolean} isEnabled - If <code>true</code>, the menu will be enabled, otherwise it will be disabled.
|
||||
* @example <caption>Disable the Settings > Advanced Menus menu item.</caption>
|
||||
* Menu.setMenuEnabled("Settings > Advanced Menus", false);
|
||||
* print(Menu.isMenuEnabled("Settings > Advanced Menus")); // false
|
||||
* @example <caption>Disable the Settings > Developer Menu item.</caption>
|
||||
* Menu.setMenuEnabled("Settings > Developer Menu", false);
|
||||
* print("Developer Menu item enabled: " + Menu.isMenuEnabled("Settings > Developer Menu"));
|
||||
*/
|
||||
void setMenuEnabled(const QString& menuName, bool isEnabled);
|
||||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* Triggered when a menu item is clicked (or triggered by {@link Menu.triggerOption}).
|
||||
* Triggered when a menu item is clicked or triggered by {@link Menu.triggerOption}.
|
||||
* @function Menu.menuItemEvent
|
||||
* @param {string} menuItem - Name of the menu item that was clicked.
|
||||
* @param {string} menuItem - Name of the menu item that was clicked or triggered.
|
||||
* @returns {Signal}
|
||||
* @example <caption>Detect menu item events.</caption>
|
||||
* function onMenuItemEvent(menuItem) {
|
||||
* print("You clicked on " + menuItem);
|
||||
* print("Menu item clicked: " + menuItem);
|
||||
* }
|
||||
*
|
||||
* Menu.menuItemEvent.connect(onMenuItemEvent);
|
||||
|
|
46
interface/src/scripting/RefreshRateScriptingInterface.h
Normal file
46
interface/src/scripting/RefreshRateScriptingInterface.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// RefreshRateScriptingInterface.h
|
||||
// interface/src/scrfipting
|
||||
//
|
||||
// Created by Dante Ruiz on 2019-04-15.
|
||||
// Copyright 2019 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_RefreshRateScriptingInterface_h
|
||||
#define hifi_RefreshRateScriptingInterface_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <Application.h>
|
||||
|
||||
class RefreshRateScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RefreshRateScriptingInterface() = default;
|
||||
~RefreshRateScriptingInterface() = default;
|
||||
|
||||
public:
|
||||
Q_INVOKABLE QString getRefreshRateProfile() {
|
||||
RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager();
|
||||
return QString::fromStdString(RefreshRateManager::refreshRateProfileToString(refreshRateManager.getRefreshRateProfile()));
|
||||
}
|
||||
|
||||
Q_INVOKABLE QString getRefreshRateRegime() {
|
||||
RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager();
|
||||
return QString::fromStdString(RefreshRateManager::refreshRateRegimeToString(refreshRateManager.getRefreshRateRegime()));
|
||||
}
|
||||
|
||||
Q_INVOKABLE QString getUXMode() {
|
||||
RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager();
|
||||
return QString::fromStdString(RefreshRateManager::uxModeToString(refreshRateManager.getUXMode()));
|
||||
}
|
||||
|
||||
Q_INVOKABLE int getActiveRefreshRate() {
|
||||
return qApp->getRefreshRateManager().getActiveRefreshRate();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -199,3 +199,13 @@ void TestScriptingInterface::setOtherAvatarsReplicaCount(int count) {
|
|||
int TestScriptingInterface::getOtherAvatarsReplicaCount() {
|
||||
return qApp->getOtherAvatarsReplicaCount();
|
||||
}
|
||||
|
||||
void TestScriptingInterface::setMinimumGPUTextureMemStabilityCount(int count) {
|
||||
QMetaObject::invokeMethod(qApp, "setMinimumGPUTextureMemStabilityCount", Qt::DirectConnection, Q_ARG(int, count));
|
||||
}
|
||||
|
||||
bool TestScriptingInterface::isTextureLoadingComplete() {
|
||||
bool result;
|
||||
QMetaObject::invokeMethod(qApp, "gpuTextureMemSizeStable", Qt::DirectConnection, Q_RETURN_ARG(bool, result));
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -163,6 +163,20 @@ public slots:
|
|||
*/
|
||||
Q_INVOKABLE int getOtherAvatarsReplicaCount();
|
||||
|
||||
/**jsdoc
|
||||
* Set number of cycles texture size is required to be stable
|
||||
* @function Entities.setMinimumGPUTextureMemStabilityCount
|
||||
* @param {number} count - Number of cycles to wait
|
||||
*/
|
||||
Q_INVOKABLE void setMinimumGPUTextureMemStabilityCount(int count);
|
||||
|
||||
/**jsdoc
|
||||
* Check whether all textures have been loaded.
|
||||
* @function Entities.isTextureLoadingComplete
|
||||
* @returns {boolean} <code>true</code> texture memory usage is not increasing
|
||||
*/
|
||||
Q_INVOKABLE bool isTextureLoadingComplete();
|
||||
|
||||
private:
|
||||
bool waitForCondition(qint64 maxWaitMs, std::function<bool()> condition);
|
||||
QString _testResultsLocation;
|
||||
|
|
|
@ -82,6 +82,28 @@ void setupPreferences() {
|
|||
preferences->addPreference(new CheckPreference(GRAPHICS_QUALITY, "Show Shadows", getterShadow, setterShadow));
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []()->QString {
|
||||
RefreshRateManager::RefreshRateProfile refreshRateProfile = qApp->getRefreshRateManager().getRefreshRateProfile();
|
||||
return QString::fromStdString(RefreshRateManager::refreshRateProfileToString(refreshRateProfile));
|
||||
};
|
||||
|
||||
auto setter = [](QString value) {
|
||||
std::string profileName = value.toStdString();
|
||||
RefreshRateManager::RefreshRateProfile refreshRateProfile = RefreshRateManager::refreshRateProfileFromString(profileName);
|
||||
qApp->getRefreshRateManager().setRefreshRateProfile(refreshRateProfile);
|
||||
};
|
||||
|
||||
auto preference = new ComboBoxPreference(GRAPHICS_QUALITY, "Refresh Rate", getter, setter);
|
||||
QStringList refreshRateProfiles
|
||||
{ QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::ECO)),
|
||||
QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::INTERACTIVE)),
|
||||
QString::fromStdString(RefreshRateManager::refreshRateProfileToString(RefreshRateManager::RefreshRateProfile::REALTIME)) };
|
||||
|
||||
preference->setItems(refreshRateProfiles);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
// UI
|
||||
static const QString UI_CATEGORY { "User Interface" };
|
||||
{
|
||||
|
@ -278,6 +300,12 @@ void setupPreferences() {
|
|||
preference->setIndented(true);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]() -> bool { return myAvatar->hoverWhenUnsupported(); };
|
||||
auto setter = [myAvatar](bool value) { myAvatar->setHoverWhenUnsupported(value); };
|
||||
auto preference = new CheckPreference(VR_MOVEMENT, "Hover When Unsupported", getter, setter);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = [myAvatar]()->int { return myAvatar->getMovementReference(); };
|
||||
auto setter = [myAvatar](int value) { myAvatar->setMovementReference(value); };
|
||||
|
|
|
@ -132,6 +132,14 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated());
|
||||
STAT_UPDATE(serverCount, (int)nodeList->size());
|
||||
STAT_UPDATE_FLOAT(renderrate, qApp->getRenderLoopRate(), 0.1f);
|
||||
RefreshRateManager& refreshRateManager = qApp->getRefreshRateManager();
|
||||
std::string refreshRateMode = RefreshRateManager::refreshRateProfileToString(refreshRateManager.getRefreshRateProfile());
|
||||
std::string refreshRateRegime = RefreshRateManager::refreshRateRegimeToString(refreshRateManager.getRefreshRateRegime());
|
||||
std::string uxMode = RefreshRateManager::uxModeToString(refreshRateManager.getUXMode());
|
||||
STAT_UPDATE(refreshRateMode, QString::fromStdString(refreshRateMode));
|
||||
STAT_UPDATE(refreshRateRegime, QString::fromStdString(refreshRateRegime));
|
||||
STAT_UPDATE(uxMode, QString::fromStdString(uxMode));
|
||||
STAT_UPDATE(refreshRateTarget, refreshRateManager.getActiveRefreshRate());
|
||||
if (qApp->getActiveDisplayPlugin()) {
|
||||
auto displayPlugin = qApp->getActiveDisplayPlugin();
|
||||
auto stats = displayPlugin->getHardwareStats();
|
||||
|
|
|
@ -206,6 +206,10 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(float, presentdroprate, 0)
|
||||
STATS_PROPERTY(int, gameLoopRate, 0)
|
||||
STATS_PROPERTY(int, avatarCount, 0)
|
||||
STATS_PROPERTY(int, refreshRateTarget, 0)
|
||||
STATS_PROPERTY(QString, refreshRateMode, QString())
|
||||
STATS_PROPERTY(QString, refreshRateRegime, QString())
|
||||
STATS_PROPERTY(QString, uxMode, QString())
|
||||
STATS_PROPERTY(int, heroAvatarCount, 0)
|
||||
STATS_PROPERTY(int, physicsObjectCount, 0)
|
||||
STATS_PROPERTY(int, updatedAvatarCount, 0)
|
||||
|
@ -1067,6 +1071,15 @@ signals:
|
|||
*/
|
||||
void decimatedTextureCountChanged();
|
||||
|
||||
|
||||
void refreshRateTargetChanged();
|
||||
|
||||
void refreshRateModeChanged();
|
||||
|
||||
void refreshRateRegimeChanged();
|
||||
|
||||
void uxModeChanged();
|
||||
|
||||
// QQuickItem signals.
|
||||
|
||||
/**jsdoc
|
||||
|
|
|
@ -11,11 +11,12 @@
|
|||
#include "AnimContext.h"
|
||||
|
||||
AnimContext::AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
|
||||
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix) :
|
||||
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix, int evaluationCount) :
|
||||
_enableDebugDrawIKTargets(enableDebugDrawIKTargets),
|
||||
_enableDebugDrawIKConstraints(enableDebugDrawIKConstraints),
|
||||
_enableDebugDrawIKChains(enableDebugDrawIKChains),
|
||||
_geometryToRigMatrix(geometryToRigMatrix),
|
||||
_rigToWorldMatrix(rigToWorldMatrix)
|
||||
_rigToWorldMatrix(rigToWorldMatrix),
|
||||
_evaluationCount(evaluationCount)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ enum class AnimNodeType {
|
|||
BlendLinearMove,
|
||||
Overlay,
|
||||
StateMachine,
|
||||
RandomSwitchStateMachine,
|
||||
Manipulator,
|
||||
InverseKinematics,
|
||||
DefaultPose,
|
||||
|
@ -37,13 +38,14 @@ class AnimContext {
|
|||
public:
|
||||
AnimContext() {}
|
||||
AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
|
||||
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix);
|
||||
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix, int evaluationCount);
|
||||
|
||||
bool getEnableDebugDrawIKTargets() const { return _enableDebugDrawIKTargets; }
|
||||
bool getEnableDebugDrawIKConstraints() const { return _enableDebugDrawIKConstraints; }
|
||||
bool getEnableDebugDrawIKChains() const { return _enableDebugDrawIKChains; }
|
||||
const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; }
|
||||
const glm::mat4& getRigToWorldMatrix() const { return _rigToWorldMatrix; }
|
||||
int getEvaluationCount() const { return _evaluationCount; }
|
||||
|
||||
float getDebugAlpha(const QString& key) const {
|
||||
auto it = _debugAlphaMap.find(key);
|
||||
|
@ -85,6 +87,7 @@ protected:
|
|||
bool _enableDebugDrawIKChains { false };
|
||||
glm::mat4 _geometryToRigMatrix;
|
||||
glm::mat4 _rigToWorldMatrix;
|
||||
int _evaluationCount{ 0 };
|
||||
|
||||
// used for debugging internal state of animation system.
|
||||
mutable DebugAlphaMap _debugAlphaMap;
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
friend class AnimDebugDraw;
|
||||
friend void buildChildMap(std::map<QString, Pointer>& map, Pointer node);
|
||||
friend class AnimStateMachine;
|
||||
friend class AnimRandomSwitch;
|
||||
|
||||
AnimNode(Type type, const QString& id) : _type(type), _id(id) {}
|
||||
virtual ~AnimNode() {}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "AnimationLogging.h"
|
||||
#include "AnimOverlay.h"
|
||||
#include "AnimStateMachine.h"
|
||||
#include "AnimRandomSwitch.h"
|
||||
#include "AnimManipulator.h"
|
||||
#include "AnimInverseKinematics.h"
|
||||
#include "AnimDefaultPose.h"
|
||||
|
@ -38,6 +39,7 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q
|
|||
static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadRandomSwitchStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
@ -51,6 +53,7 @@ static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
|
|||
// returns node on success, nullptr on failure.
|
||||
static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
||||
bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
||||
static const char* animNodeTypeToString(AnimNode::Type type) {
|
||||
switch (type) {
|
||||
|
@ -59,6 +62,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
|
|||
case AnimNode::Type::BlendLinearMove: return "blendLinearMove";
|
||||
case AnimNode::Type::Overlay: return "overlay";
|
||||
case AnimNode::Type::StateMachine: return "stateMachine";
|
||||
case AnimNode::Type::RandomSwitchStateMachine: return "randomSwitchStateMachine";
|
||||
case AnimNode::Type::Manipulator: return "manipulator";
|
||||
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
||||
case AnimNode::Type::DefaultPose: return "defaultPose";
|
||||
|
@ -92,6 +96,16 @@ static AnimStateMachine::InterpType stringToInterpType(const QString& str) {
|
|||
}
|
||||
}
|
||||
|
||||
static AnimRandomSwitch::InterpType stringToRandomInterpType(const QString& str) {
|
||||
if (str == "snapshotBoth") {
|
||||
return AnimRandomSwitch::InterpType::SnapshotBoth;
|
||||
} else if (str == "snapshotPrev") {
|
||||
return AnimRandomSwitch::InterpType::SnapshotPrev;
|
||||
} else {
|
||||
return AnimRandomSwitch::InterpType::NumTypes;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) {
|
||||
switch (type) {
|
||||
case AnimManipulator::JointVar::Type::Absolute: return "absolute";
|
||||
|
@ -122,6 +136,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
|||
case AnimNode::Type::BlendLinearMove: return loadBlendLinearMoveNode;
|
||||
case AnimNode::Type::Overlay: return loadOverlayNode;
|
||||
case AnimNode::Type::StateMachine: return loadStateMachineNode;
|
||||
case AnimNode::Type::RandomSwitchStateMachine: return loadRandomSwitchStateMachineNode;
|
||||
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
||||
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
||||
case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;
|
||||
|
@ -140,6 +155,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
|||
case AnimNode::Type::BlendLinearMove: return processDoNothing;
|
||||
case AnimNode::Type::Overlay: return processDoNothing;
|
||||
case AnimNode::Type::StateMachine: return processStateMachineNode;
|
||||
case AnimNode::Type::RandomSwitchStateMachine: return processRandomSwitchStateMachineNode;
|
||||
case AnimNode::Type::Manipulator: return processDoNothing;
|
||||
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
||||
case AnimNode::Type::DefaultPose: return processDoNothing;
|
||||
|
@ -463,6 +479,11 @@ static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const
|
|||
return node;
|
||||
}
|
||||
|
||||
static AnimNode::Pointer loadRandomSwitchStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||
auto node = std::make_shared<AnimRandomSwitch>(id);
|
||||
return node;
|
||||
}
|
||||
|
||||
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||
|
||||
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
|
||||
|
@ -780,6 +801,141 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) {
|
||||
auto smNode = std::static_pointer_cast<AnimRandomSwitch>(node);
|
||||
assert(smNode);
|
||||
|
||||
READ_STRING(currentState, jsonObj, nodeId, jsonUrl, false);
|
||||
READ_OPTIONAL_FLOAT(randomSwitchTimeMin, jsonObj, -1.0f);
|
||||
READ_OPTIONAL_FLOAT(randomSwitchTimeMax, jsonObj, -1.0f);
|
||||
READ_OPTIONAL_STRING(triggerRandomSwitch, jsonObj);
|
||||
READ_OPTIONAL_FLOAT(triggerTimeMin, jsonObj, -1.0f);
|
||||
READ_OPTIONAL_FLOAT(triggerTimeMax, jsonObj, -1.0f);
|
||||
READ_OPTIONAL_STRING(transitionVar, jsonObj);
|
||||
|
||||
|
||||
|
||||
auto statesValue = jsonObj.value("states");
|
||||
if (!statesValue.isArray()) {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad array \"states\" in random switch state Machine node, id =" << nodeId;
|
||||
return false;
|
||||
}
|
||||
|
||||
// build a map for all children by name.
|
||||
std::map<QString, int> childMap;
|
||||
buildChildMap(childMap, node);
|
||||
|
||||
// first pass parse all the states and build up the state and transition map.
|
||||
using StringPair = std::pair<QString, QString>;
|
||||
using TransitionMap = std::multimap<AnimRandomSwitch::RandomSwitchState::Pointer, StringPair>;
|
||||
TransitionMap transitionMap;
|
||||
|
||||
using RandomStateMap = std::map<QString, AnimRandomSwitch::RandomSwitchState::Pointer>;
|
||||
RandomStateMap randomStateMap;
|
||||
|
||||
auto randomStatesArray = statesValue.toArray();
|
||||
for (const auto& randomStateValue : randomStatesArray) {
|
||||
if (!randomStateValue.isObject()) {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad state object in \"random states\", id =" << nodeId;
|
||||
return false;
|
||||
}
|
||||
auto stateObj = randomStateValue.toObject();
|
||||
|
||||
READ_STRING(id, stateObj, nodeId, jsonUrl, false);
|
||||
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
|
||||
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
|
||||
READ_OPTIONAL_STRING(interpType, stateObj);
|
||||
READ_FLOAT(priority, stateObj, nodeId, jsonUrl, false);
|
||||
READ_BOOL(resume, stateObj, nodeId, jsonUrl, false);
|
||||
|
||||
READ_OPTIONAL_STRING(interpTargetVar, stateObj);
|
||||
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
|
||||
READ_OPTIONAL_STRING(interpTypeVar, stateObj);
|
||||
|
||||
auto iter = childMap.find(id);
|
||||
if (iter == childMap.end()) {
|
||||
qCCritical(animation) << "AnimNodeLoader, could not find random stateMachine child (state) with nodeId =" << nodeId << "random stateId =" << id;
|
||||
return false;
|
||||
}
|
||||
|
||||
AnimRandomSwitch::InterpType interpTypeEnum = AnimRandomSwitch::InterpType::SnapshotPrev; // default value
|
||||
if (!interpType.isEmpty()) {
|
||||
interpTypeEnum = stringToRandomInterpType(interpType);
|
||||
if (interpTypeEnum == AnimRandomSwitch::InterpType::NumTypes) {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad interpType on random state Machine state, nodeId = " << nodeId << "random stateId =" << id;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto randomStatePtr = std::make_shared<AnimRandomSwitch::RandomSwitchState>(id, iter->second, interpTarget, interpDuration, interpTypeEnum, priority, resume);
|
||||
if (priority > 0.0f) {
|
||||
smNode->addToPrioritySum(priority);
|
||||
}
|
||||
assert(randomStatePtr);
|
||||
|
||||
if (!interpTargetVar.isEmpty()) {
|
||||
randomStatePtr->setInterpTargetVar(interpTargetVar);
|
||||
}
|
||||
if (!interpDurationVar.isEmpty()) {
|
||||
randomStatePtr->setInterpDurationVar(interpDurationVar);
|
||||
}
|
||||
if (!interpTypeVar.isEmpty()) {
|
||||
randomStatePtr->setInterpTypeVar(interpTypeVar);
|
||||
}
|
||||
|
||||
smNode->addState(randomStatePtr);
|
||||
randomStateMap.insert(RandomStateMap::value_type(randomStatePtr->getID(), randomStatePtr));
|
||||
|
||||
auto transitionsValue = stateObj.value("transitions");
|
||||
if (!transitionsValue.isArray()) {
|
||||
qCritical(animation) << "AnimNodeLoader, bad array \"transitions\" in random state Machine node, stateId =" << id << "nodeId =" << nodeId;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto transitionsArray = transitionsValue.toArray();
|
||||
for (const auto& transitionValue : transitionsArray) {
|
||||
if (!transitionValue.isObject()) {
|
||||
qCritical(animation) << "AnimNodeLoader, bad transition object in \"transitions\", random stateId =" << id << "nodeId =" << nodeId;
|
||||
return false;
|
||||
}
|
||||
auto transitionObj = transitionValue.toObject();
|
||||
|
||||
READ_STRING(var, transitionObj, nodeId, jsonUrl, false);
|
||||
READ_STRING(randomSwitchState, transitionObj, nodeId, jsonUrl, false);
|
||||
|
||||
transitionMap.insert(TransitionMap::value_type(randomStatePtr, StringPair(var, randomSwitchState)));
|
||||
}
|
||||
}
|
||||
|
||||
// second pass: now iterate thru all transitions and add them to the appropriate states.
|
||||
for (auto& transition : transitionMap) {
|
||||
AnimRandomSwitch::RandomSwitchState::Pointer srcState = transition.first;
|
||||
auto iter = randomStateMap.find(transition.second.second);
|
||||
if (iter != randomStateMap.end()) {
|
||||
srcState->addTransition(AnimRandomSwitch::RandomSwitchState::Transition(transition.second.first, iter->second));
|
||||
} else {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad random state machine transition from srcState =" << srcState->_id << "dstState =" << transition.second.second << "nodeId =" << nodeId;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto iter = randomStateMap.find(currentState);
|
||||
if (iter == randomStateMap.end()) {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad currentState =" << currentState << "could not find child node" << "id =" << nodeId;
|
||||
}
|
||||
smNode->setCurrentState(iter->second);
|
||||
smNode->setRandomSwitchTimeMin(randomSwitchTimeMin);
|
||||
smNode->setRandomSwitchTimeMax(randomSwitchTimeMax);
|
||||
smNode->setTriggerRandomSwitchVar(triggerRandomSwitch);
|
||||
smNode->setTriggerTimeMin(triggerTimeMin);
|
||||
smNode->setTriggerTimeMax(triggerTimeMax);
|
||||
smNode->setTransitionVar(transitionVar);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AnimNodeLoader::AnimNodeLoader(const QUrl& url) :
|
||||
_url(url)
|
||||
{
|
||||
|
|
212
libraries/animation/src/AnimRandomSwitch.cpp
Normal file
212
libraries/animation/src/AnimRandomSwitch.cpp
Normal file
|
@ -0,0 +1,212 @@
|
|||
//
|
||||
// AnimRandomSwitch.cpp
|
||||
//
|
||||
// Created by Angus Antley on 4/8/2019.
|
||||
// Copyright (c) 2019 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AnimRandomSwitch.h"
|
||||
#include "AnimUtil.h"
|
||||
#include "AnimationLogging.h"
|
||||
|
||||
AnimRandomSwitch::AnimRandomSwitch(const QString& id) :
|
||||
AnimNode(AnimNode::Type::RandomSwitchStateMachine, id) {
|
||||
|
||||
}
|
||||
|
||||
AnimRandomSwitch::~AnimRandomSwitch() {
|
||||
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||
float parentDebugAlpha = context.getDebugAlpha(_id);
|
||||
|
||||
AnimRandomSwitch::RandomSwitchState::Pointer desiredState = _currentState;
|
||||
if (abs(_randomSwitchEvaluationCount - context.getEvaluationCount()) > 1 || animVars.lookup(_triggerRandomSwitchVar, false)) {
|
||||
|
||||
// get a random number and decide which motion to choose.
|
||||
bool currentStateHasPriority = false;
|
||||
float dice = randFloatInRange(0.0f, 1.0f);
|
||||
float lowerBound = 0.0f;
|
||||
for (const RandomSwitchState::Pointer& randState : _randomStates) {
|
||||
if (randState->getPriority() > 0.0f) {
|
||||
float upperBound = lowerBound + (randState->getPriority() / _totalPriorities);
|
||||
if ((dice > lowerBound) && (dice < upperBound)) {
|
||||
desiredState = randState;
|
||||
}
|
||||
lowerBound = upperBound;
|
||||
|
||||
// this indicates if the curent state is one that can be selected randomly, or is one that was transitioned to by the random duration timer.
|
||||
currentStateHasPriority = currentStateHasPriority || (_currentState == randState);
|
||||
}
|
||||
}
|
||||
if (abs(_randomSwitchEvaluationCount - context.getEvaluationCount()) > 1) {
|
||||
_duringInterp = false;
|
||||
switchRandomState(animVars, context, desiredState, _duringInterp);
|
||||
} else {
|
||||
// firing a random switch, be sure that we aren't completing a previously triggered transition
|
||||
if (currentStateHasPriority) {
|
||||
if (desiredState->getID() != _currentState->getID()) {
|
||||
_duringInterp = true;
|
||||
switchRandomState(animVars, context, desiredState, _duringInterp);
|
||||
} else {
|
||||
_duringInterp = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
_triggerTime = randFloatInRange(_triggerTimeMin, _triggerTimeMax);
|
||||
_randomSwitchTime = randFloatInRange(_randomSwitchTimeMin, _randomSwitchTimeMax);
|
||||
|
||||
} else {
|
||||
|
||||
// here we are checking to see if we want a temporary movement
|
||||
// evaluate currentState transitions
|
||||
auto transitionState = evaluateTransitions(animVars);
|
||||
if (transitionState != _currentState) {
|
||||
_duringInterp = true;
|
||||
switchRandomState(animVars, context, transitionState, _duringInterp);
|
||||
_triggerTime = randFloatInRange(_triggerTimeMin, _triggerTimeMax);
|
||||
_randomSwitchTime = randFloatInRange(_randomSwitchTimeMin, _randomSwitchTimeMax);
|
||||
}
|
||||
}
|
||||
|
||||
_triggerTime -= dt;
|
||||
if ((_triggerTime < 0.0f) && (_triggerTimeMin > 0.0f) && (_triggerTimeMax > 0.0f)) {
|
||||
_triggerTime = randFloatInRange(_triggerTimeMin, _triggerTimeMax);
|
||||
triggersOut.setTrigger(_transitionVar);
|
||||
}
|
||||
|
||||
_randomSwitchTime -= dt;
|
||||
if ((_randomSwitchTime < 0.0f) && (_randomSwitchTimeMin > 0.0f) && (_randomSwitchTimeMax > 0.0f)) {
|
||||
_randomSwitchTime = randFloatInRange(_randomSwitchTimeMin, _randomSwitchTimeMax);
|
||||
// restart the trigger timer if it is also enabled
|
||||
_triggerTime = randFloatInRange(_triggerTimeMin, _triggerTimeMax);
|
||||
triggersOut.setTrigger(_triggerRandomSwitchVar);
|
||||
}
|
||||
|
||||
assert(_currentState);
|
||||
auto currentStateNode = _children[_currentState->getChildIndex()];
|
||||
assert(currentStateNode);
|
||||
|
||||
if (_duringInterp) {
|
||||
_alpha += _alphaVel * dt;
|
||||
if (_alpha < 1.0f) {
|
||||
AnimPoseVec* nextPoses = nullptr;
|
||||
AnimPoseVec* prevPoses = nullptr;
|
||||
AnimPoseVec localNextPoses;
|
||||
if (_interpType == InterpType::SnapshotBoth) {
|
||||
// interp between both snapshots
|
||||
prevPoses = &_prevPoses;
|
||||
nextPoses = &_nextPoses;
|
||||
} else if (_interpType == InterpType::SnapshotPrev) {
|
||||
// interp between the prev snapshot and evaluated next target.
|
||||
// this is useful for interping into a blend
|
||||
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||
prevPoses = &_prevPoses;
|
||||
nextPoses = &localNextPoses;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
||||
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
|
||||
}
|
||||
context.setDebugAlpha(_currentState->getID(), _alpha * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||
} else {
|
||||
_duringInterp = false;
|
||||
_prevPoses.clear();
|
||||
_nextPoses.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (!_duringInterp){
|
||||
context.setDebugAlpha(_currentState->getID(), parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||
_poses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||
}
|
||||
|
||||
_randomSwitchEvaluationCount = context.getEvaluationCount();
|
||||
processOutputJoints(triggersOut);
|
||||
|
||||
context.addStateMachineInfo(_id, _currentState->getID(), _previousState->getID(), _duringInterp, _alpha);
|
||||
if (_duringInterp) {
|
||||
// hack: add previoius state to debug alpha map, with parens around it's name.
|
||||
context.setDebugAlpha(QString("(%1)").arg(_previousState->getID()), 1.0f - _alpha, AnimNodeType::Clip);
|
||||
}
|
||||
|
||||
return _poses;
|
||||
}
|
||||
|
||||
void AnimRandomSwitch::setCurrentState(RandomSwitchState::Pointer randomState) {
|
||||
_previousState = _currentState ? _currentState : randomState;
|
||||
_currentState = randomState;
|
||||
}
|
||||
|
||||
void AnimRandomSwitch::addState(RandomSwitchState::Pointer randomState) {
|
||||
_randomStates.push_back(randomState);
|
||||
}
|
||||
|
||||
void AnimRandomSwitch::switchRandomState(const AnimVariantMap& animVars, const AnimContext& context, RandomSwitchState::Pointer desiredState, bool shouldInterp) {
|
||||
|
||||
auto nextStateNode = _children[desiredState->getChildIndex()];
|
||||
if (shouldInterp) {
|
||||
|
||||
const float FRAMES_PER_SECOND = 30.0f;
|
||||
|
||||
auto prevStateNode = _children[_currentState->getChildIndex()];
|
||||
|
||||
_alpha = 0.0f;
|
||||
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
|
||||
_alphaVel = FRAMES_PER_SECOND / duration;
|
||||
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
|
||||
|
||||
// because dt is 0, we should not encounter any triggers
|
||||
const float dt = 0.0f;
|
||||
AnimVariantMap triggers;
|
||||
|
||||
if (_interpType == InterpType::SnapshotBoth) {
|
||||
// snapshot previous pose.
|
||||
_prevPoses = _poses;
|
||||
// snapshot next pose at the target frame.
|
||||
if (!desiredState->getResume()) {
|
||||
nextStateNode->setCurrentFrame(desiredState->_interpTarget);
|
||||
}
|
||||
_nextPoses = nextStateNode->evaluate(animVars, context, dt, triggers);
|
||||
} else if (_interpType == InterpType::SnapshotPrev) {
|
||||
// snapshot previoius pose
|
||||
_prevPoses = _poses;
|
||||
// no need to evaluate _nextPoses we will do it dynamically during the interp,
|
||||
// however we need to set the current frame.
|
||||
if (!desiredState->getResume()) {
|
||||
nextStateNode->setCurrentFrame(desiredState->_interpTarget - duration);
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
} else {
|
||||
if (!desiredState->getResume()) {
|
||||
nextStateNode->setCurrentFrame(desiredState->_interpTarget);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(animation) << "AnimRandomSwitch::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget << "interpType = " << (int)_interpType;
|
||||
#endif
|
||||
|
||||
setCurrentState(desiredState);
|
||||
}
|
||||
|
||||
AnimRandomSwitch::RandomSwitchState::Pointer AnimRandomSwitch::evaluateTransitions(const AnimVariantMap& animVars) const {
|
||||
assert(_currentState);
|
||||
for (auto& transition : _currentState->_transitions) {
|
||||
if (animVars.lookup(transition._var, false)) {
|
||||
return transition._randomSwitchState;
|
||||
}
|
||||
}
|
||||
return _currentState;
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimRandomSwitch::getPosesInternal() const {
|
||||
return _poses;
|
||||
}
|
184
libraries/animation/src/AnimRandomSwitch.h
Normal file
184
libraries/animation/src/AnimRandomSwitch.h
Normal file
|
@ -0,0 +1,184 @@
|
|||
//
|
||||
// AnimRandomSwitch.h
|
||||
//
|
||||
// Created by Angus Antley on 4/8/19.
|
||||
// Copyright (c) 2019 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AnimRandomSwitch_h
|
||||
#define hifi_AnimRandomSwitch_h
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "AnimNode.h"
|
||||
|
||||
// Random Switch State Machine for random transitioning between children AnimNodes
|
||||
//
|
||||
// This is mechanisim for choosing and playing a random animation and smoothly interpolating/fading
|
||||
// between them. A RandomSwitch has a set of States, which typically reference
|
||||
// child AnimNodes. Each Random Switch State has a list of Transitions, which are evaluated
|
||||
// to determine when we should switch to a new State. Parameters for the smooth
|
||||
// interpolation/fading are read from the Random Switch State that you are transitioning to.
|
||||
//
|
||||
// The currentState can be set directly via the setCurrentStateVar() and will override
|
||||
// any State transitions.
|
||||
//
|
||||
// Each Random Switch State has two parameters that can be changed via AnimVars,
|
||||
// * interpTarget - (frames) The destination frame of the interpolation. i.e. the first frame of the animation that will
|
||||
// visible after interpolation is complete.
|
||||
// * interpDuration - (frames) The total length of time it will take to interp between the current pose and the
|
||||
// interpTarget frame.
|
||||
// * interpType - How the interpolation is performed.
|
||||
// * priority - this number represents how likely this Random Switch State will be chosen.
|
||||
// the priority for each Random Switch State will be normalized, so their relative size is what is important
|
||||
// * resume - if resume is false then if this state is chosen twice in a row it will remember what frame it was playing on.
|
||||
// * SnapshotBoth: Stores two snapshots, the previous animation before interpolation begins and the target state at the
|
||||
// interTarget frame. Then during the interpolation period the two snapshots are interpolated to produce smooth motion between them.
|
||||
// * SnapshotPrev: Stores a snapshot of the previous animation before interpolation begins. However the target state is
|
||||
// evaluated dynamically. During the interpolation period the previous snapshot is interpolated with the target pose
|
||||
// to produce smooth motion between them. This mode is useful for interping into a blended animation where the actual
|
||||
// blend factor is not known at the start of the interp or is might change dramatically during the interp.
|
||||
//
|
||||
|
||||
class AnimRandomSwitch : public AnimNode {
|
||||
public:
|
||||
friend class AnimNodeLoader;
|
||||
friend bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl);
|
||||
|
||||
enum class InterpType {
|
||||
SnapshotBoth = 0,
|
||||
SnapshotPrev,
|
||||
NumTypes
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
class RandomSwitchState {
|
||||
public:
|
||||
friend AnimRandomSwitch;
|
||||
friend bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl);
|
||||
|
||||
using Pointer = std::shared_ptr<RandomSwitchState>;
|
||||
using ConstPointer = std::shared_ptr<const RandomSwitchState>;
|
||||
|
||||
class Transition {
|
||||
public:
|
||||
friend AnimRandomSwitch;
|
||||
Transition(const QString& var, RandomSwitchState::Pointer randomState) : _var(var), _randomSwitchState(randomState) {}
|
||||
protected:
|
||||
QString _var;
|
||||
RandomSwitchState::Pointer _randomSwitchState;
|
||||
};
|
||||
|
||||
RandomSwitchState(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType, float priority, bool resume) :
|
||||
_id(id),
|
||||
_childIndex(childIndex),
|
||||
_interpTarget(interpTarget),
|
||||
_interpDuration(interpDuration),
|
||||
_interpType(interpType),
|
||||
_priority(priority),
|
||||
_resume(resume){
|
||||
}
|
||||
|
||||
void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; }
|
||||
void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; }
|
||||
void setInterpTypeVar(const QString& interpTypeVar) { _interpTypeVar = interpTypeVar; }
|
||||
|
||||
int getChildIndex() const { return _childIndex; }
|
||||
float getPriority() const { return _priority; }
|
||||
bool getResume() const { return _resume; }
|
||||
const QString& getID() const { return _id; }
|
||||
|
||||
protected:
|
||||
|
||||
void setInterpTarget(float interpTarget) { _interpTarget = interpTarget; }
|
||||
void setInterpDuration(float interpDuration) { _interpDuration = interpDuration; }
|
||||
void setPriority(float priority) { _priority = priority; }
|
||||
void setResumeFlag(bool resume) { _resume = resume; }
|
||||
|
||||
void addTransition(const Transition& transition) { _transitions.push_back(transition); }
|
||||
|
||||
QString _id;
|
||||
int _childIndex;
|
||||
float _interpTarget; // frames
|
||||
float _interpDuration; // frames
|
||||
InterpType _interpType;
|
||||
float _priority {0.0f};
|
||||
bool _resume {false};
|
||||
|
||||
QString _interpTargetVar;
|
||||
QString _interpDurationVar;
|
||||
QString _interpTypeVar;
|
||||
|
||||
std::vector<Transition> _transitions;
|
||||
|
||||
private:
|
||||
// no copies
|
||||
RandomSwitchState(const RandomSwitchState&) = delete;
|
||||
RandomSwitchState& operator=(const RandomSwitchState&) = delete;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
explicit AnimRandomSwitch(const QString& id);
|
||||
virtual ~AnimRandomSwitch() override;
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||
|
||||
void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; }
|
||||
|
||||
protected:
|
||||
|
||||
void setCurrentState(RandomSwitchState::Pointer randomState);
|
||||
void setTriggerRandomSwitchVar(const QString& triggerRandomSwitchVar) { _triggerRandomSwitchVar = triggerRandomSwitchVar; }
|
||||
void setRandomSwitchTimeMin(float randomSwitchTimeMin) { _randomSwitchTimeMin = randomSwitchTimeMin; }
|
||||
void setRandomSwitchTimeMax(float randomSwitchTimeMax) { _randomSwitchTimeMax = randomSwitchTimeMax; }
|
||||
void setTransitionVar(const QString& transitionVar) { _transitionVar = transitionVar; }
|
||||
void setTriggerTimeMin(float triggerTimeMin) { _triggerTimeMin = triggerTimeMin; }
|
||||
void setTriggerTimeMax(float triggerTimeMax) { _triggerTimeMax = triggerTimeMax; }
|
||||
void addToPrioritySum(float priority) { _totalPriorities += priority; }
|
||||
|
||||
void addState(RandomSwitchState::Pointer randomState);
|
||||
|
||||
void switchRandomState(const AnimVariantMap& animVars, const AnimContext& context, RandomSwitchState::Pointer desiredState, bool shouldInterp);
|
||||
RandomSwitchState::Pointer evaluateTransitions(const AnimVariantMap& animVars) const;
|
||||
|
||||
// for AnimDebugDraw rendering
|
||||
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||
|
||||
AnimPoseVec _poses;
|
||||
|
||||
int _randomSwitchEvaluationCount { 0 };
|
||||
// interpolation state
|
||||
bool _duringInterp = false;
|
||||
InterpType _interpType{ InterpType::SnapshotPrev };
|
||||
float _alphaVel = 0.0f;
|
||||
float _alpha = 0.0f;
|
||||
AnimPoseVec _prevPoses;
|
||||
AnimPoseVec _nextPoses;
|
||||
float _totalPriorities { 0.0f };
|
||||
|
||||
RandomSwitchState::Pointer _currentState;
|
||||
RandomSwitchState::Pointer _previousState;
|
||||
std::vector<RandomSwitchState::Pointer> _randomStates;
|
||||
|
||||
QString _currentStateVar;
|
||||
QString _triggerRandomSwitchVar;
|
||||
QString _transitionVar;
|
||||
float _triggerTimeMin { 10.0f };
|
||||
float _triggerTimeMax { 20.0f };
|
||||
float _triggerTime { 0.0f };
|
||||
float _randomSwitchTimeMin { 10.0f };
|
||||
float _randomSwitchTimeMax { 20.0f };
|
||||
float _randomSwitchTime { 0.0f };
|
||||
|
||||
private:
|
||||
// no copies
|
||||
AnimRandomSwitch(const AnimRandomSwitch&) = delete;
|
||||
AnimRandomSwitch& operator=(const AnimRandomSwitch&) = delete;
|
||||
};
|
||||
|
||||
#endif // hifi_AnimRandomSwitch_h
|
|
@ -26,8 +26,9 @@ public:
|
|||
* <tr><td><code>0</code></td><td>RotationAndPosition</td><td>Attempt to reach the rotation and position end
|
||||
* effector.</td></tr>
|
||||
* <tr><td><code>1</code></td><td>RotationOnly</td><td>Attempt to reach the end effector rotation only.</td></tr>
|
||||
* <tr><td><code>2</code></td><td>HmdHead</td><td><strong>Deprecated:</strong> A special mode of IK that would attempt
|
||||
* to prevent unnecessary bending of the spine.</td></tr>
|
||||
* <tr><td><code>2</code></td><td>HmdHead</td><td>A special mode of IK that would attempt to prevent unnecessary
|
||||
* bending of the spine.<br />
|
||||
* <p class="important">Deprecated: This target type is deprecated and will be removed.</p></td></tr>
|
||||
* <tr><td><code>3</code></td><td>HipsRelativeRotationAndPosition</td><td>Attempt to reach a rotation and position end
|
||||
* effector that is not in absolute rig coordinates but is offset by the avatar hips translation.</td></tr>
|
||||
* <tr><td><code>4</code></td><td>Spline</td><td>Use a cubic Hermite spline to model the human spine. This prevents
|
||||
|
|
|
@ -1480,13 +1480,15 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
|||
if (_animNode && _enabledAnimations) {
|
||||
DETAILED_PERFORMANCE_TIMER("handleTriggers");
|
||||
|
||||
++_evaluationCount;
|
||||
|
||||
updateAnimationStateHandlers();
|
||||
_animVars.setRigToGeometryTransform(_rigToGeometryTransform);
|
||||
if (_networkNode) {
|
||||
_networkVars.setRigToGeometryTransform(_rigToGeometryTransform);
|
||||
}
|
||||
AnimContext context(_enableDebugDrawIKTargets, _enableDebugDrawIKConstraints, _enableDebugDrawIKChains,
|
||||
getGeometryToRigTransform(), rigToWorldTransform);
|
||||
getGeometryToRigTransform(), rigToWorldTransform, _evaluationCount);
|
||||
|
||||
// evaluate the animation
|
||||
AnimVariantMap triggersOut;
|
||||
|
@ -2009,8 +2011,35 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
|||
return;
|
||||
}
|
||||
|
||||
_animVars.set("isTalking", params.isTalking);
|
||||
_animVars.set("notIsTalking", !params.isTalking);
|
||||
if (_previousIsTalking != params.isTalking) {
|
||||
if (_talkIdleInterpTime < 1.0f) {
|
||||
_talkIdleInterpTime = 1.0f - _talkIdleInterpTime;
|
||||
} else {
|
||||
_talkIdleInterpTime = 0.0f;
|
||||
}
|
||||
}
|
||||
_previousIsTalking = params.isTalking;
|
||||
|
||||
const float TOTAL_EASE_IN_TIME = 0.75f;
|
||||
const float TOTAL_EASE_OUT_TIME = 1.5f;
|
||||
if (params.isTalking) {
|
||||
if (_talkIdleInterpTime < 1.0f) {
|
||||
_talkIdleInterpTime += dt / TOTAL_EASE_IN_TIME;
|
||||
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
|
||||
_animVars.set("idleOverlayAlpha", easeOutInValue);
|
||||
} else {
|
||||
_animVars.set("idleOverlayAlpha", 1.0f);
|
||||
}
|
||||
} else {
|
||||
if (_talkIdleInterpTime < 1.0f) {
|
||||
_talkIdleInterpTime += dt / TOTAL_EASE_OUT_TIME;
|
||||
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
|
||||
float talkAlpha = 1.0f - easeOutInValue;
|
||||
_animVars.set("idleOverlayAlpha", talkAlpha);
|
||||
} else {
|
||||
_animVars.set("idleOverlayAlpha", 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
_headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled;
|
||||
bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled;
|
||||
|
|
|
@ -418,9 +418,12 @@ protected:
|
|||
HandAnimState _rightHandAnimState;
|
||||
HandAnimState _leftHandAnimState;
|
||||
std::map<QString, RoleAnimState> _roleAnimStates;
|
||||
int _evaluationCount{ 0 };
|
||||
|
||||
float _leftHandOverlayAlpha { 0.0f };
|
||||
float _rightHandOverlayAlpha { 0.0f };
|
||||
float _talkIdleInterpTime { 0.0f };
|
||||
bool _previousIsTalking { false };
|
||||
|
||||
SimpleMovingAverage _averageForwardSpeed { 10 };
|
||||
SimpleMovingAverage _averageLateralSpeed { 10 };
|
||||
|
|
|
@ -48,21 +48,23 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* Configures how an audio injector plays its audio.
|
||||
* Configures where and how an audio injector plays its audio.
|
||||
* @typedef {object} AudioInjector.AudioInjectorOptions
|
||||
* @property {Vec3} position=Vec3.ZERO - The position in the domain to play the sound.
|
||||
* @property {Quat} orientation=Quat.IDENTITY - The orientation in the domain to play the sound in.
|
||||
* @property {number} volume=1.0 - Playback volume, between <code>0.0</code> and <code>1.0</code>.
|
||||
* @property {number} pitch=1.0 - Alter the pitch of the sound, within +/- 2 octaves. The value is the relative sample rate to
|
||||
* resample the sound at, range <code>0.0625</code> – <code>16.0</code>. A value of <code>0.0625</code> lowers the
|
||||
* pitch by 2 octaves; <code>1.0</code> is no change in pitch; <code>16.0</code> raises the pitch by 2 octaves.
|
||||
* resample the sound at, range <code>0.0625</code> – <code>16.0</code>.<br />
|
||||
* A value of <code>0.0625</code> lowers the pitch by 2 octaves.<br />
|
||||
* A value of <code>1.0</code> means there is no change in pitch.<br />
|
||||
* A value of <code>16.0</code> raises the pitch by 2 octaves.
|
||||
* @property {boolean} loop=false - If <code>true</code>, the sound is played repeatedly until playback is stopped.
|
||||
* @property {number} secondOffset=0 - Starts playback from a specified time (seconds) within the sound file, ≥
|
||||
* <code>0</code>.
|
||||
* @property {boolean} localOnly=false - IF <code>true</code>, the sound is played back locally on the client rather than to
|
||||
* @property {boolean} localOnly=false - If <code>true</code>, the sound is played back locally on the client rather than to
|
||||
* others via the audio mixer.
|
||||
* @property {boolean} ignorePenumbra=false - <strong>Deprecated:</strong> This property is deprecated and will be
|
||||
* removed.
|
||||
* @property {boolean} ignorePenumbra=false - <p class="important">Deprecated: This property is deprecated and will be
|
||||
* removed.</p>
|
||||
*/
|
||||
void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOptions& injectorOptions) {
|
||||
if (!object.isObject()) {
|
||||
|
|
|
@ -124,7 +124,7 @@ typedef QSharedPointer<Sound> SharedSoundPointer;
|
|||
* An audio resource, created by {@link SoundCache.getSound}, to be played back using {@link Audio.playSound}.
|
||||
* <p>Supported formats:</p>
|
||||
* <ul>
|
||||
* <li>WAV: 16-bit uncompressed WAV at any sample rate, with 1 (mono), 2 (stereo), or 4 (ambisonic) channels.</li>
|
||||
* <li>WAV: 16-bit uncompressed at any sample rate, with 1 (mono), 2 (stereo), or 4 (ambisonic) channels.</li>
|
||||
* <li>MP3: Mono or stereo, at any sample rate.</li>
|
||||
* <li>RAW: 48khz 16-bit mono or stereo. File name must include <code>".stereo"</code> to be interpreted as stereo.</li>
|
||||
* </ul>
|
||||
|
|
|
@ -1469,6 +1469,37 @@ QStringList Avatar::getJointNames() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> Avatar::getSkeletonDefaultData() {
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> defaultSkeletonData;
|
||||
if (_skeletonModel->isLoaded()) {
|
||||
auto& model = _skeletonModel->getHFMModel();
|
||||
auto& rig = _skeletonModel->getRig();
|
||||
float geometryToRigScale = extractScale(rig.getGeometryToRigTransform())[0];
|
||||
QStringList jointNames = getJointNames();
|
||||
int sizeCount = 0;
|
||||
for (int i = 0; i < jointNames.size(); i++) {
|
||||
AvatarSkeletonTrait::UnpackedJointData jointData;
|
||||
jointData.jointIndex = i;
|
||||
jointData.parentIndex = rig.getJointParentIndex(i);
|
||||
if (jointData.parentIndex == -1) {
|
||||
jointData.boneType = model.joints[i].isSkeletonJoint ? AvatarSkeletonTrait::BoneType::SkeletonRoot : AvatarSkeletonTrait::BoneType::NonSkeletonRoot;
|
||||
} else {
|
||||
jointData.boneType = model.joints[i].isSkeletonJoint ? AvatarSkeletonTrait::BoneType::SkeletonChild : AvatarSkeletonTrait::BoneType::NonSkeletonChild;
|
||||
}
|
||||
jointData.defaultRotation = rig.getAbsoluteDefaultPose(i).rot();
|
||||
jointData.defaultTranslation = getDefaultJointTranslation(i);
|
||||
float jointLocalScale = extractScale(model.joints[i].transform)[0];
|
||||
jointData.defaultScale = jointLocalScale / geometryToRigScale;
|
||||
jointData.jointName = jointNames[i];
|
||||
jointData.stringLength = jointNames[i].size();
|
||||
jointData.stringStart = sizeCount;
|
||||
sizeCount += jointNames[i].size();
|
||||
defaultSkeletonData.push_back(jointData);
|
||||
}
|
||||
}
|
||||
return defaultSkeletonData;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getJointPosition(int index) const {
|
||||
glm::vec3 position;
|
||||
_skeletonModel->getJointPositionInWorldFrame(index, position);
|
||||
|
@ -1535,6 +1566,8 @@ void Avatar::rigReady() {
|
|||
buildSpine2SplineRatioCache();
|
||||
computeMultiSphereShapes();
|
||||
buildSpine2SplineRatioCache();
|
||||
setSkeletonData(getSkeletonDefaultData());
|
||||
sendSkeletonData();
|
||||
}
|
||||
|
||||
// rig has been reset.
|
||||
|
|
|
@ -199,6 +199,8 @@ public:
|
|||
virtual int getJointIndex(const QString& name) const override;
|
||||
virtual QStringList getJointNames() const override;
|
||||
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> getSkeletonDefaultData();
|
||||
|
||||
/**jsdoc
|
||||
* Gets the default rotation of a joint (in the current avatar) relative to its parent.
|
||||
* <p>For information on the joint hierarchy used, see
|
||||
|
|
|
@ -55,7 +55,7 @@ using namespace std;
|
|||
const QString AvatarData::FRAME_NAME = "com.highfidelity.recording.AvatarData";
|
||||
|
||||
static const int TRANSLATION_COMPRESSION_RADIX = 14;
|
||||
static const int FAUX_JOINT_COMPRESSION_RADIX = 12;
|
||||
static const int HAND_CONTROLLER_COMPRESSION_RADIX = 12;
|
||||
static const int SENSOR_TO_WORLD_SCALE_RADIX = 10;
|
||||
static const float AUDIO_LOUDNESS_SCALE = 1024.0f;
|
||||
static const float DEFAULT_AVATAR_DENSITY = 1000.0f; // density of water
|
||||
|
@ -66,7 +66,7 @@ size_t AvatarDataPacket::maxFaceTrackerInfoSize(size_t numBlendshapeCoefficients
|
|||
return FACE_TRACKER_INFO_SIZE + numBlendshapeCoefficients * sizeof(float);
|
||||
}
|
||||
|
||||
size_t AvatarDataPacket::maxJointDataSize(size_t numJoints, bool hasGrabJoints) {
|
||||
size_t AvatarDataPacket::maxJointDataSize(size_t numJoints) {
|
||||
const size_t validityBitsSize = calcBitVectorSize((int)numJoints);
|
||||
|
||||
size_t totalSize = sizeof(uint8_t); // numJoints
|
||||
|
@ -76,14 +76,6 @@ size_t AvatarDataPacket::maxJointDataSize(size_t numJoints, bool hasGrabJoints)
|
|||
totalSize += validityBitsSize; // Translations mask
|
||||
totalSize += sizeof(float); // maxTranslationDimension
|
||||
totalSize += numJoints * sizeof(SixByteTrans); // Translations
|
||||
|
||||
size_t NUM_FAUX_JOINT = 2;
|
||||
totalSize += NUM_FAUX_JOINT * (sizeof(SixByteQuat) + sizeof(SixByteTrans)); // faux joints
|
||||
|
||||
if (hasGrabJoints) {
|
||||
totalSize += sizeof(AvatarDataPacket::FarGrabJoints);
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
|
@ -98,9 +90,6 @@ size_t AvatarDataPacket::minJointDataSize(size_t numJoints) {
|
|||
totalSize += sizeof(float); // maxTranslationDimension
|
||||
// assume no valid translations
|
||||
|
||||
size_t NUM_FAUX_JOINT = 2;
|
||||
totalSize += NUM_FAUX_JOINT * (sizeof(SixByteQuat) + sizeof(SixByteTrans)); // faux joints
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
|
@ -329,6 +318,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
// separately
|
||||
bool hasParentInfo = false;
|
||||
bool hasAvatarLocalPosition = false;
|
||||
bool hasHandControllers = false;
|
||||
|
||||
bool hasFaceTrackerInfo = false;
|
||||
|
||||
|
@ -346,7 +336,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
hasAvatarLocalPosition = hasParent() && (sendAll ||
|
||||
tranlationChangedSince(lastSentTime) ||
|
||||
parentInfoChangedSince(lastSentTime));
|
||||
|
||||
hasHandControllers = _controllerLeftHandMatrixCache.isValid() || _controllerRightHandMatrixCache.isValid();
|
||||
hasFaceTrackerInfo = !dropFaceTracking && (hasFaceTracker() || getHasScriptedBlendshapes()) &&
|
||||
(sendAll || faceTrackerInfoChangedSince(lastSentTime));
|
||||
hasJointData = !sendMinimum;
|
||||
|
@ -364,6 +354,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
| (hasAdditionalFlags ? AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS : 0)
|
||||
| (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0)
|
||||
| (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0)
|
||||
| (hasHandControllers ? AvatarDataPacket::PACKET_HAS_HAND_CONTROLLERS : 0)
|
||||
| (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0)
|
||||
| (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0)
|
||||
| (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0)
|
||||
|
@ -406,7 +397,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
const size_t byteArraySize = AvatarDataPacket::MAX_CONSTANT_HEADER_SIZE + NUM_BYTES_RFC4122_UUID +
|
||||
AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getBlendshapeCoefficients().size()) +
|
||||
AvatarDataPacket::maxJointDataSize(_jointData.size(), true) +
|
||||
AvatarDataPacket::maxJointDataSize(_jointData.size()) +
|
||||
AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size());
|
||||
|
||||
if (maxDataSize == 0) {
|
||||
|
@ -592,7 +583,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
}
|
||||
}
|
||||
|
||||
IF_AVATAR_SPACE(PACKET_HAS_AVATAR_LOCAL_POSITION, sizeof(getLocalPosition()) ) {
|
||||
IF_AVATAR_SPACE(PACKET_HAS_AVATAR_LOCAL_POSITION, AvatarDataPacket::AVATAR_LOCAL_POSITION_SIZE) {
|
||||
auto startSection = destinationBuffer;
|
||||
const auto localPosition = getLocalPosition();
|
||||
AVATAR_MEMCPY(localPosition);
|
||||
|
@ -603,6 +594,23 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
}
|
||||
}
|
||||
|
||||
IF_AVATAR_SPACE(PACKET_HAS_HAND_CONTROLLERS, AvatarDataPacket::HAND_CONTROLLERS_SIZE) {
|
||||
auto startSection = destinationBuffer;
|
||||
|
||||
Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix());
|
||||
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerLeftHandTransform.getRotation());
|
||||
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerLeftHandTransform.getTranslation(), HAND_CONTROLLER_COMPRESSION_RADIX);
|
||||
|
||||
Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix());
|
||||
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerRightHandTransform.getRotation());
|
||||
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(), HAND_CONTROLLER_COMPRESSION_RADIX);
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
if (outboundDataRateOut) {
|
||||
outboundDataRateOut->handControllersRate.increment(numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& blendshapeCoefficients = _headData->getBlendshapeCoefficients();
|
||||
// If it is connected, pack up the data
|
||||
IF_AVATAR_SPACE(PACKET_HAS_FACE_TRACKER_INFO, sizeof(AvatarDataPacket::FaceTrackerInfo) + (size_t)blendshapeCoefficients.size() * sizeof(float)) {
|
||||
|
@ -638,9 +646,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
// include jointData if there is room for the most minimal section. i.e. no translations or rotations.
|
||||
IF_AVATAR_SPACE(PACKET_HAS_JOINT_DATA, AvatarDataPacket::minJointDataSize(numJoints)) {
|
||||
// Minimum space required for another rotation joint -
|
||||
// size of joint + following translation bit-vector + translation scale + faux joints:
|
||||
const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat) + jointBitVectorSize +
|
||||
sizeof(float) + AvatarDataPacket::FAUX_JOINTS_SIZE;
|
||||
// size of joint + following translation bit-vector + translation scale:
|
||||
const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat) + jointBitVectorSize + sizeof(float);
|
||||
|
||||
auto startSection = destinationBuffer;
|
||||
|
||||
|
@ -759,17 +766,6 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
}
|
||||
sendStatus.translationsSent = i;
|
||||
|
||||
// faux joints
|
||||
Transform controllerLeftHandTransform = Transform(getControllerLeftHandMatrix());
|
||||
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerLeftHandTransform.getRotation());
|
||||
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerLeftHandTransform.getTranslation(),
|
||||
FAUX_JOINT_COMPRESSION_RADIX);
|
||||
|
||||
Transform controllerRightHandTransform = Transform(getControllerRightHandMatrix());
|
||||
destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, controllerRightHandTransform.getRotation());
|
||||
destinationBuffer += packFloatVec3ToSignedTwoByteFixed(destinationBuffer, controllerRightHandTransform.getTranslation(),
|
||||
FAUX_JOINT_COMPRESSION_RADIX);
|
||||
|
||||
IF_AVATAR_SPACE(PACKET_HAS_GRAB_JOINTS, sizeof (AvatarDataPacket::FarGrabJoints)) {
|
||||
// the far-grab joints may range further than 3 meters, so we can't use packFloatVec3ToSignedTwoByteFixed etc
|
||||
auto startSection = destinationBuffer;
|
||||
|
@ -902,12 +898,12 @@ bool AvatarData::shouldLogError(const quint64& now) {
|
|||
}
|
||||
|
||||
|
||||
const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSafeValueCache<glm::mat4>& matrixCache) {
|
||||
const unsigned char* unpackHandController(const unsigned char* sourceBuffer, ThreadSafeValueCache<glm::mat4>& matrixCache) {
|
||||
glm::quat orientation;
|
||||
glm::vec3 position;
|
||||
Transform transform;
|
||||
sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, orientation);
|
||||
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, position, FAUX_JOINT_COMPRESSION_RADIX);
|
||||
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, position, HAND_CONTROLLER_COMPRESSION_RADIX);
|
||||
transform.setTranslation(position);
|
||||
transform.setRotation(orientation);
|
||||
matrixCache.set(transform.getMatrix());
|
||||
|
@ -952,6 +948,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
bool hasAdditionalFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS);
|
||||
bool hasParentInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO);
|
||||
bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION);
|
||||
bool hasHandControllers = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_HAND_CONTROLLERS);
|
||||
bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO);
|
||||
bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA);
|
||||
bool hasJointDefaultPoseFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS);
|
||||
|
@ -1240,6 +1237,20 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
_localPositionUpdateRate.increment();
|
||||
}
|
||||
|
||||
if (hasHandControllers) {
|
||||
auto startSection = sourceBuffer;
|
||||
|
||||
sourceBuffer = unpackHandController(sourceBuffer, _controllerLeftHandMatrixCache);
|
||||
sourceBuffer = unpackHandController(sourceBuffer, _controllerRightHandMatrixCache);
|
||||
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_handControllersRate.increment(numBytesRead);
|
||||
_handControllersUpdateRate.increment();
|
||||
} else {
|
||||
_controllerLeftHandMatrixCache.invalidate();
|
||||
_controllerRightHandMatrixCache.invalidate();
|
||||
}
|
||||
|
||||
if (hasFaceTrackerInfo) {
|
||||
auto startSection = sourceBuffer;
|
||||
|
||||
|
@ -1351,10 +1362,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
<< "size:" << (int)(sourceBuffer - startPosition);
|
||||
}
|
||||
#endif
|
||||
// faux joints
|
||||
sourceBuffer = unpackFauxJoint(sourceBuffer, _controllerLeftHandMatrixCache);
|
||||
sourceBuffer = unpackFauxJoint(sourceBuffer, _controllerRightHandMatrixCache);
|
||||
|
||||
int numBytesRead = sourceBuffer - startSection;
|
||||
_jointDataRate.increment(numBytesRead);
|
||||
_jointDataUpdateRate.increment();
|
||||
|
@ -1445,6 +1452,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
* <tbody>
|
||||
* <tr><td><code>"globalPosition"</code></td><td>Incoming global position.</td></tr>
|
||||
* <tr><td><code>"localPosition"</code></td><td>Incoming local position.</td></tr>
|
||||
* <tr><td><code>"handControllers"</code></td><td>Incoming hand controllers.</td></tr>
|
||||
* <tr><td><code>"avatarBoundingBox"</code></td><td>Incoming avatar bounding box.</td></tr>
|
||||
* <tr><td><code>"avatarOrientation"</code></td><td>Incoming avatar orientation.</td></tr>
|
||||
* <tr><td><code>"avatarScale"</code></td><td>Incoming avatar scale.</td></tr>
|
||||
|
@ -1483,6 +1491,8 @@ float AvatarData::getDataRate(const QString& rateName) const {
|
|||
return _globalPositionRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "localPosition") {
|
||||
return _localPositionRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "handControllers") {
|
||||
return _handControllersRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "avatarBoundingBox") {
|
||||
return _avatarBoundingBoxRate.rate() / BYTES_PER_KILOBIT;
|
||||
} else if (rateName == "avatarOrientation") {
|
||||
|
@ -1547,6 +1557,7 @@ float AvatarData::getDataRate(const QString& rateName) const {
|
|||
* <tbody>
|
||||
* <tr><td><code>"globalPosition"</code></td><td>Global position.</td></tr>
|
||||
* <tr><td><code>"localPosition"</code></td><td>Local position.</td></tr>
|
||||
* <tr><td><code>"handControllers"</code></td><td>Hand controller positions and orientations.</td></tr>
|
||||
* <tr><td><code>"avatarBoundingBox"</code></td><td>Avatar bounding box.</td></tr>
|
||||
* <tr><td><code>"avatarOrientation"</code></td><td>Avatar orientation.</td></tr>
|
||||
* <tr><td><code>"avatarScale"</code></td><td>Avatar scale.</td></tr>
|
||||
|
@ -1571,6 +1582,8 @@ float AvatarData::getUpdateRate(const QString& rateName) const {
|
|||
return _globalPositionUpdateRate.rate();
|
||||
} else if (rateName == "localPosition") {
|
||||
return _localPositionUpdateRate.rate();
|
||||
} else if (rateName == "handControllers") {
|
||||
return _handControllersUpdateRate.rate();
|
||||
} else if (rateName == "avatarBoundingBox") {
|
||||
return _avatarBoundingBoxUpdateRate.rate();
|
||||
} else if (rateName == "avatarOrientation") {
|
||||
|
@ -1633,6 +1646,13 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v
|
|||
data.translationIsDefaultPose = false;
|
||||
}
|
||||
|
||||
QVector<JointData> AvatarData::getJointData() const {
|
||||
QVector<JointData> jointData;
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
jointData = _jointData;
|
||||
return jointData;
|
||||
}
|
||||
|
||||
void AvatarData::clearJointData(int index) {
|
||||
if (index < 0 || index >= LOWEST_PSEUDO_JOINT_INDEX) {
|
||||
return;
|
||||
|
@ -1987,11 +2007,98 @@ QUrl AvatarData::getWireSafeSkeletonModelURL() const {
|
|||
return QUrl();
|
||||
}
|
||||
}
|
||||
QByteArray AvatarData::packSkeletonData() const {
|
||||
// Send an avatar trait packet with the skeleton data before the mesh is loaded
|
||||
int avatarDataSize = 0;
|
||||
QByteArray avatarDataByteArray;
|
||||
_avatarSkeletonDataLock.withReadLock([&] {
|
||||
// Add header
|
||||
AvatarSkeletonTrait::Header header;
|
||||
header.maxScaleDimension = 0.0f;
|
||||
header.maxTranslationDimension = 0.0f;
|
||||
header.numJoints = (uint8_t)_avatarSkeletonData.size();
|
||||
header.stringTableLength = 0;
|
||||
|
||||
for (size_t i = 0; i < _avatarSkeletonData.size(); i++) {
|
||||
header.stringTableLength += (uint16_t)_avatarSkeletonData[i].jointName.size();
|
||||
auto& translation = _avatarSkeletonData[i].defaultTranslation;
|
||||
header.maxTranslationDimension = std::max(header.maxTranslationDimension, std::max(std::max(translation.x, translation.y), translation.z));
|
||||
header.maxScaleDimension = std::max(header.maxScaleDimension, _avatarSkeletonData[i].defaultScale);
|
||||
}
|
||||
|
||||
const int byteArraySize = (int)sizeof(AvatarSkeletonTrait::Header) + (int)(header.numJoints * sizeof(AvatarSkeletonTrait::JointData)) + header.stringTableLength;
|
||||
avatarDataByteArray = QByteArray(byteArraySize, 0);
|
||||
unsigned char* destinationBuffer = reinterpret_cast<unsigned char*>(avatarDataByteArray.data());
|
||||
const unsigned char* const startPosition = destinationBuffer;
|
||||
|
||||
memcpy(destinationBuffer, &header, sizeof(header));
|
||||
destinationBuffer += sizeof(AvatarSkeletonTrait::Header);
|
||||
|
||||
QString stringTable = "";
|
||||
for (size_t i = 0; i < _avatarSkeletonData.size(); i++) {
|
||||
AvatarSkeletonTrait::JointData jdata;
|
||||
jdata.boneType = _avatarSkeletonData[i].boneType;
|
||||
jdata.parentIndex = _avatarSkeletonData[i].parentIndex;
|
||||
packFloatRatioToTwoByte((uint8_t*)(&jdata.defaultScale), _avatarSkeletonData[i].defaultScale / header.maxScaleDimension);
|
||||
packOrientationQuatToSixBytes(jdata.defaultRotation, _avatarSkeletonData[i].defaultRotation);
|
||||
packFloatVec3ToSignedTwoByteFixed(jdata.defaultTranslation, _avatarSkeletonData[i].defaultTranslation / header.maxTranslationDimension, TRANSLATION_COMPRESSION_RADIX);
|
||||
jdata.jointIndex = (uint16_t)i;
|
||||
jdata.stringStart = (uint16_t)_avatarSkeletonData[i].stringStart;
|
||||
jdata.stringLength = (uint8_t)_avatarSkeletonData[i].stringLength;
|
||||
stringTable += _avatarSkeletonData[i].jointName;
|
||||
memcpy(destinationBuffer, &jdata, sizeof(AvatarSkeletonTrait::JointData));
|
||||
destinationBuffer += sizeof(AvatarSkeletonTrait::JointData);
|
||||
}
|
||||
|
||||
memcpy(destinationBuffer, stringTable.toUtf8(), header.stringTableLength);
|
||||
destinationBuffer += header.stringTableLength;
|
||||
|
||||
avatarDataSize = destinationBuffer - startPosition;
|
||||
});
|
||||
return avatarDataByteArray.left(avatarDataSize);
|
||||
}
|
||||
|
||||
QByteArray AvatarData::packSkeletonModelURL() const {
|
||||
return getWireSafeSkeletonModelURL().toEncoded();
|
||||
}
|
||||
|
||||
void AvatarData::unpackSkeletonData(const QByteArray& data) {
|
||||
|
||||
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(data.data());
|
||||
const unsigned char* sourceBuffer = startPosition;
|
||||
|
||||
auto header = reinterpret_cast<const AvatarSkeletonTrait::Header*>(sourceBuffer);
|
||||
sourceBuffer += sizeof(const AvatarSkeletonTrait::Header);
|
||||
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> joints;
|
||||
for (uint8_t i = 0; i < header->numJoints; i++) {
|
||||
auto jointData = reinterpret_cast<const AvatarSkeletonTrait::JointData*>(sourceBuffer);
|
||||
sourceBuffer += sizeof(const AvatarSkeletonTrait::JointData);
|
||||
AvatarSkeletonTrait::UnpackedJointData uJointData;
|
||||
uJointData.boneType = (int)jointData->boneType;
|
||||
uJointData.jointIndex = (int)i;
|
||||
uJointData.stringLength = (int)jointData->stringLength;
|
||||
uJointData.stringStart = (int)jointData->stringStart;
|
||||
uJointData.parentIndex = ((uJointData.boneType == AvatarSkeletonTrait::BoneType::SkeletonRoot) ||
|
||||
(uJointData.boneType == AvatarSkeletonTrait::BoneType::NonSkeletonRoot)) ? -1 : (int)jointData->parentIndex;
|
||||
unpackOrientationQuatFromSixBytes(reinterpret_cast<const unsigned char*>(&jointData->defaultRotation), uJointData.defaultRotation);
|
||||
unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast<const unsigned char*>(&jointData->defaultTranslation), uJointData.defaultTranslation, TRANSLATION_COMPRESSION_RADIX);
|
||||
unpackFloatRatioFromTwoByte(reinterpret_cast<const unsigned char*>(&jointData->defaultScale), uJointData.defaultScale);
|
||||
uJointData.defaultTranslation *= header->maxTranslationDimension;
|
||||
uJointData.defaultScale *= header->maxScaleDimension;
|
||||
joints.push_back(uJointData);
|
||||
}
|
||||
QString table = QString::fromUtf8(reinterpret_cast<const char*>(sourceBuffer), (int)header->stringTableLength);
|
||||
for (size_t i = 0; i < joints.size(); i++) {
|
||||
QStringRef subString(&table, joints[i].stringStart, joints[i].stringLength);
|
||||
joints[i].jointName = subString.toString();
|
||||
}
|
||||
if (_clientTraitsHandler) {
|
||||
_clientTraitsHandler->markTraitUpdated(AvatarTraits::SkeletonData);
|
||||
}
|
||||
setSkeletonData(joints);
|
||||
}
|
||||
|
||||
void AvatarData::unpackSkeletonModelURL(const QByteArray& data) {
|
||||
auto skeletonModelURL = QUrl::fromEncoded(data);
|
||||
setSkeletonModelURL(skeletonModelURL);
|
||||
|
@ -2027,6 +2134,8 @@ QByteArray AvatarData::packTrait(AvatarTraits::TraitType traitType) const {
|
|||
// Call packer function
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
traitBinaryData = packSkeletonModelURL();
|
||||
} else if (traitType == AvatarTraits::SkeletonData) {
|
||||
traitBinaryData = packSkeletonData();
|
||||
}
|
||||
|
||||
return traitBinaryData;
|
||||
|
@ -2048,6 +2157,8 @@ QByteArray AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, Avat
|
|||
void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) {
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
unpackSkeletonModelURL(traitBinaryData);
|
||||
} else if (traitType == AvatarTraits::SkeletonData) {
|
||||
unpackSkeletonData(traitBinaryData);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2110,7 +2221,6 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
}
|
||||
|
||||
_skeletonModelURL = expanded;
|
||||
|
||||
if (_clientTraitsHandler) {
|
||||
_clientTraitsHandler->markTraitUpdated(AvatarTraits::SkeletonModelURL);
|
||||
}
|
||||
|
@ -3008,6 +3118,26 @@ AABox AvatarData::computeBubbleBox(float bubbleScale) const {
|
|||
return box;
|
||||
}
|
||||
|
||||
void AvatarData::setSkeletonData(const std::vector<AvatarSkeletonTrait::UnpackedJointData>& skeletonData) {
|
||||
_avatarSkeletonDataLock.withWriteLock([&] {
|
||||
_avatarSkeletonData = skeletonData;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> AvatarData::getSkeletonData() const {
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> skeletonData;
|
||||
_avatarSkeletonDataLock.withReadLock([&] {
|
||||
skeletonData = _avatarSkeletonData;
|
||||
});
|
||||
return skeletonData;
|
||||
}
|
||||
|
||||
void AvatarData::sendSkeletonData() const{
|
||||
if (_clientTraitsHandler) {
|
||||
_clientTraitsHandler->markTraitUpdated(AvatarTraits::SkeletonData);
|
||||
}
|
||||
}
|
||||
|
||||
AABox AvatarData::getDefaultBubbleBox() const {
|
||||
AABox bubbleBox(_defaultBubbleBox);
|
||||
bubbleBox.translate(_globalPosition);
|
||||
|
|
|
@ -145,6 +145,45 @@ const char AVATARDATA_FLAGS_MINIMUM = 0;
|
|||
|
||||
using SmallFloat = uint16_t; // a compressed float with less precision, user defined radix
|
||||
|
||||
namespace AvatarSkeletonTrait {
|
||||
enum BoneType {
|
||||
SkeletonRoot = 0,
|
||||
SkeletonChild,
|
||||
NonSkeletonRoot,
|
||||
NonSkeletonChild
|
||||
};
|
||||
|
||||
PACKED_BEGIN struct Header {
|
||||
float maxTranslationDimension;
|
||||
float maxScaleDimension;
|
||||
uint8_t numJoints;
|
||||
uint16_t stringTableLength;
|
||||
} PACKED_END;
|
||||
|
||||
PACKED_BEGIN struct JointData {
|
||||
uint16_t stringStart;
|
||||
uint8_t stringLength;
|
||||
uint8_t boneType;
|
||||
uint8_t defaultTranslation[6];
|
||||
uint8_t defaultRotation[6];
|
||||
uint16_t defaultScale;
|
||||
uint16_t jointIndex;
|
||||
uint16_t parentIndex;
|
||||
} PACKED_END;
|
||||
|
||||
struct UnpackedJointData {
|
||||
int stringStart;
|
||||
int stringLength;
|
||||
int boneType;
|
||||
glm::vec3 defaultTranslation;
|
||||
glm::quat defaultRotation;
|
||||
float defaultScale;
|
||||
int jointIndex;
|
||||
int parentIndex;
|
||||
QString jointName;
|
||||
};
|
||||
}
|
||||
|
||||
namespace AvatarDataPacket {
|
||||
|
||||
// NOTE: every time AvatarData is sent from mixer to client, it also includes the GUIID for the session
|
||||
|
@ -164,10 +203,11 @@ namespace AvatarDataPacket {
|
|||
const HasFlags PACKET_HAS_ADDITIONAL_FLAGS = 1U << 7;
|
||||
const HasFlags PACKET_HAS_PARENT_INFO = 1U << 8;
|
||||
const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9;
|
||||
const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10;
|
||||
const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11;
|
||||
const HasFlags PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS = 1U << 12;
|
||||
const HasFlags PACKET_HAS_GRAB_JOINTS = 1U << 13;
|
||||
const HasFlags PACKET_HAS_HAND_CONTROLLERS = 1U << 10;
|
||||
const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 11;
|
||||
const HasFlags PACKET_HAS_JOINT_DATA = 1U << 12;
|
||||
const HasFlags PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS = 1U << 13;
|
||||
const HasFlags PACKET_HAS_GRAB_JOINTS = 1U << 14;
|
||||
const size_t AVATAR_HAS_FLAGS_SIZE = 2;
|
||||
|
||||
using SixByteQuat = uint8_t[6];
|
||||
|
@ -230,7 +270,7 @@ namespace AvatarDataPacket {
|
|||
//
|
||||
// POTENTIAL SAVINGS - 20 bytes
|
||||
|
||||
SixByteQuat sensorToWorldQuat; // 6 byte compressed quaternion part of sensor to world matrix
|
||||
SixByteQuat sensorToWorldQuat; // 6 byte compressed quaternion part of sensor to world matrix
|
||||
uint16_t sensorToWorldScale; // uniform scale of sensor to world matrix
|
||||
float sensorToWorldTrans[3]; // fourth column of sensor to world matrix
|
||||
// FIXME - sensorToWorldTrans might be able to be better compressed if it was
|
||||
|
@ -258,6 +298,7 @@ namespace AvatarDataPacket {
|
|||
PACKED_BEGIN struct AvatarLocalPosition {
|
||||
float localPosition[3]; // parent frame translation of the avatar
|
||||
} PACKED_END;
|
||||
|
||||
const size_t AVATAR_LOCAL_POSITION_SIZE = 12;
|
||||
static_assert(sizeof(AvatarLocalPosition) == AVATAR_LOCAL_POSITION_SIZE, "AvatarDataPacket::AvatarLocalPosition size doesn't match.");
|
||||
|
||||
|
@ -273,6 +314,15 @@ namespace AvatarDataPacket {
|
|||
PARENT_INFO_SIZE +
|
||||
AVATAR_LOCAL_POSITION_SIZE;
|
||||
|
||||
PACKED_BEGIN struct HandControllers {
|
||||
SixByteQuat leftHandRotation;
|
||||
SixByteTrans leftHandTranslation;
|
||||
SixByteQuat rightHandRotation;
|
||||
SixByteTrans rightHandTranslation;
|
||||
} PACKED_END;
|
||||
static const size_t HAND_CONTROLLERS_SIZE = 24;
|
||||
static_assert(sizeof(HandControllers) == HAND_CONTROLLERS_SIZE, "AvatarDataPacket::HandControllers size doesn't match.");
|
||||
|
||||
|
||||
// variable length structure follows
|
||||
|
||||
|
@ -303,7 +353,7 @@ namespace AvatarDataPacket {
|
|||
SixByteTrans rightHandControllerTranslation;
|
||||
};
|
||||
*/
|
||||
size_t maxJointDataSize(size_t numJoints, bool hasGrabJoints);
|
||||
size_t maxJointDataSize(size_t numJoints);
|
||||
size_t minJointDataSize(size_t numJoints);
|
||||
|
||||
/*
|
||||
|
@ -327,7 +377,6 @@ namespace AvatarDataPacket {
|
|||
static_assert(sizeof(FarGrabJoints) == FAR_GRAB_JOINTS_SIZE, "AvatarDataPacket::FarGrabJoints size doesn't match.");
|
||||
|
||||
static const size_t MIN_BULK_PACKET_SIZE = NUM_BYTES_RFC4122_UUID + HEADER_SIZE;
|
||||
static const size_t FAUX_JOINTS_SIZE = 2 * (sizeof(SixByteQuat) + sizeof(SixByteTrans));
|
||||
|
||||
struct SendStatus {
|
||||
HasFlags itemFlags { 0 };
|
||||
|
@ -404,6 +453,7 @@ class AvatarDataRate {
|
|||
public:
|
||||
RateCounter<> globalPositionRate;
|
||||
RateCounter<> localPositionRate;
|
||||
RateCounter<> handControllersRate;
|
||||
RateCounter<> avatarBoundingBoxRate;
|
||||
RateCounter<> avatarOrientationRate;
|
||||
RateCounter<> avatarScaleRate;
|
||||
|
@ -467,8 +517,8 @@ class AvatarData : public QObject, public SpatiallyNestable {
|
|||
* @property {boolean} lookAtSnappingEnabled=true - <code>true</code> if the avatar's eyes snap to look at another avatar's
|
||||
* eyes when the other avatar is in the line of sight and also has <code>lookAtSnappingEnabled == true</code>.
|
||||
* @property {string} skeletonModelURL - The avatar's FST file.
|
||||
* @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.<br />
|
||||
* <strong>Deprecated:</strong> Use avatar entities instead.
|
||||
* @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed. Use avatar entities instead.</p>
|
||||
* @property {string[]} jointNames - The list of joints in the current avatar model. <em>Read-only.</em>
|
||||
* @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. <em>Read-only.</em>
|
||||
* @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the
|
||||
|
@ -1076,7 +1126,7 @@ public:
|
|||
* Gets information about the models currently attached to your avatar.
|
||||
* @function Avatar.getAttachmentsVariant
|
||||
* @returns {AttachmentData[]} Information about all models attached to your avatar.
|
||||
* @deprecated Use avatar entities instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use avatar entities instead.
|
||||
*/
|
||||
// FIXME: Can this name be improved? Can it be deprecated?
|
||||
Q_INVOKABLE virtual QVariantList getAttachmentsVariant() const;
|
||||
|
@ -1087,7 +1137,7 @@ public:
|
|||
* update your avatar's attachments per the changed data.
|
||||
* @function Avatar.setAttachmentsVariant
|
||||
* @param {AttachmentData[]} variant - The attachment data defining the models to have attached to your avatar.
|
||||
* @deprecated Use avatar entities instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use avatar entities instead.
|
||||
*/
|
||||
// FIXME: Can this name be improved? Can it be deprecated?
|
||||
Q_INVOKABLE virtual void setAttachmentsVariant(const QVariantList& variant);
|
||||
|
@ -1168,7 +1218,7 @@ public:
|
|||
* Gets information about the models currently attached to your avatar.
|
||||
* @function Avatar.getAttachmentData
|
||||
* @returns {AttachmentData[]} Information about all models attached to your avatar.
|
||||
* @deprecated Use avatar entities instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use avatar entities instead.
|
||||
* @example <caption>Report the URLs of all current attachments.</caption>
|
||||
* var attachments = MyAvatar.getaAttachmentData();
|
||||
* for (var i = 0; i < attachments.length; i++) {
|
||||
|
@ -1186,7 +1236,7 @@ public:
|
|||
* @function Avatar.setAttachmentData
|
||||
* @param {AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use
|
||||
* <code>null</code> to remove all attachments.
|
||||
* @deprecated Use avatar entities instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use avatar entities instead.
|
||||
* @example <caption>Remove a hat attachment if your avatar is wearing it.</caption>
|
||||
* var hatURL = "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx";
|
||||
* var attachments = MyAvatar.getAttachmentData();
|
||||
|
@ -1223,7 +1273,7 @@ public:
|
|||
* @param {boolean} [allowDuplicates=false] - If <code>true</code> then more than one copy of any particular model may be
|
||||
* attached to the same joint; if <code>false</code> then the same model cannot be attached to the same joint.
|
||||
* @param {boolean} [useSaved=true] - <em>Not used.</em>
|
||||
* @deprecated Use avatar entities instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use avatar entities instead.
|
||||
* @example <caption>Attach a cowboy hat to your avatar's head.</caption>
|
||||
* var attachment = {
|
||||
* modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx",
|
||||
|
@ -1254,7 +1304,7 @@ public:
|
|||
* @param {string} modelURL - The URL of the model to detach.
|
||||
* @param {string} [jointName=""] - The name of the joint to detach the model from. If <code>""</code>, then the most
|
||||
* recently attached model is removed from which ever joint it was attached to.
|
||||
* @deprecated Use avatar entities instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use avatar entities instead.
|
||||
*/
|
||||
Q_INVOKABLE virtual void detachOne(const QString& modelURL, const QString& jointName = QString());
|
||||
|
||||
|
@ -1264,7 +1314,7 @@ public:
|
|||
* @param {string} modelURL - The URL of the model to detach.
|
||||
* @param {string} [jointName=""] - The name of the joint to detach the model from. If <code>""</code>, then the model is
|
||||
* detached from all joints.
|
||||
* @deprecated Use avatar entities instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use avatar entities instead.
|
||||
*/
|
||||
Q_INVOKABLE virtual void detachAll(const QString& modelURL, const QString& jointName = QString());
|
||||
|
||||
|
@ -1420,6 +1470,10 @@ public:
|
|||
void setIsNewAvatar(bool isNewAvatar) { _isNewAvatar = isNewAvatar; }
|
||||
bool getIsNewAvatar() { return _isNewAvatar; }
|
||||
void setIsClientAvatar(bool isClientAvatar) { _isClientAvatar = isClientAvatar; }
|
||||
void setSkeletonData(const std::vector<AvatarSkeletonTrait::UnpackedJointData>& skeletonData);
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> getSkeletonData() const;
|
||||
void sendSkeletonData() const;
|
||||
QVector<JointData> getJointData() const;
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -1598,12 +1652,13 @@ protected:
|
|||
bool hasParent() const { return !getParentID().isNull(); }
|
||||
bool hasFaceTracker() const { return _headData ? _headData->_isFaceTrackerConnected : false; }
|
||||
|
||||
QByteArray packSkeletonData() const;
|
||||
QByteArray packSkeletonModelURL() const;
|
||||
QByteArray packAvatarEntityTraitInstance(AvatarTraits::TraitInstanceID traitInstanceID);
|
||||
QByteArray packGrabTraitInstance(AvatarTraits::TraitInstanceID traitInstanceID);
|
||||
|
||||
void unpackSkeletonModelURL(const QByteArray& data);
|
||||
|
||||
void unpackSkeletonData(const QByteArray& data);
|
||||
|
||||
// isReplicated will be true on downstream Avatar Mixers and their clients, but false on the upstream "master"
|
||||
// Audio Mixer that the replicated avatar is connected to.
|
||||
|
@ -1671,6 +1726,7 @@ protected:
|
|||
RateCounter<> _parseBufferRate;
|
||||
RateCounter<> _globalPositionRate;
|
||||
RateCounter<> _localPositionRate;
|
||||
RateCounter<> _handControllersRate;
|
||||
RateCounter<> _avatarBoundingBoxRate;
|
||||
RateCounter<> _avatarOrientationRate;
|
||||
RateCounter<> _avatarScaleRate;
|
||||
|
@ -1688,6 +1744,7 @@ protected:
|
|||
RateCounter<> _parseBufferUpdateRate;
|
||||
RateCounter<> _globalPositionUpdateRate;
|
||||
RateCounter<> _localPositionUpdateRate;
|
||||
RateCounter<> _handControllersUpdateRate;
|
||||
RateCounter<> _avatarBoundingBoxUpdateRate;
|
||||
RateCounter<> _avatarOrientationUpdateRate;
|
||||
RateCounter<> _avatarScaleUpdateRate;
|
||||
|
@ -1720,6 +1777,9 @@ protected:
|
|||
AvatarGrabDataMap _avatarGrabData;
|
||||
bool _avatarGrabDataChanged { false }; // by network
|
||||
|
||||
mutable ReadWriteLockable _avatarSkeletonDataLock;
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> _avatarSkeletonData;
|
||||
|
||||
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
|
||||
ThreadSafeValueCache<glm::mat4> _sensorToWorldMatrixCache { glm::mat4() };
|
||||
ThreadSafeValueCache<glm::mat4> _controllerLeftHandMatrixCache { glm::mat4() };
|
||||
|
|
|
@ -332,6 +332,12 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
|
|||
void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
AvatarTraits::TraitMessageSequence seq;
|
||||
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message->getBytesLeftToRead() < (qint64)sizeof(AvatarTraits::TraitMessageSequence)) {
|
||||
qWarning() << "Malformed bulk trait packet, bailling";
|
||||
return;
|
||||
}
|
||||
|
||||
message->readPrimitive(&seq);
|
||||
|
||||
auto traitsAckPacket = NLPacket::create(PacketType::BulkAvatarTraitsAck, sizeof(AvatarTraits::TraitMessageSequence), true);
|
||||
|
@ -344,7 +350,14 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
|
|||
nodeList->sendPacket(std::move(traitsAckPacket), *avatarMixer);
|
||||
}
|
||||
|
||||
while (message->getBytesLeftToRead()) {
|
||||
while (message->getBytesLeftToRead() > 0) {
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message->getBytesLeftToRead() < qint64(NUM_BYTES_RFC4122_UUID +
|
||||
sizeof(AvatarTraits::TraitType))) {
|
||||
qWarning() << "Malformed bulk trait packet, bailling";
|
||||
return;
|
||||
}
|
||||
|
||||
// read the avatar ID to figure out which avatar this is for
|
||||
auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
|
@ -360,6 +373,12 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
|
|||
auto& lastProcessedVersions = _processedTraitVersions[avatarID];
|
||||
|
||||
while (traitType != AvatarTraits::NullTrait && message->getBytesLeftToRead() > 0) {
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message->getBytesLeftToRead() < qint64(sizeof(AvatarTraits::TraitVersion))) {
|
||||
qWarning() << "Malformed bulk trait packet, bailling";
|
||||
return;
|
||||
}
|
||||
|
||||
AvatarTraits::TraitVersion packetTraitVersion;
|
||||
message->readPrimitive(&packetTraitVersion);
|
||||
|
||||
|
@ -367,8 +386,20 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
|
|||
bool skipBinaryTrait = false;
|
||||
|
||||
if (AvatarTraits::isSimpleTrait(traitType)) {
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message->getBytesLeftToRead() < qint64(sizeof(AvatarTraits::TraitWireSize))) {
|
||||
qWarning() << "Malformed bulk trait packet, bailling";
|
||||
return;
|
||||
}
|
||||
|
||||
message->readPrimitive(&traitBinarySize);
|
||||
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message->getBytesLeftToRead() < traitBinarySize) {
|
||||
qWarning() << "Malformed bulk trait packet, bailling";
|
||||
return;
|
||||
}
|
||||
|
||||
// check if this trait version is newer than what we already have for this avatar
|
||||
if (packetTraitVersion > lastProcessedVersions[traitType]) {
|
||||
auto traitData = message->read(traitBinarySize);
|
||||
|
@ -379,11 +410,24 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
|
|||
skipBinaryTrait = true;
|
||||
}
|
||||
} else {
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message->getBytesLeftToRead() < qint64(NUM_BYTES_RFC4122_UUID +
|
||||
sizeof(AvatarTraits::TraitWireSize))) {
|
||||
qWarning() << "Malformed bulk trait packet, bailling";
|
||||
return;
|
||||
}
|
||||
|
||||
AvatarTraits::TraitInstanceID traitInstanceID =
|
||||
QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
message->readPrimitive(&traitBinarySize);
|
||||
|
||||
// Trying to read more bytes than available, bail
|
||||
if (traitBinarySize < -1 || message->getBytesLeftToRead() < traitBinarySize) {
|
||||
qWarning() << "Malformed bulk trait packet, bailling";
|
||||
return;
|
||||
}
|
||||
|
||||
auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID);
|
||||
if (packetTraitVersion > processedInstanceVersion) {
|
||||
if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) {
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace AvatarTraits {
|
|||
|
||||
// Simple traits
|
||||
SkeletonModelURL = 0,
|
||||
|
||||
SkeletonData,
|
||||
// Instanced traits
|
||||
FirstInstancedTrait,
|
||||
AvatarEntity = FirstInstancedTrait,
|
||||
|
|
|
@ -107,8 +107,7 @@ int ClientTraitsHandler::sendChangedTraitsToMixer() {
|
|||
|
||||
if (initialSend || *simpleIt == Updated) {
|
||||
bytesWritten += AvatarTraits::packTrait(traitType, *traitsPacketList, *_owningAvatar);
|
||||
|
||||
|
||||
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
// keep track of our skeleton version in case we get an override back
|
||||
_currentSkeletonVersion = _currentTraitVersion;
|
||||
|
@ -147,7 +146,16 @@ int ClientTraitsHandler::sendChangedTraitsToMixer() {
|
|||
void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||
if (sendingNode->getType() == NodeType::AvatarMixer) {
|
||||
Lock lock(_traitLock);
|
||||
while (message->getBytesLeftToRead()) {
|
||||
|
||||
while (message->getBytesLeftToRead() > 0) {
|
||||
// Trying to read more bytes than available, bail
|
||||
if (message->getBytesLeftToRead() < qint64(sizeof(AvatarTraits::TraitType) +
|
||||
sizeof(AvatarTraits::TraitVersion) +
|
||||
sizeof(AvatarTraits::TraitWireSize))) {
|
||||
qWarning() << "Malformed trait override packet, bailling";
|
||||
return;
|
||||
}
|
||||
|
||||
AvatarTraits::TraitType traitType;
|
||||
message->readPrimitive(&traitType);
|
||||
|
||||
|
@ -157,6 +165,12 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer<ReceivedMessage> m
|
|||
AvatarTraits::TraitWireSize traitBinarySize;
|
||||
message->readPrimitive(&traitBinarySize);
|
||||
|
||||
// Trying to read more bytes than available, bail
|
||||
if (traitBinarySize < -1 || message->getBytesLeftToRead() < traitBinarySize) {
|
||||
qWarning() << "Malformed trait override packet, bailling";
|
||||
return;
|
||||
}
|
||||
|
||||
// only accept an override if this is for a trait type we override
|
||||
// and the version matches what we last sent for skeleton
|
||||
if (traitType == AvatarTraits::SkeletonModelURL
|
||||
|
|
|
@ -40,13 +40,14 @@
|
|||
* @property {string} displayName - The avatar's display name.
|
||||
* @property {string} sessionDisplayName - The avatar's display name, sanitized and versioned, as defined by the avatar mixer.
|
||||
* It is unique among all avatars present in the domain at the time.
|
||||
* @property {boolean} isReplicated - <strong>Deprecated.</strong>
|
||||
* @property {boolean} isReplicated - <span class="important">Deprecated: This property is deprecated and will be
|
||||
* removed.</span>
|
||||
* @property {boolean} lookAtSnappingEnabled - <code>true</code> if the avatar's eyes snap to look at another avatar's eyes
|
||||
* when the other avatar is in the line of sight and also has <code>lookAtSnappingEnabled == true</code>.
|
||||
*
|
||||
* @property {string} skeletonModelURL - The avatar's FST file.
|
||||
* @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.<br />
|
||||
* <strong>Deprecated:</strong> Use avatar entities instead.
|
||||
* @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.
|
||||
* <p class="important">Deprecated: This property is deprecated and will be removed. Use avatar entities instead.</p>
|
||||
* @property {string[]} jointNames - The list of joints in the current avatar model.
|
||||
*
|
||||
* @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include <FBXWriter.h>
|
||||
|
||||
#include "ModelBakingLoggingCategory.h"
|
||||
#include "TextureBaker.h"
|
||||
|
||||
FBXBaker::FBXBaker(const QUrl& inputModelURL, const QString& bakedOutputDirectory, const QString& originalOutputDirectory, bool hasBeenBaked) :
|
||||
ModelBaker(inputModelURL, bakedOutputDirectory, originalOutputDirectory, hasBeenBaked) {
|
||||
|
|
|
@ -18,15 +18,11 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include "Baker.h"
|
||||
#include "TextureBaker.h"
|
||||
#include "ModelBaker.h"
|
||||
#include "ModelBakingLoggingCategory.h"
|
||||
|
||||
#include <gpu/Texture.h>
|
||||
|
||||
#include <FBX.h>
|
||||
|
||||
using TextureBakerThreadGetter = std::function<QThread*()>;
|
||||
|
||||
class FBXBaker : public ModelBaker {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -133,12 +133,12 @@ void MaterialBaker::processMaterial() {
|
|||
QString extension = idx >= 0 ? cleanURL.mid(idx + 1).toLower() : "";
|
||||
|
||||
if (QImageReader::supportedImageFormats().contains(extension.toLatin1())) {
|
||||
QPair<QUrl, image::TextureUsage::Type> textureKey(textureURL, type);
|
||||
TextureKey textureKey(textureURL, type);
|
||||
if (!_textureBakers.contains(textureKey)) {
|
||||
auto baseTextureFileName = _textureFileNamer.createBaseTextureFileName(textureURL.fileName(), type);
|
||||
|
||||
QSharedPointer<TextureBaker> textureBaker {
|
||||
new TextureBaker(textureURL, type, _textureOutputDir, "", baseTextureFileName, content),
|
||||
new TextureBaker(textureURL, type, _textureOutputDir, baseTextureFileName, content),
|
||||
&TextureBaker::deleteLater
|
||||
};
|
||||
textureBaker->setMapChannel(mapChannel);
|
||||
|
@ -170,7 +170,7 @@ void MaterialBaker::handleFinishedTextureBaker() {
|
|||
auto baker = qobject_cast<TextureBaker*>(sender());
|
||||
|
||||
if (baker) {
|
||||
QPair<QUrl, image::TextureUsage::Type> textureKey = { baker->getTextureURL(), baker->getTextureType() };
|
||||
TextureKey textureKey = { baker->getTextureURL(), baker->getTextureType() };
|
||||
if (!baker->hasErrors()) {
|
||||
// this TextureBaker is done and everything went according to plan
|
||||
qCDebug(material_baking) << "Re-writing texture references to" << baker->getTextureURL();
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
static const QString BAKED_MATERIAL_EXTENSION = ".baked.json";
|
||||
|
||||
using TextureKey = QPair<QUrl, image::TextureUsage::Type>;
|
||||
|
||||
class MaterialBaker : public Baker {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -58,8 +60,8 @@ private:
|
|||
|
||||
NetworkMaterialResourcePointer _materialResource;
|
||||
|
||||
QHash<QPair<QUrl, image::TextureUsage::Type>, QSharedPointer<TextureBaker>> _textureBakers;
|
||||
QMultiHash<QPair<QUrl, image::TextureUsage::Type>, std::shared_ptr<NetworkMaterial>> _materialsNeedingRewrite;
|
||||
QHash<TextureKey, QSharedPointer<TextureBaker>> _textureBakers;
|
||||
QMultiHash<TextureKey, std::shared_ptr<NetworkMaterial>> _materialsNeedingRewrite;
|
||||
|
||||
QString _bakedOutputDir;
|
||||
QString _textureOutputDir;
|
||||
|
|
|
@ -13,13 +13,9 @@
|
|||
#define hifi_OBJBaker_h
|
||||
|
||||
#include "Baker.h"
|
||||
#include "TextureBaker.h"
|
||||
#include "ModelBaker.h"
|
||||
|
||||
#include "ModelBakingLoggingCategory.h"
|
||||
|
||||
using TextureBakerThreadGetter = std::function<QThread*()>;
|
||||
|
||||
using NodeID = qlonglong;
|
||||
|
||||
class OBJBaker : public ModelBaker {
|
||||
|
|
|
@ -33,14 +33,13 @@ const QString BAKED_META_TEXTURE_SUFFIX = ".texmeta.json";
|
|||
bool TextureBaker::_compressionEnabled = true;
|
||||
|
||||
TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType,
|
||||
const QDir& outputDirectory, const QString& metaTexturePathPrefix,
|
||||
const QString& baseFilename, const QByteArray& textureContent) :
|
||||
const QDir& outputDirectory, const QString& baseFilename,
|
||||
const QByteArray& textureContent) :
|
||||
_textureURL(textureURL),
|
||||
_originalTexture(textureContent),
|
||||
_textureType(textureType),
|
||||
_baseFilename(baseFilename),
|
||||
_outputDirectory(outputDirectory),
|
||||
_metaTexturePathPrefix(metaTexturePathPrefix)
|
||||
_outputDirectory(outputDirectory)
|
||||
{
|
||||
if (baseFilename.isEmpty()) {
|
||||
// figure out the baked texture filename
|
||||
|
@ -151,7 +150,7 @@ void TextureBaker::processTexture() {
|
|||
// IMPORTANT: _originalTexture is empty past this point
|
||||
_originalTexture.clear();
|
||||
_outputFiles.push_back(originalCopyFilePath);
|
||||
meta.original = _metaTexturePathPrefix + _originalCopyFilePath.fileName();
|
||||
meta.original = _originalCopyFilePath.fileName();
|
||||
}
|
||||
|
||||
// Load the copy of the original file from the baked output directory. New images will be created using the original as the source data.
|
||||
|
@ -204,7 +203,7 @@ void TextureBaker::processTexture() {
|
|||
return;
|
||||
}
|
||||
_outputFiles.push_back(filePath);
|
||||
meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName;
|
||||
meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = fileName;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,7 +239,7 @@ void TextureBaker::processTexture() {
|
|||
return;
|
||||
}
|
||||
_outputFiles.push_back(filePath);
|
||||
meta.uncompressed = _metaTexturePathPrefix + fileName;
|
||||
meta.uncompressed = fileName;
|
||||
} else {
|
||||
buffer.reset();
|
||||
}
|
||||
|
|
|
@ -32,8 +32,8 @@ class TextureBaker : public Baker {
|
|||
|
||||
public:
|
||||
TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType,
|
||||
const QDir& outputDirectory, const QString& metaTexturePathPrefix = "",
|
||||
const QString& baseFilename = QString(), const QByteArray& textureContent = QByteArray());
|
||||
const QDir& outputDirectory, const QString& baseFilename = QString(),
|
||||
const QByteArray& textureContent = QByteArray());
|
||||
|
||||
const QByteArray& getOriginalTexture() const { return _originalTexture; }
|
||||
|
||||
|
@ -74,7 +74,6 @@ private:
|
|||
QString _baseFilename;
|
||||
QDir _outputDirectory;
|
||||
QString _metaTextureFileName;
|
||||
QString _metaTexturePathPrefix;
|
||||
QUrl _originalCopyFilePath;
|
||||
|
||||
std::atomic<bool> _abortProcessing { false };
|
||||
|
|
|
@ -201,15 +201,16 @@ namespace controller {
|
|||
* <tr><td><code>UiNavSelect</code></td><td>number</td><td>number</td><td>Generate a keyboard Enter key event.
|
||||
* </td></tr>
|
||||
* <tr><td><code>UiNavBack</code></td><td>number</td><td>number</td><td>Generate a keyboard Esc key event.</td></tr>
|
||||
* <tr><td><code>LeftHandClick</code></td><td>number</td><td>number</td><td><strong>Deprecated: </strong> No action.
|
||||
* </td></tr>
|
||||
* <tr><td><code>RightHandClick</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> No action.
|
||||
* </td></tr>
|
||||
* <tr><td><code>Shift</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> No action.</td></tr>
|
||||
* <tr><td><code>PrimaryAction</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> No action.
|
||||
* </td></tr>
|
||||
* <tr><td><code>SecondaryAction</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> No action.
|
||||
* </td></tr>
|
||||
* <tr><td><code>LeftHandClick</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>RightHandClick</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>Shift</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>PrimaryAction</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>SecondaryAction</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Aliases</strong></td>
|
||||
* <tr><td><code>Backward</code></td><td>number</td><td>number</td><td>Alias for <code>TranslateZ</code> in the
|
||||
|
@ -234,86 +235,108 @@ namespace controller {
|
|||
* direction.</td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Deprecated Aliases</strong></td>
|
||||
* <tr><td><code>LEFT_HAND</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>LeftHand</code> instead.</td></tr>
|
||||
* <tr><td><code>RIGHT_HAND</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>RightHand</code> instead.</td></tr>
|
||||
* <tr><td><code>BOOM_IN</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>BoomIn</code> instead.</td></tr>
|
||||
* <tr><td><code>BOOM_OUT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>BoomOut</code> instead.</td></tr>
|
||||
* <tr><td><code>CONTEXT_MENU</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>ContextMenu</code> instead.</td></tr>
|
||||
* <tr><td><code>TOGGLE_MUTE</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>ToggleMute</code> instead.</td></tr>
|
||||
* <tr><td><code>TOGGLE_PUSHTOTALK</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>TogglePushToTalk</code> instead.</td></tr>
|
||||
* <tr><td><code>SPRINT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Sprint</code> instead.</td></tr>
|
||||
* <tr><td><code>LONGITUDINAL_BACKWARD</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Backward</code> instead.</td></tr>
|
||||
* <tr><td><code>LONGITUDINAL_FORWARD</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Forward</code> instead.</td></tr>
|
||||
* <tr><td><code>LATERAL_LEFT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>StrafeLeft</code> instead.</td></tr>
|
||||
* <tr><td><code>LATERAL_RIGHT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>StrafeRight</code> instead.</td></tr>
|
||||
* <tr><td><code>VERTICAL_UP</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Up</code> instead.</td></tr>
|
||||
* <tr><td><code>VERTICAL_DOWN</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Down</code> instead.</td></tr>
|
||||
* <tr><td><code>PITCH_DOWN</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>PitchDown</code> instead.</td></tr>
|
||||
* <tr><td><code>PITCH_UP</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>PitchUp</code> instead.</td></tr>
|
||||
* <tr><td><code>YAW_LEFT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>YawLeft</code> instead.</td></tr>
|
||||
* <tr><td><code>YAW_RIGHT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>YawRight</code> instead.</td></tr>
|
||||
* <tr><td><code>LEFT_HAND_CLICK</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>LeftHandClick</code> instead.</td></tr>
|
||||
* <tr><td><code>RIGHT_HAND_CLICK</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>RightHandClick</code> instead.</td></tr>
|
||||
* <tr><td><code>SHIFT</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>Shift</code> instead.</td></tr>
|
||||
* <tr><td><code>ACTION1</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>PrimaryAction</code> instead.</td></tr>
|
||||
* <tr><td><code>ACTION2</code></td><td>number</td><td>number</td><td><strong>Deprecated:</strong> Use
|
||||
* <code>SecondaryAction</code> instead.</td></tr>
|
||||
* <tr><td><code>LEFT_HAND</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use <code>LeftHand</code> instead.</span></td></tr>
|
||||
* <tr><td><code>RIGHT_HAND</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>RightHand</code> instead.</span></td></tr>
|
||||
* <tr><td><code>BOOM_IN</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>BoomIn</code> instead.</span></td></tr>
|
||||
* <tr><td><code>BOOM_OUT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>BoomOut</code> instead.</span></td></tr>
|
||||
* <tr><td><code>CONTEXT_MENU</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>ContextMenu</code> instead.</span></td></tr>
|
||||
* <tr><td><code>TOGGLE_MUTE</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>ToggleMute</code> instead.</span></td></tr>
|
||||
* <tr><td><code>TOGGLE_PUSHTOTALK</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>TogglePushToTalk</code> instead.</span></td></tr>
|
||||
* <tr><td><code>SPRINT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Sprint</code> instead.</span></td></tr>
|
||||
* <tr><td><code>LONGITUDINAL_BACKWARD</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Backward</code> instead.</span></td></tr>
|
||||
* <tr><td><code>LONGITUDINAL_FORWARD</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Forward</code> instead.</span></td></tr>
|
||||
* <tr><td><code>LATERAL_LEFT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>StrafeLeft</code> instead.</span></td></tr>
|
||||
* <tr><td><code>LATERAL_RIGHT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>StrafeRight</code> instead.</span></td></tr>
|
||||
* <tr><td><code>VERTICAL_UP</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Up</code> instead.</span></td></tr>
|
||||
* <tr><td><code>VERTICAL_DOWN</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Down</code> instead.</span></td></tr>
|
||||
* <tr><td><code>PITCH_DOWN</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>PitchDown</code> instead.</span></td></tr>
|
||||
* <tr><td><code>PITCH_UP</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>PitchUp</code> instead.</span></td></tr>
|
||||
* <tr><td><code>YAW_LEFT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>YawLeft</code> instead.</span></td></tr>
|
||||
* <tr><td><code>YAW_RIGHT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>YawRight</code> instead.</span></td></tr>
|
||||
* <tr><td><code>LEFT_HAND_CLICK</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>LeftHandClick</code> instead.</span></td></tr>
|
||||
* <tr><td><code>RIGHT_HAND_CLICK</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>RightHandClick</code> instead.</span></td></tr>
|
||||
* <tr><td><code>SHIFT</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>Shift</code> instead.</span></td></tr>
|
||||
* <tr><td><code>ACTION1</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>PrimaryAction</code> instead.</span></td></tr>
|
||||
* <tr><td><code>ACTION2</code></td><td>number</td><td>number</td><td><span class="important">Deprecated: This
|
||||
* action is deprecated and will be removed. Use
|
||||
* <code>SecondaryAction</code> instead.</span></td></tr>
|
||||
*
|
||||
* <tr><td colSpan=4><strong>Deprecated Trackers</strong></td>
|
||||
* <tr><td><code>TrackedObject00</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject01</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject02</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject03</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject04</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject05</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject06</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject07</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject08</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject09</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject10</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject11</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject12</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject13</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject14</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject15</code></td><td>number</td><td>{@link Pose}</td><td><strong>Deprecated: </strong> No
|
||||
* action.</td></tr>
|
||||
* <tr><td><code>TrackedObject00</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject01</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject02</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject03</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject04</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject05</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject06</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject07</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject08</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject09</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject10</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject11</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject12</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject13</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject14</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* <tr><td><code>TrackedObject15</code></td><td>number</td><td>{@link Pose}</td><td><span class="important">Deprecated:
|
||||
* This action is deprecated and will be removed. It takes no action.</span></td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {object} Controller.Actions
|
||||
|
|
|
@ -76,7 +76,7 @@ namespace controller {
|
|||
* Get a list of all available actions.
|
||||
* @function Controller.getAllActions
|
||||
* @returns {Action[]} All available actions.
|
||||
* @deprecated This function no longer works.
|
||||
* @deprecated This function is deprecated and will be removed. It no longer works.
|
||||
*/
|
||||
// FIXME: This function causes a JavaScript crash: https://highfidelity.manuscript.com/f/cases/edit/13921
|
||||
Q_INVOKABLE QVector<Action> getAllActions();
|
||||
|
@ -86,7 +86,7 @@ namespace controller {
|
|||
* @function Controller.getAvailableInputs
|
||||
* @param {number} deviceID - Integer ID of the hardware device.
|
||||
* @returns {NamedPair[]} All available inputs for the device.
|
||||
* @deprecated This function no longer works.
|
||||
* @deprecated This function is deprecated and will be removed. It no longer works.
|
||||
*/
|
||||
// FIXME: This function causes a JavaScript crash: https://highfidelity.manuscript.com/f/cases/edit/13922
|
||||
Q_INVOKABLE QVector<Input::NamedPair> getAvailableInputs(unsigned int device);
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
#include <TextureCache.h>
|
||||
#include "CompositorHelper.h"
|
||||
#include "Logging.h"
|
||||
|
||||
#include "RefreshRateController.h"
|
||||
extern QThread* RENDER_THREAD;
|
||||
|
||||
class PresentThread : public QThread, public Dependency {
|
||||
|
@ -60,12 +60,16 @@ public:
|
|||
shutdown();
|
||||
});
|
||||
setObjectName("Present");
|
||||
|
||||
_refreshRateController = std::make_shared<RefreshRateController>();
|
||||
}
|
||||
|
||||
~PresentThread() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
auto getRefreshRateController() { return _refreshRateController; }
|
||||
|
||||
void shutdown() {
|
||||
if (isRunning()) {
|
||||
// First ensure that we turn off any current display plugin
|
||||
|
@ -178,14 +182,22 @@ public:
|
|||
continue;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
_context->makeCurrent();
|
||||
#endif
|
||||
// Execute the frame and present it to the display device.
|
||||
{
|
||||
PROFILE_RANGE(render, "PluginPresent")
|
||||
gl::globalLock();
|
||||
currentPlugin->present();
|
||||
currentPlugin->present(_refreshRateController);
|
||||
gl::globalRelease(false);
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
#if defined(Q_OS_MAC)
|
||||
_context->doneCurrent();
|
||||
#endif
|
||||
|
||||
_refreshRateController->sleepThreadIfNeeded(this, currentPlugin->isHmd());
|
||||
}
|
||||
|
||||
_context->doneCurrent();
|
||||
|
@ -234,6 +246,7 @@ private:
|
|||
bool _finishedOtherThreadOperation { false };
|
||||
std::queue<OpenGLDisplayPlugin*> _newPluginQueue;
|
||||
gl::Context* _context { nullptr };
|
||||
std::shared_ptr<RefreshRateController> _refreshRateController { nullptr };
|
||||
};
|
||||
|
||||
bool OpenGLDisplayPlugin::activate() {
|
||||
|
@ -687,11 +700,11 @@ void OpenGLDisplayPlugin::internalPresent() {
|
|||
_presentRate.increment();
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::present() {
|
||||
void OpenGLDisplayPlugin::present(const std::shared_ptr<RefreshRateController>& refreshRateController) {
|
||||
auto frameId = (uint64_t)presentCount();
|
||||
PROFILE_RANGE_EX(render, __FUNCTION__, 0xffffff00, frameId)
|
||||
uint64_t startPresent = usecTimestampNow();
|
||||
|
||||
refreshRateController->clockStartTime();
|
||||
{
|
||||
PROFILE_RANGE_EX(render, "updateFrameData", 0xff00ff00, frameId)
|
||||
updateFrameData();
|
||||
|
@ -735,6 +748,7 @@ void OpenGLDisplayPlugin::present() {
|
|||
}
|
||||
|
||||
// Take the composite framebuffer and send it to the output device
|
||||
refreshRateController->clockEndTime();
|
||||
{
|
||||
PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId)
|
||||
internalPresent();
|
||||
|
@ -742,7 +756,10 @@ void OpenGLDisplayPlugin::present() {
|
|||
|
||||
gpu::Backend::freeGPUMemSize.set(gpu::gl::getFreeDedicatedMemory());
|
||||
} else if (alwaysPresent()) {
|
||||
refreshRateController->clockEndTime();
|
||||
internalPresent();
|
||||
} else {
|
||||
refreshRateController->clockEndTime();
|
||||
}
|
||||
_movingAveragePresent.addSample((float)(usecTimestampNow() - startPresent));
|
||||
}
|
||||
|
@ -759,6 +776,13 @@ float OpenGLDisplayPlugin::presentRate() const {
|
|||
return _presentRate.rate();
|
||||
}
|
||||
|
||||
std::function<void(int)> OpenGLDisplayPlugin::getRefreshRateOperator() {
|
||||
return [](int targetRefreshRate) {
|
||||
auto refreshRateController = DependencyManager::get<PresentThread>()->getRefreshRateController();
|
||||
refreshRateController->setRefreshRateLimitPeriod(targetRefreshRate);
|
||||
};
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::resetPresentRate() {
|
||||
// FIXME
|
||||
// _presentRate = RateCounter<100>();
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace gpu {
|
|||
}
|
||||
}
|
||||
|
||||
class RefreshRateController;
|
||||
|
||||
class OpenGLDisplayPlugin : public DisplayPlugin {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float hudAlpha MEMBER _hudAlpha)
|
||||
|
@ -41,6 +43,9 @@ public:
|
|||
~OpenGLDisplayPlugin();
|
||||
// These must be final to ensure proper ordering of operations
|
||||
// between the main thread and the presentation thread
|
||||
|
||||
static std::function<void(int)> getRefreshRateOperator();
|
||||
|
||||
bool activate() override final;
|
||||
void deactivate() override final;
|
||||
bool startStandBySession() override final;
|
||||
|
@ -123,7 +128,7 @@ protected:
|
|||
|
||||
void withOtherThreadContext(std::function<void()> f) const;
|
||||
|
||||
void present();
|
||||
void present(const std::shared_ptr<RefreshRateController>& refreshRateController);
|
||||
virtual void swapBuffers();
|
||||
ivec4 eyeViewport(Eye eye) const;
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// RefreshRateController.cpp
|
||||
// libraries/display-pluging/src/display-plugin
|
||||
//
|
||||
// Created by Dante Ruiz on 2019-04-15.
|
||||
// Copyright 2019 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 "RefreshRateController.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
long int hzToDurationNanoseconds(int refreshRate) {
|
||||
return (int64_t) (NSECS_PER_SECOND / (quint64) refreshRate);
|
||||
}
|
||||
|
||||
int durationNanosecondsToHz(int64_t refreshRateLimitPeriod) {
|
||||
return (int) (NSECS_PER_SECOND / (quint64) refreshRateLimitPeriod);
|
||||
}
|
||||
|
||||
void RefreshRateController::setRefreshRateLimitPeriod(int refreshRateLimit) {
|
||||
_refreshRateLimitPeriod = hzToDurationNanoseconds(refreshRateLimit);
|
||||
}
|
||||
|
||||
int RefreshRateController::getRefreshRateLimitPeriod() const {
|
||||
return durationNanosecondsToHz(_refreshRateLimitPeriod);
|
||||
}
|
||||
|
||||
void RefreshRateController::sleepThreadIfNeeded(QThread* thread, bool isHmd) {
|
||||
if (!isHmd) {
|
||||
static const std::chrono::nanoseconds EPSILON = std::chrono::milliseconds(1);
|
||||
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(_endTime - _startTime);
|
||||
auto refreshRateLimitPeriod = std::chrono::nanoseconds(_refreshRateLimitPeriod);
|
||||
auto sleepDuration = refreshRateLimitPeriod - (duration + EPSILON);
|
||||
if (sleepDuration.count() > 0) {
|
||||
thread->msleep(std::chrono::duration_cast<std::chrono::milliseconds>(sleepDuration).count());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// RefreshRateController.h
|
||||
// libraries/display-pluging/src/display-plugin
|
||||
//
|
||||
// Created by Dante Ruiz on 2019-04-15.
|
||||
// Copyright 2019 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_RefreshRateController_h
|
||||
#define hifi_RefreshRateController_h
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
|
||||
class QThread;
|
||||
|
||||
class RefreshRateController {
|
||||
public:
|
||||
RefreshRateController() = default;
|
||||
~RefreshRateController() = default;
|
||||
|
||||
void setRefreshRateLimitPeriod(int refreshRateLimit);
|
||||
int getRefreshRateLimitPeriod() const;
|
||||
|
||||
void clockStartTime() { _startTime = std::chrono::high_resolution_clock::now(); }
|
||||
void clockEndTime() { _endTime = std::chrono::high_resolution_clock::now(); }
|
||||
void sleepThreadIfNeeded(QThread* thread, bool isHmd);
|
||||
private:
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> _startTime { std::chrono::high_resolution_clock::now() };
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> _endTime { std::chrono::high_resolution_clock::now() };
|
||||
std::atomic<int64_t> _refreshRateLimitPeriod { 50 };
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1550,7 +1550,7 @@ public slots:
|
|||
* @function Entities.getMeshes
|
||||
* @param {Uuid} entityID - The ID of the <code>Model</code> or <code>PolyVox</code> entity to get the meshes of.
|
||||
* @param {Entities~getMeshesCallback} callback - The function to call upon completion.
|
||||
* @deprecated Use the {@link Graphics} API instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use the {@link Graphics} API instead.
|
||||
*/
|
||||
/**jsdoc
|
||||
* Called when {@link Entities.getMeshes} is complete.
|
||||
|
@ -1559,7 +1559,7 @@ public slots:
|
|||
* <code>Model</code> or <code>PolyVox</code> entity; otherwise <code>undefined</code>.
|
||||
* @param {boolean} success - <code>true</code> if the {@link Entities.getMeshes} call was successful, <code>false</code>
|
||||
* otherwise. The call may be unsuccessful if the requested entity could not be found.
|
||||
* @deprecated Use the {@link Graphics} API instead.
|
||||
* @deprecated This function is deprecated and will be removed. Use the {@link Graphics} API instead.
|
||||
*/
|
||||
// FIXME move to a renderable entity interface
|
||||
Q_INVOKABLE void getMeshes(const QUuid& entityID, QScriptValue callback);
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <ResourceManager.h>
|
||||
#include <PathUtils.h>
|
||||
#include <image/ColorChannel.h>
|
||||
#include <FaceshiftConstants.h>
|
||||
|
||||
#include "FBXSerializer.h"
|
||||
|
||||
|
@ -483,7 +484,7 @@ bool GLTFSerializer::addMesh(const QJsonObject& object) {
|
|||
GLTFMeshPrimitiveAttr target;
|
||||
foreach(const QString & tarKey, tarKeys) {
|
||||
int tarVal;
|
||||
getIntVal(jsAttributes, tarKey, tarVal, target.defined);
|
||||
getIntVal(jsTarget, tarKey, tarVal, target.defined);
|
||||
target.values.insert(tarKey, tarVal);
|
||||
}
|
||||
primitive.targets.push_back(target);
|
||||
|
@ -493,7 +494,18 @@ bool GLTFSerializer::addMesh(const QJsonObject& object) {
|
|||
mesh.primitives.push_back(primitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject jsExtras;
|
||||
GLTFMeshExtra extras;
|
||||
if (getObjectVal(object, "extras", jsExtras, mesh.defined)) {
|
||||
QJsonArray jsTargetNames;
|
||||
if (getObjectArrayVal(jsExtras, "targetNames", jsTargetNames, extras.defined)) {
|
||||
foreach (const QJsonValue& tarName, jsTargetNames) {
|
||||
extras.targetNames.push_back(tarName.toString());
|
||||
}
|
||||
}
|
||||
mesh.extras = extras;
|
||||
}
|
||||
|
||||
_file.meshes.push_back(mesh);
|
||||
|
@ -751,7 +763,24 @@ void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>&
|
|||
}
|
||||
}
|
||||
|
||||
bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
|
||||
void GLTFSerializer::generateTargetData(int index, float weight, QVector<glm::vec3>& returnVector) {
|
||||
GLTFAccessor& accessor = _file.accessors[index];
|
||||
GLTFBufferView& bufferview = _file.bufferviews[accessor.bufferView];
|
||||
GLTFBuffer& buffer = _file.buffers[bufferview.buffer];
|
||||
int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0;
|
||||
QVector<float> storedValues;
|
||||
addArrayOfType(buffer.blob,
|
||||
bufferview.byteOffset + accBoffset,
|
||||
accessor.count,
|
||||
storedValues,
|
||||
accessor.type,
|
||||
accessor.componentType);
|
||||
for (int n = 0; n < storedValues.size(); n = n + 3) {
|
||||
returnVector.push_back(glm::vec3(weight * storedValues[n], weight * storedValues[n + 1], weight * storedValues[n + 2]));
|
||||
}
|
||||
}
|
||||
|
||||
bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url) {
|
||||
int numNodes = _file.nodes.size();
|
||||
|
||||
// Build dependencies
|
||||
|
@ -1138,6 +1167,82 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
|
|||
if (mesh.texCoords.size() == 0 && !hfmModel.hasSkeletonJoints) {
|
||||
for (int i = 0; i < part.triangleIndices.size(); i++) { mesh.texCoords.push_back(glm::vec2(0.0, 1.0)); }
|
||||
}
|
||||
|
||||
// Build morph targets (blend shapes)
|
||||
if (!primitive.targets.isEmpty()) {
|
||||
|
||||
// Build list of blendshapes from FST
|
||||
typedef QPair<int, float> WeightedIndex;
|
||||
hifi::VariantHash blendshapeMappings = mapping.value("bs").toHash();
|
||||
QMultiHash<QString, WeightedIndex> blendshapeIndices;
|
||||
|
||||
for (int i = 0;; i++) {
|
||||
hifi::ByteArray blendshapeName = FACESHIFT_BLENDSHAPES[i];
|
||||
if (blendshapeName.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
QList<QVariant> mappings = blendshapeMappings.values(blendshapeName);
|
||||
foreach (const QVariant& mapping, mappings) {
|
||||
QVariantList blendshapeMapping = mapping.toList();
|
||||
blendshapeIndices.insert(blendshapeMapping.at(0).toByteArray(), WeightedIndex(i, blendshapeMapping.at(1).toFloat()));
|
||||
}
|
||||
}
|
||||
|
||||
// glTF morph targets may or may not have names. if they are labeled, add them based on
|
||||
// the corresponding names from the FST. otherwise, just add them in the order they are given
|
||||
mesh.blendshapes.resize(blendshapeMappings.size());
|
||||
auto values = blendshapeIndices.values();
|
||||
auto keys = blendshapeIndices.keys();
|
||||
auto names = _file.meshes[node.mesh].extras.targetNames;
|
||||
QVector<double> weights = _file.meshes[node.mesh].weights;
|
||||
|
||||
for (int weightedIndex = 0; weightedIndex < values.size(); weightedIndex++) {
|
||||
float weight = 0.1f;
|
||||
int indexFromMapping = weightedIndex;
|
||||
int targetIndex = weightedIndex;
|
||||
hfmModel.blendshapeChannelNames.push_back("target_" + QString::number(weightedIndex));
|
||||
|
||||
if (!names.isEmpty()) {
|
||||
targetIndex = names.indexOf(keys[weightedIndex]);
|
||||
indexFromMapping = values[weightedIndex].first;
|
||||
weight = weight * values[weightedIndex].second;
|
||||
hfmModel.blendshapeChannelNames[weightedIndex] = keys[weightedIndex];
|
||||
}
|
||||
HFMBlendshape& blendshape = mesh.blendshapes[indexFromMapping];
|
||||
blendshape.indices = part.triangleIndices;
|
||||
auto target = primitive.targets[targetIndex];
|
||||
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<glm::vec3> vertices;
|
||||
|
||||
if (weights.size() == primitive.targets.size()) {
|
||||
int targetWeight = weights[targetIndex];
|
||||
if (targetWeight != 0) {
|
||||
weight = weight * targetWeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (target.values.contains((QString) "NORMAL")) {
|
||||
generateTargetData(target.values.value((QString) "NORMAL"), weight, normals);
|
||||
}
|
||||
if (target.values.contains((QString) "POSITION")) {
|
||||
generateTargetData(target.values.value((QString) "POSITION"), weight, vertices);
|
||||
}
|
||||
bool isNewBlendshape = blendshape.vertices.size() < vertices.size();
|
||||
int count = 0;
|
||||
for (int i : blendshape.indices) {
|
||||
if (isNewBlendshape) {
|
||||
blendshape.vertices.push_back(vertices[i]);
|
||||
blendshape.normals.push_back(normals[i]);
|
||||
} else {
|
||||
blendshape.vertices[count] = blendshape.vertices[count] + vertices[i];
|
||||
blendshape.normals[count] = blendshape.normals[count] + normals[i];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh.meshExtents.reset();
|
||||
foreach(const glm::vec3& vertex, mesh.vertices) {
|
||||
mesh.meshExtents.addPoint(vertex);
|
||||
|
@ -1183,7 +1288,7 @@ HFMModel::Pointer GLTFSerializer::read(const hifi::ByteArray& data, const hifi::
|
|||
//_file.dump();
|
||||
auto hfmModelPtr = std::make_shared<HFMModel>();
|
||||
HFMModel& hfmModel = *hfmModelPtr;
|
||||
buildGeometry(hfmModel, _url);
|
||||
buildGeometry(hfmModel, mapping, _url);
|
||||
|
||||
//hfmDebugDump(data);
|
||||
return hfmModelPtr;
|
||||
|
|
|
@ -159,9 +159,20 @@ struct GLTFMeshPrimitive {
|
|||
}
|
||||
};
|
||||
|
||||
struct GLTFMeshExtra {
|
||||
QVector<QString> targetNames;
|
||||
QMap<QString, bool> defined;
|
||||
void dump() {
|
||||
if (defined["targetNames"]) {
|
||||
qCDebug(modelformat) << "targetNames: " << targetNames;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct GLTFMesh {
|
||||
QString name;
|
||||
QVector<GLTFMeshPrimitive> primitives;
|
||||
GLTFMeshExtra extras;
|
||||
QVector<double> weights;
|
||||
QMap<QString, bool> defined;
|
||||
void dump() {
|
||||
|
@ -172,6 +183,10 @@ struct GLTFMesh {
|
|||
qCDebug(modelformat) << "primitives: ";
|
||||
foreach(auto prim, primitives) prim.dump();
|
||||
}
|
||||
if (defined["extras"]) {
|
||||
qCDebug(modelformat) << "extras: ";
|
||||
extras.dump();
|
||||
}
|
||||
if (defined["weights"]) {
|
||||
qCDebug(modelformat) << "weights: " << weights;
|
||||
}
|
||||
|
@ -713,8 +728,9 @@ private:
|
|||
|
||||
glm::mat4 getModelTransform(const GLTFNode& node);
|
||||
void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues);
|
||||
void generateTargetData(int index, float weight, QVector<glm::vec3>& returnVector);
|
||||
|
||||
bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
|
||||
bool buildGeometry(HFMModel& hfmModel, const hifi::VariantHash& mapping, const hifi::URL& url);
|
||||
bool parseGLTF(const hifi::ByteArray& data);
|
||||
|
||||
bool getStringVal(const QJsonObject& object, const QString& fieldname,
|
||||
|
|
|
@ -37,7 +37,6 @@ ContextMetricCount Texture::_textureCPUCount;
|
|||
ContextMetricSize Texture::_textureCPUMemSize;
|
||||
std::atomic<Texture::Size> Texture::_allowedCPUMemoryUsage { 0 };
|
||||
|
||||
|
||||
#define MIN_CORES_FOR_INCREMENTAL_TEXTURES 5
|
||||
bool recommendedSparseTextures = (QThread::idealThreadCount() >= MIN_CORES_FOR_INCREMENTAL_TEXTURES);
|
||||
|
||||
|
|
|
@ -550,7 +550,7 @@ public:
|
|||
void setUsage(const Usage& usage) { _usage = usage; }
|
||||
Usage getUsage() const { return _usage; }
|
||||
|
||||
// For Cube Texture, it's possible to generate the irradiance spherical harmonics and make them availalbe with the texture
|
||||
// For Cube Texture, it's possible to generate the irradiance spherical harmonics and make them available with the texture
|
||||
bool generateIrradiance(gpu::BackendTarget target);
|
||||
const SHPointer& getIrradiance(uint16 slice = 0) const { return _irradiance; }
|
||||
void overrideIrradiance(SHPointer irradiance) { _irradiance = irradiance; }
|
||||
|
|
|
@ -103,7 +103,7 @@ gpu::Element getHDRTextureFormatForTarget(BackendTarget target, bool compressed)
|
|||
}
|
||||
}
|
||||
|
||||
TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, const QVariantMap& options) {
|
||||
TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type) {
|
||||
switch (type) {
|
||||
case ALBEDO_TEXTURE:
|
||||
return image::TextureUsage::createAlbedoTextureFromImage;
|
||||
|
@ -114,11 +114,7 @@ TextureUsage::TextureLoader TextureUsage::getTextureLoaderForType(Type type, con
|
|||
case SKY_TEXTURE:
|
||||
return image::TextureUsage::createCubeTextureFromImage;
|
||||
case AMBIENT_TEXTURE:
|
||||
if (options.value("generateIrradiance", true).toBool()) {
|
||||
return image::TextureUsage::createAmbientCubeTextureAndIrradianceFromImage;
|
||||
} else {
|
||||
return image::TextureUsage::createAmbientCubeTextureFromImage;
|
||||
}
|
||||
return image::TextureUsage::createAmbientCubeTextureAndIrradianceFromImage;
|
||||
case BUMP_TEXTURE:
|
||||
return image::TextureUsage::createNormalTextureFromBumpImage;
|
||||
case NORMAL_TEXTURE:
|
||||
|
@ -188,21 +184,11 @@ gpu::TexturePointer TextureUsage::createMetallicTextureFromImage(Image&& srcImag
|
|||
return process2DTextureGrayscaleFromImage(std::move(srcImage), srcImageName, compress, target, false, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createCubeTextureAndIrradianceFromImage(Image&& srcImage, const std::string& srcImageName,
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, CUBE_GENERATE_IRRADIANCE, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createCubeTextureFromImage(Image&& srcImage, const std::string& srcImageName,
|
||||
bool compress, BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return processCubeTextureColorFromImage(std::move(srcImage), srcImageName, compress, target, CUBE_DEFAULT, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createAmbientCubeTextureFromImage(Image&& image, const std::string& srcImageName,
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return processCubeTextureColorFromImage(std::move(image), srcImageName, compress, target, CUBE_GGX_CONVOLVE, abortProcessing);
|
||||
}
|
||||
|
||||
gpu::TexturePointer TextureUsage::createAmbientCubeTextureAndIrradianceFromImage(Image&& image, const std::string& srcImageName,
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing) {
|
||||
return processCubeTextureColorFromImage(std::move(image), srcImageName, compress, target, CUBE_GENERATE_IRRADIANCE | CUBE_GGX_CONVOLVE, abortProcessing);
|
||||
|
@ -388,7 +374,7 @@ gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::
|
|||
if (sourceChannel != ColorChannel::NONE) {
|
||||
mapToRedChannel(image, sourceChannel);
|
||||
}
|
||||
|
||||
|
||||
auto loader = TextureUsage::getTextureLoaderForType(textureType);
|
||||
auto texture = loader(std::move(image), filename, compress, target, abortProcessing);
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ enum Type {
|
|||
};
|
||||
|
||||
using TextureLoader = std::function<gpu::TexturePointer(Image&&, const std::string&, bool, gpu::BackendTarget, const std::atomic<bool>&)>;
|
||||
TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap());
|
||||
TextureLoader getTextureLoaderForType(Type type);
|
||||
|
||||
gpu::TexturePointer create2DTextureFromImage(Image&& image, const std::string& srcImageName,
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
|
@ -98,10 +98,6 @@ gpu::TexturePointer createMetallicTextureFromImage(Image&& image, const std::str
|
|||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createCubeTextureFromImage(Image&& image, const std::string& srcImageName,
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createCubeTextureAndIrradianceFromImage(Image&& image, const std::string& srcImageName,
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createAmbientCubeTextureFromImage(Image&& image, const std::string& srcImageName,
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createAmbientCubeTextureAndIrradianceFromImage(Image&& image, const std::string& srcImageName,
|
||||
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
|
||||
gpu::TexturePointer createLightmapTextureFromImage(Image&& image, const std::string& srcImageName,
|
||||
|
|
|
@ -311,13 +311,13 @@ gpu::BackendTarget getBackendTarget() {
|
|||
}
|
||||
|
||||
/// Returns a texture version of an image file
|
||||
gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) {
|
||||
gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type) {
|
||||
QImage image = QImage(path);
|
||||
if (image.isNull()) {
|
||||
qCWarning(networking) << "Unable to load required resource texture" << path;
|
||||
return nullptr;
|
||||
}
|
||||
auto loader = image::TextureUsage::getTextureLoaderForType(type, options);
|
||||
auto loader = image::TextureUsage::getTextureLoaderForType(type);
|
||||
|
||||
#ifdef USE_GLES
|
||||
constexpr bool shouldCompress = true;
|
||||
|
|
|
@ -176,7 +176,7 @@ public:
|
|||
const gpu::TexturePointer& getBlackTexture();
|
||||
|
||||
/// Returns a texture version of an image file
|
||||
static gpu::TexturePointer getImageTexture(const QString& path, image::TextureUsage::Type type = image::TextureUsage::DEFAULT_TEXTURE, QVariantMap options = QVariantMap());
|
||||
static gpu::TexturePointer getImageTexture(const QString& path, image::TextureUsage::Type type = image::TextureUsage::DEFAULT_TEXTURE);
|
||||
|
||||
/// Loads a texture from the specified URL.
|
||||
NetworkTexturePointer getTexture(const QUrl& url, image::TextureUsage::Type type = image::TextureUsage::DEFAULT_TEXTURE,
|
||||
|
|
|
@ -24,11 +24,11 @@
|
|||
#include "ReceivedMessage.h"
|
||||
|
||||
/**jsdoc
|
||||
* <p>The Messages API enables text and data to be sent between scripts over named "channels". A channel can have an arbitrary
|
||||
* name to help separate messaging between different sets of scripts.</p>
|
||||
* <p>The <code>Messages</code> API enables text and data to be sent between scripts over named "channels". A channel can have
|
||||
* an arbitrary name to help separate messaging between different sets of scripts.</p>
|
||||
*
|
||||
* <p><strong>Note:</strong> If you want to call a function in another script, you should use one of the following rather than
|
||||
* sending a message:</p>
|
||||
* <p><strong>Note:</strong> To call a function in another script, you should use one of the following rather than sending a
|
||||
* message:</p>
|
||||
* <ul>
|
||||
* <li>{@link Entities.callEntityClientMethod}</li>
|
||||
* <li>{@link Entities.callEntityMethod}</li>
|
||||
|
@ -52,7 +52,7 @@ public:
|
|||
void startThread();
|
||||
|
||||
/**jsdoc
|
||||
* Send a text message on a channel.
|
||||
* Sends a text message on a channel.
|
||||
* @function Messages.sendMessage
|
||||
* @param {string} channel - The channel to send the message on.
|
||||
* @param {string} message - The message to send.
|
||||
|
@ -90,9 +90,8 @@ public:
|
|||
Q_INVOKABLE void sendMessage(QString channel, QString message, bool localOnly = false);
|
||||
|
||||
/**jsdoc
|
||||
* Send a text message locally on a channel.
|
||||
* This is the same as calling {@link Messages.sendMessage|sendMessage} with <code>localOnly</code> set to
|
||||
* <code>true</code>.
|
||||
* Sends a text message locally on a channel.
|
||||
* This is the same as calling {@link Messages.sendMessage|sendMessage} with <code>localOnly == true</code>.
|
||||
* @function Messages.sendLocalMessage
|
||||
* @param {string} channel - The channel to send the message on.
|
||||
* @param {string} message - The message to send.
|
||||
|
@ -100,10 +99,10 @@ public:
|
|||
Q_INVOKABLE void sendLocalMessage(QString channel, QString message);
|
||||
|
||||
/**jsdoc
|
||||
* Send a data message on a channel.
|
||||
* Sends a data message on a channel.
|
||||
* @function Messages.sendData
|
||||
* @param {string} channel - The channel to send the data on.
|
||||
* @param {object} data - The data to send. The data is handled as a byte stream, for example as may be provided via a
|
||||
* @param {object} data - The data to send. The data is handled as a byte stream, for example, as may be provided via a
|
||||
* JavaScript <code>Int8Array</code> object.
|
||||
* @param {boolean} [localOnly=false] - If <code>false</code> then the message is sent to all Interface, client entity,
|
||||
* server entity, and assignment client scripts in the domain.<br />
|
||||
|
@ -147,16 +146,16 @@ public:
|
|||
Q_INVOKABLE void sendData(QString channel, QByteArray data, bool localOnly = false);
|
||||
|
||||
/**jsdoc
|
||||
* Subscribe the scripting environment — Interface, the entity script server, or assignment client instance —
|
||||
* to receive messages on a specific channel. Note that, for example, if there are two Interface scripts that subscribe to
|
||||
* different channels, both scripts will receive messages on both channels.
|
||||
* Subscribes the scripting environment — Interface, the entity script server, or assignment client instance —
|
||||
* to receive messages on a specific channel.This means, for example, that if there are two Interface scripts that
|
||||
* subscribe to different channels, both scripts will receive messages on both channels.
|
||||
* @function Messages.subscribe
|
||||
* @param {string} channel - The channel to subscribe to.
|
||||
*/
|
||||
Q_INVOKABLE void subscribe(QString channel);
|
||||
|
||||
/**jsdoc
|
||||
* Unsubscribe the scripting environment from receiving messages on a specific channel.
|
||||
* Unsubscribes the scripting environment from receiving messages on a specific channel.
|
||||
* @function Messages.unsubscribe
|
||||
* @param {string} channel - The channel to unsubscribe from.
|
||||
*/
|
||||
|
@ -170,15 +169,15 @@ public:
|
|||
|
||||
signals:
|
||||
/**jsdoc
|
||||
* Triggered when the a text message is received.
|
||||
* Triggered when a text message is received.
|
||||
* @function Messages.messageReceived
|
||||
* @param {string} channel - The channel that the message was sent on. You can use this to filter out messages not relevant
|
||||
* @param {string} channel - The channel that the message was sent on. This can be used to filter out messages not relevant
|
||||
* to your script.
|
||||
* @param {string} message - The message received.
|
||||
* @param {Uuid} senderID - The UUID of the sender: the user's session UUID if sent by an Interface or client entity
|
||||
* script, the UUID of the entity script server if sent by a server entity script, or the UUID of the assignment client
|
||||
* instance if sent by an assignment client script.
|
||||
* @param {boolean} localOnly - <code>true</code> if the message was sent with <code>localOnly = true</code>.
|
||||
* @param {boolean} localOnly - <code>true</code> if the message was sent with <code>localOnly == true</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void messageReceived(QString channel, QString message, QUuid senderUUID, bool localOnly);
|
||||
|
@ -186,15 +185,14 @@ signals:
|
|||
/**jsdoc
|
||||
* Triggered when a data message is received.
|
||||
* @function Messages.dataReceived
|
||||
* @param {string} channel - The channel that the message was sent on. You can use this to filter out messages not relevant
|
||||
* @param {string} channel - The channel that the message was sent on. This can be used to filter out messages not relevant
|
||||
* to your script.
|
||||
* @param {object} data - The data received. The data is handled as a byte stream, for example as may be used by a
|
||||
* @param {object} data - The data received. The data is handled as a byte stream, for example, as may be used by a
|
||||
* JavaScript <code>Int8Array</code> object.
|
||||
* @param {Uuid} senderID - The UUID of the sender: the user's session UUID if sent by an Interface or client entity
|
||||
* script, the UUID of the entity script server if sent by a server entity script, or the UUID of the assignment client
|
||||
* script, the UUID of the entity script server if sent by a server entity script, or the UUID of the assignment client
|
||||
* instance if sent by an assignment client script.
|
||||
* @param {boolean} localOnly - <code>true</code> if the message was sent with <code>localOnly = true</code>.
|
||||
* @param {boolean} localOnly - <code>true</code> if the message was sent with <code>localOnly == true</code>.
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void dataReceived(QString channel, QByteArray data, QUuid senderUUID, bool localOnly);
|
||||
|
|
|
@ -9,9 +9,10 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#include "ReceivedMessage.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "QSharedPointer"
|
||||
|
||||
int receivedMessageMetaTypeId = qRegisterMetaType<ReceivedMessage*>("ReceivedMessage*");
|
||||
|
@ -83,20 +84,26 @@ void ReceivedMessage::appendPacket(NLPacket& packet) {
|
|||
}
|
||||
|
||||
qint64 ReceivedMessage::peek(char* data, qint64 size) {
|
||||
memcpy(data, _data.constData() + _position, size);
|
||||
return size;
|
||||
size_t bytesLeft = _data.size() - _position;
|
||||
size_t sizeRead = std::min((size_t)size, bytesLeft);
|
||||
memcpy(data, _data.constData() + _position, sizeRead);
|
||||
return sizeRead;
|
||||
}
|
||||
|
||||
qint64 ReceivedMessage::read(char* data, qint64 size) {
|
||||
memcpy(data, _data.constData() + _position, size);
|
||||
_position += size;
|
||||
return size;
|
||||
size_t bytesLeft = _data.size() - _position;
|
||||
size_t sizeRead = std::min((size_t)size, bytesLeft);
|
||||
memcpy(data, _data.constData() + _position, sizeRead);
|
||||
_position += sizeRead;
|
||||
return sizeRead;
|
||||
}
|
||||
|
||||
qint64 ReceivedMessage::readHead(char* data, qint64 size) {
|
||||
memcpy(data, _headData.constData() + _position, size);
|
||||
_position += size;
|
||||
return size;
|
||||
size_t bytesLeft = _headData.size() - _position;
|
||||
size_t sizeRead = std::min((size_t)size, bytesLeft);
|
||||
memcpy(data, _headData.constData() + _position, sizeRead);
|
||||
_position += sizeRead;
|
||||
return sizeRead;
|
||||
}
|
||||
|
||||
QByteArray ReceivedMessage::peek(qint64 size) {
|
||||
|
|
|
@ -38,10 +38,10 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
||||
case PacketType::AvatarIdentity:
|
||||
case PacketType::AvatarData:
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::FBXJointOrderChange);
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::HandControllerSection);
|
||||
case PacketType::BulkAvatarData:
|
||||
case PacketType::KillAvatar:
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::FBXJointOrderChange);
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::HandControllerSection);
|
||||
case PacketType::MessagesData:
|
||||
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
|
||||
// ICE packets
|
||||
|
|
|
@ -331,7 +331,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
|
|||
AvatarTraitsAck,
|
||||
FasterAvatarEntities,
|
||||
SendMaxTranslationDimension,
|
||||
FBXJointOrderChange
|
||||
FBXJointOrderChange,
|
||||
HandControllerSection,
|
||||
};
|
||||
|
||||
enum class DomainConnectRequestVersion : PacketVersion {
|
||||
|
|
|
@ -754,7 +754,11 @@ void CharacterController::updateState() {
|
|||
switch (_state) {
|
||||
case State::Ground:
|
||||
if (!rayHasHit && !_hasSupport) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
if (_hoverWhenUnsupported) {
|
||||
SET_STATE(State::Hover, "no ground detected");
|
||||
} else {
|
||||
SET_STATE(State::InAir, "falling");
|
||||
}
|
||||
} else if (_pendingFlags & PENDING_FLAG_JUMP && _jumpButtonDownCount != _takeoffJumpButtonID) {
|
||||
_takeoffJumpButtonID = _jumpButtonDownCount;
|
||||
_takeoffToInAirStartTime = now;
|
||||
|
@ -764,7 +768,7 @@ void CharacterController::updateState() {
|
|||
}
|
||||
break;
|
||||
case State::Takeoff:
|
||||
if (!rayHasHit && !_hasSupport) {
|
||||
if (_hoverWhenUnsupported && (!rayHasHit && !_hasSupport)) {
|
||||
SET_STATE(State::Hover, "no ground");
|
||||
} else if ((now - _takeoffToInAirStartTime) > TAKE_OFF_TO_IN_AIR_PERIOD) {
|
||||
SET_STATE(State::InAir, "takeoff done");
|
||||
|
@ -791,7 +795,7 @@ void CharacterController::updateState() {
|
|||
SET_STATE(State::Hover, "double jump button");
|
||||
} else if (_comfortFlyingAllowed && (jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) {
|
||||
SET_STATE(State::Hover, "jump button held");
|
||||
} else if ((!rayHasHit && !_hasSupport) || _floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) {
|
||||
} else if (_hoverWhenUnsupported && ((!rayHasHit && !_hasSupport) || _floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT)) {
|
||||
// Transition to hover if there's no ground beneath us or we are above the fall threshold, regardless of _comfortFlyingAllowed
|
||||
SET_STATE(State::Hover, "above fall threshold");
|
||||
}
|
||||
|
|
|
@ -131,6 +131,7 @@ public:
|
|||
|
||||
void setZoneFlyingAllowed(bool value) { _zoneFlyingAllowed = value; }
|
||||
void setComfortFlyingAllowed(bool value) { _comfortFlyingAllowed = value; }
|
||||
void setHoverWhenUnsupported(bool value) { _hoverWhenUnsupported = value; }
|
||||
void setCollisionlessAllowed(bool value);
|
||||
|
||||
void setPendingFlagsUpdateCollisionMask(){ _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_MASK; }
|
||||
|
@ -215,6 +216,7 @@ protected:
|
|||
|
||||
bool _zoneFlyingAllowed { true };
|
||||
bool _comfortFlyingAllowed { true };
|
||||
bool _hoverWhenUnsupported{ true };
|
||||
bool _collisionlessAllowed { true };
|
||||
bool _collisionless { false };
|
||||
|
||||
|
|
|
@ -90,6 +90,7 @@ public:
|
|||
// HMD display functionality
|
||||
// TODO move out of this file don't derive DisplayPlugin from this. Instead use dynamic casting when
|
||||
// displayPlugin->isHmd returns true
|
||||
class RefreshRateController;
|
||||
class HmdDisplay : public StereoDisplay {
|
||||
public:
|
||||
// HMD specific methods
|
||||
|
@ -128,6 +129,7 @@ public:
|
|||
/// By default, all HMDs are stereo
|
||||
virtual bool isStereo() const { return isHmd(); }
|
||||
virtual bool isThrottled() const { return false; }
|
||||
|
||||
virtual float getTargetFrameRate() const { return 1.0f; }
|
||||
virtual bool hasAsyncReprojection() const { return false; }
|
||||
|
||||
|
|
|
@ -41,25 +41,54 @@ public:
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* Add nodes to the audio solo list
|
||||
* Adds avatars to the audio solo list. If the audio solo list is not empty, only audio from the avatars in the list is
|
||||
* played.
|
||||
* @function Audio.addToSoloList
|
||||
* @param {Uuid[]} uuidList - List of node UUIDs to add to the solo list.
|
||||
* @param {Uuid[]} ids - Avatar IDs to add to the solo list.
|
||||
|
||||
* @example <caption>Listen to a single nearby avatar for a short while.</caption>
|
||||
* // Find nearby avatars.
|
||||
* var RANGE = 100; // m
|
||||
* var nearbyAvatars = AvatarList.getAvatarsInRange(MyAvatar.position, RANGE);
|
||||
*
|
||||
* // Remove own avatar from list.
|
||||
* var myAvatarIndex = nearbyAvatars.indexOf(MyAvatar.sessionUUID);
|
||||
* if (myAvatarIndex !== -1) {
|
||||
* nearbyAvatars.splice(myAvatarIndex, 1);
|
||||
* }
|
||||
*
|
||||
* if (nearbyAvatars.length > 0) {
|
||||
* // Listen to only one of the nearby avatars.
|
||||
* var avatarName = AvatarList.getAvatar(nearbyAvatars[0]).displayName;
|
||||
* print("Listening only to " + avatarName);
|
||||
* Audio.addToSoloList([nearbyAvatars[0]]);
|
||||
*
|
||||
* // Stop listening to only the one avatar after a short while.
|
||||
* Script.setTimeout(function () {
|
||||
* print("Finished listening only to " + avatarName);
|
||||
* Audio.resetSoloList();
|
||||
* }, 10000); // 10s
|
||||
*
|
||||
* } else {
|
||||
* print("No nearby avatars");
|
||||
* }
|
||||
*/
|
||||
Q_INVOKABLE void addToSoloList(QVector<QUuid> uuidList) {
|
||||
_localAudioInterface->getAudioSolo().addUUIDs(uuidList);
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Remove nodes from the audio solo list
|
||||
* Removes avatars from the audio solo list. If the audio solo list is not empty, only audio from the avatars in the list
|
||||
* is played.
|
||||
* @function Audio.removeFromSoloList
|
||||
* @param {Uuid[]} uuidList - List of node UUIDs to remove from the solo list.
|
||||
* @param {Uuid[]} ids - Avatar IDs to remove from the solo list.
|
||||
*/
|
||||
Q_INVOKABLE void removeFromSoloList(QVector<QUuid> uuidList) {
|
||||
_localAudioInterface->getAudioSolo().removeUUIDs(uuidList);
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* Reset the list of soloed nodes.
|
||||
* Clears the audio solo list.
|
||||
* @function Audio.resetSoloList
|
||||
*/
|
||||
Q_INVOKABLE void resetSoloList() {
|
||||
|
@ -67,33 +96,56 @@ public:
|
|||
}
|
||||
|
||||
/**jsdoc
|
||||
* Gets whether your microphone audio is echoed back to you from the server. When enabled, microphone audio is echoed only
|
||||
* if you're unmuted or are using push-to-talk.
|
||||
* @function Audio.getServerEcho
|
||||
* @returns {boolean} <code>true</code> if echoing microphone audio back to you from the server is enabled,
|
||||
* <code>false</code> if it isn't.
|
||||
*/
|
||||
Q_INVOKABLE bool getServerEcho();
|
||||
|
||||
/**jsdoc
|
||||
* Sets whether your microphone audio is echoed back to you from the server. When enabled, microphone audio is echoed
|
||||
* only if you're unmuted or are using push-to-talk.
|
||||
* @function Audio.setServerEcho
|
||||
* @parm {boolean} serverEcho
|
||||
* @parm {boolean} serverEcho - <code>true</code> to enable echoing microphone back to you from the server,
|
||||
* <code>false<code> to disable.
|
||||
*/
|
||||
Q_INVOKABLE void setServerEcho(bool serverEcho);
|
||||
|
||||
/**jsdoc
|
||||
* Toggles the echoing of microphone audio back to you from the server. When enabled, microphone audio is echoed only if
|
||||
* you're unmuted or are using push-to-talk.
|
||||
* @function Audio.toggleServerEcho
|
||||
*/
|
||||
Q_INVOKABLE void toggleServerEcho();
|
||||
|
||||
/**jsdoc
|
||||
* Gets whether your microphone audio is echoed back to you by the client. When enabled, microphone audio is echoed
|
||||
* even if you're muted or not using push-to-talk.
|
||||
* @function Audio.getLocalEcho
|
||||
* @returns {boolean} <code>true</code> if echoing microphone audio back to you from the client is enabled,
|
||||
* <code>false</code> if it isn't.
|
||||
*/
|
||||
Q_INVOKABLE bool getLocalEcho();
|
||||
|
||||
/**jsdoc
|
||||
* Sets whether your microphone audio is echoed back to you by the client. When enabled, microphone audio is echoed
|
||||
* even if you're muted or not using push-to-talk.
|
||||
* @function Audio.setLocalEcho
|
||||
* @parm {boolean} localEcho
|
||||
* @parm {boolean} localEcho - <code>true</code> to enable echoing microphone audio back to you from the client,
|
||||
* <code>false</code> to disable.
|
||||
* @example <caption>Echo local audio for a few seconds.</caption>
|
||||
* Audio.setLocalEcho(true);
|
||||
* Script.setTimeout(function () {
|
||||
* Audio.setLocalEcho(false);
|
||||
* }, 3000); // 3s
|
||||
*/
|
||||
Q_INVOKABLE void setLocalEcho(bool localEcho);
|
||||
|
||||
/**jsdoc
|
||||
* Toggles the echoing of microphone audio back to you by the client. When enabled, microphone audio is echoed even if
|
||||
* you're muted or not using push-to-talk.
|
||||
* @function Audio.toggleLocalEcho
|
||||
*/
|
||||
Q_INVOKABLE void toggleLocalEcho();
|
||||
|
@ -105,7 +157,7 @@ protected:
|
|||
// these methods are protected to stop C++ callers from calling, but invokable from script
|
||||
|
||||
/**jsdoc
|
||||
* Starts playing — "injecting" — the content of an audio file. The sound is played globally (sent to the audio
|
||||
* Starts playing or "injecting" the content of an audio file. The sound is played globally (sent to the audio
|
||||
* mixer) so that everyone hears it, unless the <code>injectorOptions</code> has <code>localOnly</code> set to
|
||||
* <code>true</code> in which case only the client hears the sound played. No sound is played if sent to the audio mixer
|
||||
* but the client is not connected to an audio mixer. The {@link AudioInjector} object returned by the function can be used
|
||||
|
@ -113,7 +165,8 @@ protected:
|
|||
* @function Audio.playSound
|
||||
* @param {SoundObject} sound - The content of an audio file, loaded using {@link SoundCache.getSound}. See
|
||||
* {@link SoundObject} for supported formats.
|
||||
* @param {AudioInjector.AudioInjectorOptions} [injectorOptions={}] - Audio injector configuration.
|
||||
* @param {AudioInjector.AudioInjectorOptions} [injectorOptions={}] - Configures where and how the audio injector plays the
|
||||
* audio file.
|
||||
* @returns {AudioInjector} The audio injector that plays the audio file.
|
||||
* @example <caption>Play a sound.</caption>
|
||||
* var sound = SoundCache.getSound("http://hifi-content.s3.amazonaws.com/ken/samples/forest_ambiX.wav");
|
||||
|
@ -139,26 +192,26 @@ protected:
|
|||
Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions());
|
||||
|
||||
/**jsdoc
|
||||
* Start playing the content of an audio file, locally (isn't sent to the audio mixer). This is the same as calling
|
||||
* Starts playing the content of an audio file locally (isn't sent to the audio mixer). This is the same as calling
|
||||
* {@link Audio.playSound} with {@link AudioInjector.AudioInjectorOptions} <code>localOnly</code> set <code>true</code> and
|
||||
* the specified <code>position</code>.
|
||||
* @function Audio.playSystemSound
|
||||
* @param {SoundObject} sound - The content of an audio file, loaded using {@link SoundCache.getSound}. See
|
||||
* @param {SoundObject} sound - The content of an audio file, which is loaded using {@link SoundCache.getSound}. See
|
||||
* {@link SoundObject} for supported formats.
|
||||
* @returns {AudioInjector} The audio injector that plays the audio file.
|
||||
*/
|
||||
Q_INVOKABLE ScriptAudioInjector* playSystemSound(SharedSoundPointer sound);
|
||||
|
||||
/**jsdoc
|
||||
* Set whether or not the audio input should be used in stereo. If the audio input does not support stereo then setting a
|
||||
* value of <code>true</code> has no effect.
|
||||
* Sets whether the audio input should be used in stereo. If the audio input doesn't support stereo then setting a value
|
||||
* of <code>true</code> has no effect.
|
||||
* @function Audio.setStereoInput
|
||||
* @param {boolean} stereo - <code>true</code> if the audio input should be used in stereo, otherwise <code>false</code>.
|
||||
*/
|
||||
Q_INVOKABLE void setStereoInput(bool stereo);
|
||||
|
||||
/**jsdoc
|
||||
* Get whether or not the audio input is used in stereo.
|
||||
* Gets whether the audio input is used in stereo.
|
||||
* @function Audio.isStereoInput
|
||||
* @returns {boolean} <code>true</code> if the audio input is used in stereo, otherwise <code>false</code>.
|
||||
*/
|
||||
|
@ -168,7 +221,7 @@ signals:
|
|||
|
||||
/**jsdoc
|
||||
* Triggered when the client is muted by the mixer because their loudness value for the noise background has reached the
|
||||
* threshold set for the domain in the server settings.
|
||||
* threshold set for the domain (in the server settings).
|
||||
* @function Audio.mutedByMixer
|
||||
* @returns {Signal}
|
||||
*/
|
||||
|
@ -197,7 +250,7 @@ signals:
|
|||
void disconnected();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the noise gate is opened: the input audio signal is no longer blocked (fully attenuated) because it has
|
||||
* Triggered when the noise gate is opened. The input audio signal is no longer blocked (fully attenuated) because it has
|
||||
* risen above an adaptive threshold set just above the noise floor. Only occurs if <code>Audio.noiseReduction</code> is
|
||||
* <code>true</code>.
|
||||
* @function Audio.noiseGateOpened
|
||||
|
@ -206,7 +259,7 @@ signals:
|
|||
void noiseGateOpened();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the noise gate is closed: the input audio signal is blocked (fully attenuated) because it has fallen
|
||||
* Triggered when the noise gate is closed. The input audio signal is blocked (fully attenuated) because it has fallen
|
||||
* below an adaptive threshold set just above the noise floor. Only occurs if <code>Audio.noiseReduction</code> is
|
||||
* <code>true</code>.
|
||||
* @function Audio.noiseGateClosed
|
||||
|
|
|
@ -53,22 +53,22 @@ QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuIt
|
|||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Menu.addMenuItem} to create a new menu item.
|
||||
*
|
||||
* If none of <code>position</code>, <code>beforeItem</code>, <code>afterItem</code>, or <code>grouping</code> are specified,
|
||||
* the menu item will be placed at the end of the menu.
|
||||
* If none of the properties, <code>position</code>, <code>beforeItem</code>, <code>afterItem</code>, or <code>grouping</code>
|
||||
* are specified, the menu item will be placed at the end of the menu.
|
||||
*
|
||||
* @typedef {object} Menu.MenuItemProperties
|
||||
* @property {string} menuName Name of the menu. Nested menus can be described using the ">" symbol.
|
||||
* @property {string} menuItemName Name of the menu item.
|
||||
* @property {boolean} [isCheckable=false] Whether or not the menu item is checkable.
|
||||
* @property {boolean} [isChecked=false] Whether or not the menu item is checked.
|
||||
* @property {boolean} [isSeparator=false] Whether or not the menu item is a separator.
|
||||
* @property {string} [shortcutKey] A shortcut key that triggers the menu item.
|
||||
* @property {KeyEvent} [shortcutKeyEvent] A {@link KeyEvent} that specifies a key that triggers the menu item.
|
||||
* @property {number} [position] The position to place the new menu item. An integer number with <code>0</code> being the first
|
||||
* @property {string} menuName - Name of the menu. Nested menus can be described using the ">" character.
|
||||
* @property {string} menuItemName - Name of the menu item.
|
||||
* @property {boolean} [isCheckable=false] - Whether or not the menu item is checkable.
|
||||
* @property {boolean} [isChecked=false] - Whether or not the menu item is checked.
|
||||
* @property {boolean} [isSeparator=false] - Whether or not the menu item is a separator.
|
||||
* @property {string} [shortcutKey] - A shortcut key that triggers the menu item.
|
||||
* @property {KeyEvent} [shortcutKeyEvent] - A {@link KeyEvent} that specifies a key that triggers the menu item.
|
||||
* @property {number} [position] - The position to place the new menu item. An integer number with <code>0</code> being the first
|
||||
* menu item.
|
||||
* @property {string} [beforeItem] The name of the menu item to place this menu item before.
|
||||
* @property {string} [afterItem] The name of the menu item to place this menu item after.
|
||||
* @property {string} [grouping] The name of grouping to add this menu item to.
|
||||
* @property {string} [beforeItem] - The name of the menu item to place this menu item before.
|
||||
* @property {string} [afterItem] - The name of the menu item to place this menu item after.
|
||||
* @property {string} [grouping] - The name of grouping to add this menu item to.
|
||||
*/
|
||||
void menuItemPropertiesFromScriptValue(const QScriptValue& object, MenuItemProperties& properties) {
|
||||
properties.menuName = object.property("menuName").toVariant().toString();
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
#include <AudioInjectorManager.h>
|
||||
|
||||
/**jsdoc
|
||||
* Plays — "injects" — the content of an audio file. Used in the {@link Audio} API.
|
||||
* Plays or "injects" the content of an audio file.
|
||||
*
|
||||
* <p>Create using {@link Audio} API methods.</p>
|
||||
*
|
||||
* @class AudioInjector
|
||||
*
|
||||
|
@ -45,13 +47,13 @@ public:
|
|||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
* Stop current playback, if any, and start playing from the beginning.
|
||||
* Stops current playback, if any, and starts playing from the beginning.
|
||||
* @function AudioInjector.restart
|
||||
*/
|
||||
void restart() { DependencyManager::get<AudioInjectorManager>()->restart(_injector); }
|
||||
|
||||
/**jsdoc
|
||||
* Stop audio playback.
|
||||
* Stops audio playback.
|
||||
* @function AudioInjector.stop
|
||||
* @example <caption>Stop playing a sound before it finishes.</caption>
|
||||
* var sound = SoundCache.getSound(Script.resourcesPath() + "sounds/sample.wav");
|
||||
|
@ -71,28 +73,28 @@ public slots:
|
|||
void stop() { DependencyManager::get<AudioInjectorManager>()->stop(_injector); }
|
||||
|
||||
/**jsdoc
|
||||
* Get the current configuration of the audio injector.
|
||||
* Gets the current configuration of the audio injector.
|
||||
* @function AudioInjector.getOptions
|
||||
* @returns {AudioInjector.AudioInjectorOptions} Configuration of how the injector plays the audio.
|
||||
*/
|
||||
AudioInjectorOptions getOptions() const { return DependencyManager::get<AudioInjectorManager>()->getOptions(_injector); }
|
||||
|
||||
/**jsdoc
|
||||
* Configure how the injector plays the audio.
|
||||
* Configures how the injector plays the audio.
|
||||
* @function AudioInjector.setOptions
|
||||
* @param {AudioInjector.AudioInjectorOptions} options - Configuration of how the injector plays the audio.
|
||||
*/
|
||||
void setOptions(const AudioInjectorOptions& options) { DependencyManager::get<AudioInjectorManager>()->setOptions(_injector, options); }
|
||||
|
||||
/**jsdoc
|
||||
* Get the loudness of the most recent frame of audio played.
|
||||
* Gets the loudness of the most recent frame of audio played.
|
||||
* @function AudioInjector.getLoudness
|
||||
* @returns {number} The loudness of the most recent frame of audio played, range <code>0.0</code> – <code>1.0</code>.
|
||||
*/
|
||||
float getLoudness() const { return DependencyManager::get<AudioInjectorManager>()->getLoudness(_injector); }
|
||||
|
||||
/**jsdoc
|
||||
* Get whether or not the audio is currently playing.
|
||||
* Gets whether or not the audio is currently playing.
|
||||
* @function AudioInjector.isPlaying
|
||||
* @returns {boolean} <code>true</code> if the audio is currently playing, otherwise <code>false</code>.
|
||||
* @example <caption>See if a sound is playing.</caption>
|
||||
|
|
|
@ -687,7 +687,6 @@ public:
|
|||
* Get the number of vertices in the mesh.
|
||||
* @function MeshProxy#getNumVertices
|
||||
* @returns {number} Integer number of vertices in the mesh.
|
||||
* @deprecated Use the {@link Graphics} API instead.
|
||||
*/
|
||||
Q_INVOKABLE virtual int getNumVertices() const = 0;
|
||||
|
||||
|
@ -696,7 +695,6 @@ public:
|
|||
* @function MeshProxy#getPos
|
||||
* @param {number} index - Integer index of the mesh vertex.
|
||||
* @returns {Vec3} Local position of the vertex relative to the mesh.
|
||||
* @deprecated Use the {@link Graphics} API instead.
|
||||
*/
|
||||
Q_INVOKABLE virtual glm::vec3 getPos(int index) const = 0;
|
||||
Q_INVOKABLE virtual glm::vec3 getPos3(int index) const { return getPos(index); } // deprecated
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue