mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 01:17:14 +02:00
Merge branch 'feature/quest_custom_plugins' into feature/quest
This commit is contained in:
commit
a6f23f48d6
136 changed files with 3190 additions and 2228 deletions
|
@ -81,7 +81,11 @@ if (ANDROID)
|
||||||
set(GLES_OPTION ON)
|
set(GLES_OPTION ON)
|
||||||
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
|
set(PLATFORM_QT_COMPONENTS AndroidExtras WebView)
|
||||||
add_definitions(-DHIFI_ANDROID_APP=\"${HIFI_ANDROID_APP}\")
|
add_definitions(-DHIFI_ANDROID_APP=\"${HIFI_ANDROID_APP}\")
|
||||||
if (${HIFI_ANDROID_APP} STREQUAL "questInterface")
|
if (
|
||||||
|
(${HIFI_ANDROID_APP} STREQUAL "questInterface") OR
|
||||||
|
(${HIFI_ANDROID_APP} STREQUAL "questFramePlayer") OR
|
||||||
|
(${HIFI_ANDROID_APP} STREQUAL "framePlayer")
|
||||||
|
)
|
||||||
# We know the quest hardware has this extension, so we can force the use of instanced stereo
|
# We know the quest hardware has this extension, so we can force the use of instanced stereo
|
||||||
add_definitions(-DHAVE_EXT_clip_cull_distance)
|
add_definitions(-DHAVE_EXT_clip_cull_distance)
|
||||||
# We can also use multiview stereo techniques
|
# We can also use multiview stereo techniques
|
||||||
|
@ -89,9 +93,15 @@ if (ANDROID)
|
||||||
add_definitions(-DHAVE_OVR_multiview)
|
add_definitions(-DHAVE_OVR_multiview)
|
||||||
# We can also use our own foveated textures
|
# We can also use our own foveated textures
|
||||||
add_definitions(-DHAVE_QCOM_texture_foveated)
|
add_definitions(-DHAVE_QCOM_texture_foveated)
|
||||||
|
|
||||||
|
# if set, the application itself or some library it depends on MUST implement
|
||||||
|
# `DisplayPluginList getDisplayPlugins()` and `InputPluginList getInputPlugins()`
|
||||||
|
add_definitions(-DCUSTOM_INPUT_PLUGINS)
|
||||||
|
add_definitions(-DCUSTOM_DISPLAY_PLUGINS)
|
||||||
|
set(PLATFORM_PLUGIN_LIBRARIES oculusMobile oculusMobilePlugin)
|
||||||
endif()
|
endif()
|
||||||
else ()
|
else ()
|
||||||
set(PLATFORM_QT_COMPONENTS WebEngine)
|
set(PLATFORM_QT_COMPONENTS WebEngine Xml)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (USE_GLES AND (NOT ANDROID))
|
if (USE_GLES AND (NOT ANDROID))
|
||||||
|
|
|
@ -38,6 +38,19 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
|
||||||
// FIXME - what we'd actually like to do is send to users at ~50% of their present rate down to 30hz. Assume 90 for now.
|
// FIXME - what we'd actually like to do is send to users at ~50% of their present rate down to 30hz. Assume 90 for now.
|
||||||
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
|
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
|
||||||
|
|
||||||
|
const QRegularExpression AvatarMixer::suffixedNamePattern { R"(^\s*(.+)\s*_(\d)+\s*$)" };
|
||||||
|
|
||||||
|
// Lexicographic comparison:
|
||||||
|
bool AvatarMixer::SessionDisplayName::operator<(const SessionDisplayName& rhs) const {
|
||||||
|
if (_baseName < rhs._baseName) {
|
||||||
|
return true;
|
||||||
|
} else if (rhs._baseName < _baseName) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return _suffix < rhs._suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||||
ThreadedAssignment(message),
|
ThreadedAssignment(message),
|
||||||
_slavePool(&_slaveSharedData)
|
_slavePool(&_slaveSharedData)
|
||||||
|
@ -313,27 +326,40 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
|
||||||
bool sendIdentity = false;
|
bool sendIdentity = false;
|
||||||
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
|
if (nodeData && nodeData->getAvatarSessionDisplayNameMustChange()) {
|
||||||
AvatarData& avatar = nodeData->getAvatar();
|
AvatarData& avatar = nodeData->getAvatar();
|
||||||
const QString& existingBaseDisplayName = nodeData->getBaseDisplayName();
|
const QString& existingBaseDisplayName = nodeData->getAvatar().getSessionDisplayName();
|
||||||
if (--_sessionDisplayNames[existingBaseDisplayName].second <= 0) {
|
if (!existingBaseDisplayName.isEmpty()) {
|
||||||
_sessionDisplayNames.remove(existingBaseDisplayName);
|
SessionDisplayName existingDisplayName { existingBaseDisplayName };
|
||||||
|
|
||||||
|
auto suffixMatch = suffixedNamePattern.match(existingBaseDisplayName);
|
||||||
|
if (suffixMatch.hasMatch()) {
|
||||||
|
existingDisplayName._baseName = suffixMatch.captured(1);
|
||||||
|
existingDisplayName._suffix = suffixMatch.captured(2).toInt();
|
||||||
|
}
|
||||||
|
_sessionDisplayNames.erase(existingDisplayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString baseName = avatar.getDisplayName().trimmed();
|
QString baseName = avatar.getDisplayName().trimmed();
|
||||||
const QRegularExpression curses { "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?).
|
const QRegularExpression curses { "fuck|shit|damn|cock|cunt" }; // POC. We may eventually want something much more elaborate (subscription?).
|
||||||
baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk.
|
baseName = baseName.replace(curses, "*"); // Replace rather than remove, so that people have a clue that the person's a jerk.
|
||||||
const QRegularExpression trailingDigits { "\\s*(_\\d+\\s*)?(\\s*\\n[^$]*)?$" }; // trailing whitespace "_123" and any subsequent lines
|
static const QRegularExpression trailingDigits { R"(\s*(_\d+\s*)?(\s*\n[^$]*)?$)" }; // trailing whitespace "_123" and any subsequent lines
|
||||||
baseName = baseName.remove(trailingDigits);
|
baseName = baseName.remove(trailingDigits);
|
||||||
if (baseName.isEmpty()) {
|
if (baseName.isEmpty()) {
|
||||||
baseName = "anonymous";
|
baseName = "anonymous";
|
||||||
}
|
}
|
||||||
|
|
||||||
QPair<int, int>& soFar = _sessionDisplayNames[baseName]; // Inserts and answers 0, 0 if not already present, which is what we want.
|
SessionDisplayName newDisplayName { baseName };
|
||||||
int& highWater = soFar.first;
|
auto nameIter = _sessionDisplayNames.lower_bound(newDisplayName);
|
||||||
nodeData->setBaseDisplayName(baseName);
|
if (nameIter != _sessionDisplayNames.end() && nameIter->_baseName == baseName) {
|
||||||
QString sessionDisplayName = (highWater > 0) ? baseName + "_" + QString::number(highWater) : baseName;
|
// Existing instance(s) of name; find first free suffix
|
||||||
|
while (*nameIter == newDisplayName && ++newDisplayName._suffix && ++nameIter != _sessionDisplayNames.end())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sessionDisplayNames.insert(newDisplayName);
|
||||||
|
QString sessionDisplayName = (newDisplayName._suffix > 0) ? baseName + "_" + QString::number(newDisplayName._suffix) : baseName;
|
||||||
avatar.setSessionDisplayName(sessionDisplayName);
|
avatar.setSessionDisplayName(sessionDisplayName);
|
||||||
highWater++;
|
nodeData->setBaseDisplayName(baseName);
|
||||||
soFar.second++; // refcount
|
|
||||||
nodeData->flagIdentityChange();
|
nodeData->flagIdentityChange();
|
||||||
nodeData->setAvatarSessionDisplayNameMustChange(false);
|
nodeData->setAvatarSessionDisplayNameMustChange(false);
|
||||||
sendIdentity = true;
|
sendIdentity = true;
|
||||||
|
@ -409,10 +435,19 @@ void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) {
|
||||||
{ // decrement sessionDisplayNames table and possibly remove
|
{ // decrement sessionDisplayNames table and possibly remove
|
||||||
QMutexLocker nodeDataLocker(&avatarNode->getLinkedData()->getMutex());
|
QMutexLocker nodeDataLocker(&avatarNode->getLinkedData()->getMutex());
|
||||||
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
|
AvatarMixerClientData* nodeData = dynamic_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
|
||||||
const QString& baseDisplayName = nodeData->getBaseDisplayName();
|
const QString& displayName = nodeData->getAvatar().getSessionDisplayName();
|
||||||
// No sense guarding against very rare case of a node with no entry, as this will work without the guard and do one less lookup in the common case.
|
SessionDisplayName exitingDisplayName { displayName };
|
||||||
if (--_sessionDisplayNames[baseDisplayName].second <= 0) {
|
|
||||||
_sessionDisplayNames.remove(baseDisplayName);
|
auto suffixMatch = suffixedNamePattern.match(displayName);
|
||||||
|
if (suffixMatch.hasMatch()) {
|
||||||
|
exitingDisplayName._baseName = suffixMatch.captured(1);
|
||||||
|
exitingDisplayName._suffix = suffixMatch.captured(2).toInt();
|
||||||
|
}
|
||||||
|
auto displayNameIter = _sessionDisplayNames.find(exitingDisplayName);
|
||||||
|
if (displayNameIter == _sessionDisplayNames.end()) {
|
||||||
|
qCDebug(avatars) << "Exiting avatar displayname" << displayName << "not found";
|
||||||
|
} else {
|
||||||
|
_sessionDisplayNames.erase(displayNameIter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#ifndef hifi_AvatarMixer_h
|
#ifndef hifi_AvatarMixer_h
|
||||||
#define hifi_AvatarMixer_h
|
#define hifi_AvatarMixer_h
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include <shared/RateCounter.h>
|
#include <shared/RateCounter.h>
|
||||||
#include <PortableHighResolutionClock.h>
|
#include <PortableHighResolutionClock.h>
|
||||||
|
|
||||||
|
@ -88,7 +89,24 @@ private:
|
||||||
|
|
||||||
RateCounter<> _broadcastRate;
|
RateCounter<> _broadcastRate;
|
||||||
p_high_resolution_clock::time_point _lastDebugMessage;
|
p_high_resolution_clock::time_point _lastDebugMessage;
|
||||||
QHash<QString, QPair<int, int>> _sessionDisplayNames;
|
|
||||||
|
// Pair of basename + uniquifying integer suffix.
|
||||||
|
struct SessionDisplayName {
|
||||||
|
explicit SessionDisplayName(QString baseName = QString(), int suffix = 0) :
|
||||||
|
_baseName(baseName),
|
||||||
|
_suffix(suffix) { }
|
||||||
|
// Does lexicographic ordering:
|
||||||
|
bool operator<(const SessionDisplayName& rhs) const;
|
||||||
|
bool operator==(const SessionDisplayName& rhs) const {
|
||||||
|
return _baseName == rhs._baseName && _suffix == rhs._suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString _baseName;
|
||||||
|
int _suffix;
|
||||||
|
};
|
||||||
|
static const QRegularExpression suffixedNamePattern;
|
||||||
|
|
||||||
|
std::set<SessionDisplayName> _sessionDisplayNames;
|
||||||
|
|
||||||
quint64 _displayNameManagementElapsedTime { 0 }; // total time spent in broadcastAvatarData/display name management... since last stats window
|
quint64 _displayNameManagementElapsedTime { 0 }; // total time spent in broadcastAvatarData/display name management... since last stats window
|
||||||
quint64 _ignoreCalculationElapsedTime { 0 };
|
quint64 _ignoreCalculationElapsedTime { 0 };
|
||||||
|
|
36
cmake/macros/FixupNitpick.cmake
Normal file
36
cmake/macros/FixupNitpick.cmake
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#
|
||||||
|
# FixupNitpick.cmake
|
||||||
|
# cmake/macros
|
||||||
|
#
|
||||||
|
# Copyright 2019 High Fidelity, Inc.
|
||||||
|
# Created by Nissim Hadar on January 14th, 2016
|
||||||
|
#
|
||||||
|
# Distributed under the Apache License, Version 2.0.
|
||||||
|
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
#
|
||||||
|
|
||||||
|
macro(fixup_nitpick)
|
||||||
|
if (APPLE)
|
||||||
|
string(REPLACE " " "\\ " ESCAPED_BUNDLE_NAME ${NITPICK_BUNDLE_NAME})
|
||||||
|
string(REPLACE " " "\\ " ESCAPED_INSTALL_PATH ${NITPICK_INSTALL_DIR})
|
||||||
|
set(_NITPICK_INSTALL_PATH "${ESCAPED_INSTALL_PATH}/${ESCAPED_BUNDLE_NAME}.app")
|
||||||
|
|
||||||
|
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH)
|
||||||
|
|
||||||
|
if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD))
|
||||||
|
message(FATAL_ERROR "Could not find macdeployqt at ${QT_DIR}/bin.\
|
||||||
|
It is required to produce a relocatable nitpick application.\
|
||||||
|
Check that the environment variable QT_DIR points to your Qt installation.\
|
||||||
|
")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
install(CODE "
|
||||||
|
execute_process(COMMAND ${MACDEPLOYQT_COMMAND}\
|
||||||
|
\${CMAKE_INSTALL_PREFIX}/${_NITPICK_INSTALL_PATH}/\
|
||||||
|
-verbose=2 -qmldir=${CMAKE_SOURCE_DIR}/interface/resources/qml/\
|
||||||
|
)"
|
||||||
|
COMPONENT ${CLIENT_COMPONENT}
|
||||||
|
)
|
||||||
|
|
||||||
|
endif ()
|
||||||
|
endmacro()
|
|
@ -77,6 +77,9 @@ macro(SET_PACKAGING_PARAMETERS)
|
||||||
add_definitions(-DDEV_BUILD)
|
add_definitions(-DDEV_BUILD)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
set(NITPICK_BUNDLE_NAME "nitpick")
|
||||||
|
set(NITPICK_ICON_PREFIX "nitpick")
|
||||||
|
|
||||||
string(TIMESTAMP BUILD_TIME "%d/%m/%Y")
|
string(TIMESTAMP BUILD_TIME "%d/%m/%Y")
|
||||||
|
|
||||||
# if STABLE_BUILD is 1, PRODUCTION_BUILD must be 1 and
|
# if STABLE_BUILD is 1, PRODUCTION_BUILD must be 1 and
|
||||||
|
@ -140,8 +143,9 @@ macro(SET_PACKAGING_PARAMETERS)
|
||||||
|
|
||||||
set(DMG_SUBFOLDER_ICON "${HF_CMAKE_DIR}/installer/install-folder.rsrc")
|
set(DMG_SUBFOLDER_ICON "${HF_CMAKE_DIR}/installer/install-folder.rsrc")
|
||||||
|
|
||||||
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
set(CONSOLE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||||
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
set(INTERFACE_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||||
|
set(NITPICK_INSTALL_DIR ${DMG_SUBFOLDER_NAME})
|
||||||
|
|
||||||
if (CLIENT_ONLY)
|
if (CLIENT_ONLY)
|
||||||
set(CONSOLE_EXEC_NAME "Console.app")
|
set(CONSOLE_EXEC_NAME "Console.app")
|
||||||
|
@ -159,11 +163,14 @@ macro(SET_PACKAGING_PARAMETERS)
|
||||||
|
|
||||||
set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app")
|
set(INTERFACE_INSTALL_APP_PATH "${CONSOLE_INSTALL_DIR}/${INTERFACE_BUNDLE_NAME}.app")
|
||||||
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns")
|
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.icns")
|
||||||
|
set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.icns")
|
||||||
else ()
|
else ()
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(CONSOLE_INSTALL_DIR "server-console")
|
set(CONSOLE_INSTALL_DIR "server-console")
|
||||||
|
set(NITPICK_INSTALL_DIR "nitpick")
|
||||||
else ()
|
else ()
|
||||||
set(CONSOLE_INSTALL_DIR ".")
|
set(CONSOLE_INSTALL_DIR ".")
|
||||||
|
set(NITPICK_INSTALL_DIR ".")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
set(COMPONENT_INSTALL_DIR ".")
|
set(COMPONENT_INSTALL_DIR ".")
|
||||||
|
@ -173,6 +180,7 @@ macro(SET_PACKAGING_PARAMETERS)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(INTERFACE_EXEC_PREFIX "interface")
|
set(INTERFACE_EXEC_PREFIX "interface")
|
||||||
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.ico")
|
set(INTERFACE_ICON_FILENAME "${INTERFACE_ICON_PREFIX}.ico")
|
||||||
|
set(NITPICK_ICON_FILENAME "${NITPICK_ICON_PREFIX}.ico")
|
||||||
|
|
||||||
set(CONSOLE_EXEC_NAME "server-console.exe")
|
set(CONSOLE_EXEC_NAME "server-console.exe")
|
||||||
|
|
||||||
|
|
|
@ -217,8 +217,8 @@ link_hifi_libraries(
|
||||||
ui-plugins display-plugins input-plugins
|
ui-plugins display-plugins input-plugins
|
||||||
# Platform specific GL libraries
|
# Platform specific GL libraries
|
||||||
${PLATFORM_GL_BACKEND}
|
${PLATFORM_GL_BACKEND}
|
||||||
# Plaform specific display plugins libraries
|
# Plaform specific input & display plugin libraries
|
||||||
${PLATFORM_DISPLAY_PLUGINS}
|
${PLATFORM_PLUGIN_LIBRARIES}
|
||||||
shaders
|
shaders
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
BIN
interface/resources/images/unsupportedImage.png
Normal file
BIN
interface/resources/images/unsupportedImage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -10,10 +10,6 @@ joint = jointRoot = Hips
|
||||||
joint = jointLeftHand = LeftHand
|
joint = jointLeftHand = LeftHand
|
||||||
joint = jointRightHand = RightHand
|
joint = jointRightHand = RightHand
|
||||||
joint = jointHead = Head
|
joint = jointHead = Head
|
||||||
freeJoint = LeftArm
|
|
||||||
freeJoint = LeftForeArm
|
|
||||||
freeJoint = RightArm
|
|
||||||
freeJoint = RightForeArm
|
|
||||||
bs = JawOpen = mouth_Open = 1
|
bs = JawOpen = mouth_Open = 1
|
||||||
bs = LipsFunnel = Oo = 1
|
bs = LipsFunnel = Oo = 1
|
||||||
bs = BrowsU_L = brow_Up = 1
|
bs = BrowsU_L = brow_Up = 1
|
||||||
|
|
108
interface/resources/qml/+webengine/QmlWebWindow.qml
Normal file
108
interface/resources/qml/+webengine/QmlWebWindow.qml
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
//
|
||||||
|
// QmlWebWindow.qml
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 17 Dec 2015
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtWebEngine 1.1
|
||||||
|
import QtWebChannel 1.0
|
||||||
|
|
||||||
|
import "qrc:////qml//windows" as Windows
|
||||||
|
import controlsUit 1.0 as Controls
|
||||||
|
import stylesUit 1.0
|
||||||
|
|
||||||
|
Windows.ScrollingWindow {
|
||||||
|
id: root
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
title: "WebWindow"
|
||||||
|
resizable: true
|
||||||
|
shown: false
|
||||||
|
// Don't destroy on close... otherwise the JS/C++ will have a dangling pointer
|
||||||
|
destroyOnCloseButton: false
|
||||||
|
property alias source: webview.url
|
||||||
|
property alias scriptUrl: webview.userScriptUrl
|
||||||
|
|
||||||
|
// This is for JS/QML communication, which is unused in a WebWindow,
|
||||||
|
// but not having this here results in spurious warnings about a
|
||||||
|
// missing signal
|
||||||
|
signal sendToScript(var message);
|
||||||
|
|
||||||
|
signal moved(vector2d position);
|
||||||
|
signal resized(size size);
|
||||||
|
|
||||||
|
function notifyMoved() {
|
||||||
|
moved(Qt.vector2d(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
function notifyResized() {
|
||||||
|
resized(Qt.size(width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
onXChanged: notifyMoved();
|
||||||
|
onYChanged: notifyMoved();
|
||||||
|
|
||||||
|
onWidthChanged: notifyResized();
|
||||||
|
onHeightChanged: notifyResized();
|
||||||
|
|
||||||
|
onShownChanged: {
|
||||||
|
keyboardEnabled = HMD.active;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: pane.contentWidth
|
||||||
|
implicitHeight: pane.scrollHeight
|
||||||
|
|
||||||
|
Controls.WebView {
|
||||||
|
id: webview
|
||||||
|
url: "about:blank"
|
||||||
|
anchors.fill: parent
|
||||||
|
focus: true
|
||||||
|
profile: HFWebEngineProfile;
|
||||||
|
|
||||||
|
property string userScriptUrl: ""
|
||||||
|
|
||||||
|
// Create a global EventBridge object for raiseAndLowerKeyboard.
|
||||||
|
WebEngineScript {
|
||||||
|
id: createGlobalEventBridge
|
||||||
|
sourceCode: eventBridgeJavaScriptToInject
|
||||||
|
injectionPoint: WebEngineScript.DocumentCreation
|
||||||
|
worldId: WebEngineScript.MainWorld
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect when may want to raise and lower keyboard.
|
||||||
|
WebEngineScript {
|
||||||
|
id: raiseAndLowerKeyboard
|
||||||
|
injectionPoint: WebEngineScript.Deferred
|
||||||
|
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
|
||||||
|
worldId: WebEngineScript.MainWorld
|
||||||
|
}
|
||||||
|
|
||||||
|
// User script.
|
||||||
|
WebEngineScript {
|
||||||
|
id: userScript
|
||||||
|
sourceUrl: webview.userScriptUrl
|
||||||
|
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
|
||||||
|
worldId: WebEngineScript.MainWorld
|
||||||
|
}
|
||||||
|
|
||||||
|
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
|
||||||
|
|
||||||
|
function onWebEventReceived(event) {
|
||||||
|
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
||||||
|
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
webChannel.registerObject("eventBridge", eventBridge);
|
||||||
|
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
||||||
|
eventBridge.webEventReceived.connect(onWebEventReceived);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,6 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtWebView 1.1
|
|
||||||
import QtWebChannel 1.0
|
|
||||||
|
|
||||||
import "windows" as Windows
|
import "windows" as Windows
|
||||||
import controlsUit 1.0 as Controls
|
import controlsUit 1.0 as Controls
|
||||||
|
@ -60,22 +58,9 @@ Windows.ScrollingWindow {
|
||||||
Controls.WebView {
|
Controls.WebView {
|
||||||
id: webview
|
id: webview
|
||||||
url: "about:blank"
|
url: "about:blank"
|
||||||
|
property string userScriptUrl: ""
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
property string userScriptUrl: ""
|
|
||||||
|
|
||||||
function onWebEventReceived(event) {
|
|
||||||
if (event.slice(0, 17) === "CLARA.IO DOWNLOAD") {
|
|
||||||
ApplicationInterface.addAssetToWorldFromURL(event.slice(18));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
webChannel.registerObject("eventBridge", eventBridge);
|
|
||||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
|
||||||
eventBridge.webEventReceived.connect(onWebEventReceived);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
//
|
||||||
|
// WebSpinner.qml
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 23 May 2017
|
||||||
|
// Copyright 2017 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
|
||||||
|
//
|
||||||
|
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtWebEngine 1.5
|
||||||
|
|
||||||
|
AnimatedImage {
|
||||||
|
property WebEngineView webview: parent
|
||||||
|
source: "qrc:////icons//loader-snake-64-w.gif"
|
||||||
|
visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString())
|
||||||
|
playing: visible
|
||||||
|
z: 10000
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,11 +9,15 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtWebView 1.1
|
|
||||||
|
|
||||||
AnimatedImage {
|
Image {
|
||||||
property WebView webview: parent
|
Item {
|
||||||
source: "../../icons/loader-snake-64-w.gif"
|
id: webView
|
||||||
|
property bool loading: false
|
||||||
|
property string url: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
source: "qrc:////images//unsupportedImage.png"
|
||||||
visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString())
|
visible: webview.loading && /^(http.*|)$/i.test(webview.url.toString())
|
||||||
playing: visible
|
playing: visible
|
||||||
z: 10000
|
z: 10000
|
||||||
|
|
44
interface/resources/qml/hifi/+webengine/DesktopWebEngine.qml
Normal file
44
interface/resources/qml/hifi/+webengine/DesktopWebEngine.qml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtWebEngine 1.5
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool webViewProfileSetup: false
|
||||||
|
property string currentUrl: ""
|
||||||
|
property string downloadUrl: ""
|
||||||
|
property string adaptedPath: ""
|
||||||
|
property string tempDir: ""
|
||||||
|
function setupWebEngineSettings() {
|
||||||
|
WebEngine.settings.javascriptCanOpenWindows = true;
|
||||||
|
WebEngine.settings.javascriptCanAccessClipboard = false;
|
||||||
|
WebEngine.settings.spatialNavigationEnabled = false;
|
||||||
|
WebEngine.settings.localContentCanAccessRemoteUrls = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initWebviewProfileHandlers(profile) {
|
||||||
|
downloadUrl = currentUrl;
|
||||||
|
if (webViewProfileSetup) return;
|
||||||
|
webViewProfileSetup = true;
|
||||||
|
|
||||||
|
profile.downloadRequested.connect(function(download){
|
||||||
|
adaptedPath = File.convertUrlToPath(downloadUrl);
|
||||||
|
tempDir = File.getTempDir();
|
||||||
|
download.path = tempDir + "/" + adaptedPath;
|
||||||
|
download.accept();
|
||||||
|
if (download.state === WebEngineDownloadItem.DownloadInterrupted) {
|
||||||
|
console.log("download failed to complete");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
profile.downloadFinished.connect(function(download){
|
||||||
|
if (download.state === WebEngineDownloadItem.DownloadCompleted) {
|
||||||
|
File.runUnzip(download.path, downloadUrl, autoAdd);
|
||||||
|
} else {
|
||||||
|
console.log("The download was corrupted, state: " + download.state);
|
||||||
|
}
|
||||||
|
autoAdd = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtWebView 1.1
|
|
||||||
import Qt.labs.settings 1.0 as QtSettings
|
import Qt.labs.settings 1.0 as QtSettings
|
||||||
|
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
|
@ -87,15 +86,21 @@ OriginalDesktop.Desktop {
|
||||||
return map;
|
return map;
|
||||||
})({});
|
})({});
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
webEngineConfig.setupWebEngineSettings();
|
||||||
|
}
|
||||||
|
|
||||||
// Accept a download through the webview
|
// Accept a download through the webview
|
||||||
property bool webViewProfileSetup: false
|
property alias webViewProfileSetup: webEngineConfig.webViewProfileSetup
|
||||||
property string currentUrl: ""
|
property alias currentUrl: webEngineConfig.currentUrl
|
||||||
property string downloadUrl: ""
|
property alias downloadUrl: webEngineConfig.downloadUrl
|
||||||
property string adaptedPath: ""
|
property alias adaptedPath: webEngineConfig.adaptedPath
|
||||||
property string tempDir: ""
|
property alias tempDir: webEngineConfig.tempDir
|
||||||
|
property var initWebviewProfileHandlers: webEngineConfig.initWebviewProfileHandlers
|
||||||
property bool autoAdd: false
|
property bool autoAdd: false
|
||||||
|
|
||||||
function initWebviewProfileHandlers(profile) {
|
DesktopWebEngine {
|
||||||
|
id: webEngineConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAutoAdd(auto) {
|
function setAutoAdd(auto) {
|
||||||
|
|
17
interface/resources/qml/hifi/DesktopWebEngine.qml
Normal file
17
interface/resources/qml/hifi/DesktopWebEngine.qml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import QtQuick 2.7
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool webViewProfileSetup: false
|
||||||
|
property string currentUrl: ""
|
||||||
|
property string downloadUrl: ""
|
||||||
|
property string adaptedPath: ""
|
||||||
|
property string tempDir: ""
|
||||||
|
function setupWebEngineSettings() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initWebviewProfileHandlers(profile) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,358 +0,0 @@
|
||||||
|
|
||||||
//
|
|
||||||
// WebBrowser.qml
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// Created by Vlad Stelmahovsky on 06/22/2017
|
|
||||||
// Copyright 2017 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
|
|
||||||
//
|
|
||||||
|
|
||||||
import QtQuick 2.7
|
|
||||||
import QtQuick.Controls 2.2 as QQControls
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
|
|
||||||
import QtWebView 1.1
|
|
||||||
import QtWebChannel 1.0
|
|
||||||
|
|
||||||
import stylesUit 1.0
|
|
||||||
import controlsUit 1.0 as HifiControls
|
|
||||||
import "../windows"
|
|
||||||
import "../controls"
|
|
||||||
|
|
||||||
import HifiWeb 1.0
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root;
|
|
||||||
|
|
||||||
HifiConstants { id: hifi; }
|
|
||||||
|
|
||||||
property string title: "";
|
|
||||||
signal sendToScript(var message);
|
|
||||||
property bool keyboardEnabled: true // FIXME - Keyboard HMD only: Default to false
|
|
||||||
property bool keyboardRaised: false
|
|
||||||
property bool punctuationMode: false
|
|
||||||
property var suggestionsList: []
|
|
||||||
readonly property string searchUrlTemplate: "https://www.google.com/search?client=hifibrowser&q=";
|
|
||||||
|
|
||||||
|
|
||||||
WebBrowserSuggestionsEngine {
|
|
||||||
id: searchEngine
|
|
||||||
|
|
||||||
onSuggestions: {
|
|
||||||
if (suggestions.length > 0) {
|
|
||||||
suggestionsList = []
|
|
||||||
suggestionsList.push(addressBarInput.text); //do not overwrite edit text
|
|
||||||
for(var i = 0; i < suggestions.length; i++) {
|
|
||||||
suggestionsList.push(suggestions[i]);
|
|
||||||
}
|
|
||||||
addressBar.model = suggestionsList
|
|
||||||
if (!addressBar.popup.visible) {
|
|
||||||
addressBar.popup.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: suggestionRequestTimer
|
|
||||||
interval: 200
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
if (addressBar.editText !== "") {
|
|
||||||
searchEngine.querySuggestions(addressBarInput.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
color: hifi.colors.baseGray;
|
|
||||||
|
|
||||||
function goTo(url) {
|
|
||||||
//must be valid attempt to open an site with dot
|
|
||||||
var urlNew = url
|
|
||||||
if (url.indexOf(".") > 0) {
|
|
||||||
if (url.indexOf("http") < 0) {
|
|
||||||
urlNew = "http://" + url;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
urlNew = searchUrlTemplate + url
|
|
||||||
}
|
|
||||||
|
|
||||||
addressBar.model = []
|
|
||||||
//need to rebind if binfing was broken by selecting from suggestions
|
|
||||||
addressBar.editText = Qt.binding( function() { return webStack.currentItem.webEngineView.url; });
|
|
||||||
webStack.currentItem.webEngineView.url = urlNew
|
|
||||||
suggestionRequestTimer.stop();
|
|
||||||
addressBar.popup.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
spacing: 2
|
|
||||||
width: parent.width;
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: addressBarRow
|
|
||||||
width: parent.width;
|
|
||||||
height: 48
|
|
||||||
|
|
||||||
HifiControls.WebGlyphButton {
|
|
||||||
enabled: webStack.currentItem.webEngineView.canGoBack || webStack.depth > 1
|
|
||||||
glyph: hifi.glyphs.backward;
|
|
||||||
anchors.verticalCenter: parent.verticalCenter;
|
|
||||||
size: 38;
|
|
||||||
onClicked: {
|
|
||||||
if (webStack.currentItem.webEngineView.canGoBack) {
|
|
||||||
webStack.currentItem.webEngineView.goBack();
|
|
||||||
} else if (webStack.depth > 1) {
|
|
||||||
webStack.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HifiControls.WebGlyphButton {
|
|
||||||
enabled: webStack.currentItem.webEngineView.canGoForward
|
|
||||||
glyph: hifi.glyphs.forward;
|
|
||||||
anchors.verticalCenter: parent.verticalCenter;
|
|
||||||
size: 38;
|
|
||||||
onClicked: {
|
|
||||||
webStack.currentItem.webEngineView.goForward();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QQControls.ComboBox {
|
|
||||||
id: addressBar
|
|
||||||
|
|
||||||
//selectByMouse: true
|
|
||||||
focus: true
|
|
||||||
|
|
||||||
editable: true
|
|
||||||
//flat: true
|
|
||||||
indicator: Item {}
|
|
||||||
background: Item {}
|
|
||||||
onActivated: {
|
|
||||||
goTo(textAt(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
onHighlightedIndexChanged: {
|
|
||||||
if (highlightedIndex >= 0) {
|
|
||||||
addressBar.editText = textAt(highlightedIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
popup.height: webStack.height
|
|
||||||
|
|
||||||
onFocusChanged: {
|
|
||||||
if (focus) {
|
|
||||||
addressBarInput.selectAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: QQControls.TextField {
|
|
||||||
id: addressBarInput
|
|
||||||
leftPadding: 26
|
|
||||||
rightPadding: hifi.dimensions.controlLineHeight + 5
|
|
||||||
text: addressBar.editText
|
|
||||||
placeholderText: qsTr("Enter URL")
|
|
||||||
font: addressBar.font
|
|
||||||
selectByMouse: true
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
onFocusChanged: {
|
|
||||||
if (focus) {
|
|
||||||
selectAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onDeletePressed: {
|
|
||||||
addressBarInput.text = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: {
|
|
||||||
if (event.key === Qt.Key_Return) {
|
|
||||||
goTo(addressBarInput.text);
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter;
|
|
||||||
x: 5
|
|
||||||
z: 2
|
|
||||||
id: faviconImage
|
|
||||||
width: 16; height: 16
|
|
||||||
sourceSize: Qt.size(width, height)
|
|
||||||
source: webStack.currentItem.webEngineView.icon
|
|
||||||
}
|
|
||||||
|
|
||||||
HifiControls.WebGlyphButton {
|
|
||||||
glyph: webStack.currentItem.WebView.Loading ? hifi.glyphs.closeSmall : hifi.glyphs.reloadSmall;
|
|
||||||
anchors.verticalCenter: parent.verticalCenter;
|
|
||||||
width: hifi.dimensions.controlLineHeight
|
|
||||||
z: 2
|
|
||||||
x: addressBarInput.width - implicitWidth
|
|
||||||
onClicked: {
|
|
||||||
if (webStack.currentItem.WebView.Loading) {
|
|
||||||
webStack.currentItem.webEngineView.stop();
|
|
||||||
} else {
|
|
||||||
webStack.currentItem.reloadTimer.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i");
|
|
||||||
|
|
||||||
Keys.onPressed: {
|
|
||||||
if (event.key === Qt.Key_Return) {
|
|
||||||
goTo(addressBarInput.text);
|
|
||||||
event.accepted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onEditTextChanged: {
|
|
||||||
if (addressBar.editText !== "" && addressBar.editText !== webStack.currentItem.webEngineView.url.toString()) {
|
|
||||||
suggestionRequestTimer.restart();
|
|
||||||
} else {
|
|
||||||
addressBar.model = []
|
|
||||||
addressBar.popup.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
editText: webStack.currentItem.webEngineView.url
|
|
||||||
onAccepted: goTo(addressBarInput.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
HifiControls.WebGlyphButton {
|
|
||||||
checkable: true
|
|
||||||
checked: webStack.currentItem.webEngineView.audioMuted
|
|
||||||
glyph: checked ? hifi.glyphs.unmuted : hifi.glyphs.muted
|
|
||||||
anchors.verticalCenter: parent.verticalCenter;
|
|
||||||
width: hifi.dimensions.controlLineHeight
|
|
||||||
onClicked: {
|
|
||||||
webStack.currentItem.webEngineView.audioMuted = !webStack.currentItem.webEngineView.audioMuted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QQControls.ProgressBar {
|
|
||||||
id: loadProgressBar
|
|
||||||
background: Rectangle {
|
|
||||||
implicitHeight: 2
|
|
||||||
color: "#6A6A6A"
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
implicitHeight: 2
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: loadProgressBar.visualPosition * parent.width
|
|
||||||
height: parent.height
|
|
||||||
color: "#00B4EF"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
width: parent.width;
|
|
||||||
from: 0
|
|
||||||
to: 100
|
|
||||||
value: webStack.currentItem.WebView.LoadProgress
|
|
||||||
height: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: webViewComponent
|
|
||||||
Rectangle {
|
|
||||||
property alias webEngineView: webEngineView
|
|
||||||
property alias reloadTimer: reloadTimer
|
|
||||||
|
|
||||||
property var request: null
|
|
||||||
|
|
||||||
property bool isDialog: QQControls.StackView.index > 0
|
|
||||||
property real margins: isDialog ? 10 : 0
|
|
||||||
|
|
||||||
color: "#d1d1d1"
|
|
||||||
|
|
||||||
QQControls.StackView.onActivated: {
|
|
||||||
addressBar.editText = Qt.binding( function() { return webStack.currentItem.webEngineView.url; });
|
|
||||||
}
|
|
||||||
|
|
||||||
onRequestChanged: {
|
|
||||||
if (isDialog && request !== null && request !== undefined) {//is Dialog ?
|
|
||||||
request.openIn(webEngineView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HifiControls.BaseWebView {
|
|
||||||
id: webEngineView
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: parent.margins
|
|
||||||
|
|
||||||
layer.enabled: parent.isDialog
|
|
||||||
layer.effect: DropShadow {
|
|
||||||
verticalOffset: 8
|
|
||||||
horizontalOffset: 8
|
|
||||||
color: "#330066ff"
|
|
||||||
samples: 10
|
|
||||||
spread: 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
focus: true
|
|
||||||
objectName: "tabletWebEngineView"
|
|
||||||
|
|
||||||
profile.httpUserAgent: "Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0"
|
|
||||||
|
|
||||||
property string userScriptUrl: ""
|
|
||||||
|
|
||||||
onLoadingChanged: {
|
|
||||||
if (!loading) {
|
|
||||||
addressBarInput.cursorPosition = 0 //set input field cursot to beginning
|
|
||||||
suggestionRequestTimer.stop();
|
|
||||||
addressBar.popup.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onLinkHovered: {
|
|
||||||
//TODO: change cursor shape?
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
webChannel.registerObject("eventBridge", eventBridge);
|
|
||||||
webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Timer {
|
|
||||||
id: reloadTimer
|
|
||||||
interval: 0
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
onTriggered: webEngineView.reload()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QQControls.StackView {
|
|
||||||
id: webStack
|
|
||||||
width: parent.width;
|
|
||||||
property real webViewHeight: root.height - loadProgressBar.height - 48 - 4
|
|
||||||
height: keyboardEnabled && keyboardRaised ? webViewHeight - keyboard.height : webViewHeight
|
|
||||||
|
|
||||||
Component.onCompleted: webStack.push(webViewComponent, {"webEngineView.url": "https://www.highfidelity.com"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
HifiControls.Keyboard {
|
|
||||||
id: keyboard
|
|
||||||
raised: parent.keyboardEnabled && parent.keyboardRaised
|
|
||||||
numeric: parent.punctuationMode
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
bottom: parent.bottom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,7 +16,13 @@ import TabletScriptingInterface 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
readonly property var level: AudioScriptingInterface.inputLevel;
|
readonly property var level: AudioScriptingInterface.inputLevel;
|
||||||
|
|
||||||
|
property bool gated: false;
|
||||||
|
Component.onCompleted: {
|
||||||
|
AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; });
|
||||||
|
AudioScriptingInterface.noiseGateClosed.connect(function() { gated = true; });
|
||||||
|
}
|
||||||
|
|
||||||
property bool standalone: false;
|
property bool standalone: false;
|
||||||
property var dragTarget: null;
|
property var dragTarget: null;
|
||||||
|
|
||||||
|
@ -77,6 +83,7 @@ Rectangle {
|
||||||
readonly property string gutter: "#575757";
|
readonly property string gutter: "#575757";
|
||||||
readonly property string greenStart: "#39A38F";
|
readonly property string greenStart: "#39A38F";
|
||||||
readonly property string greenEnd: "#1FC6A6";
|
readonly property string greenEnd: "#1FC6A6";
|
||||||
|
readonly property string yellow: "#C0C000";
|
||||||
readonly property string red: colors.muted;
|
readonly property string red: colors.muted;
|
||||||
readonly property string fill: "#55000000";
|
readonly property string fill: "#55000000";
|
||||||
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
|
readonly property string border: standalone ? "#80FFFFFF" : "#55FFFFFF";
|
||||||
|
@ -189,7 +196,7 @@ Rectangle {
|
||||||
|
|
||||||
Rectangle { // mask
|
Rectangle { // mask
|
||||||
id: mask;
|
id: mask;
|
||||||
width: parent.width * level;
|
width: gated ? 0 : parent.width * level;
|
||||||
radius: 5;
|
radius: 5;
|
||||||
anchors {
|
anchors {
|
||||||
bottom: parent.bottom;
|
bottom: parent.bottom;
|
||||||
|
@ -212,18 +219,42 @@ Rectangle {
|
||||||
color: colors.greenStart;
|
color: colors.greenStart;
|
||||||
}
|
}
|
||||||
GradientStop {
|
GradientStop {
|
||||||
position: 0.8;
|
position: 0.5;
|
||||||
color: colors.greenEnd;
|
color: colors.greenEnd;
|
||||||
}
|
}
|
||||||
GradientStop {
|
|
||||||
position: 0.81;
|
|
||||||
color: colors.red;
|
|
||||||
}
|
|
||||||
GradientStop {
|
GradientStop {
|
||||||
position: 1;
|
position: 1;
|
||||||
color: colors.red;
|
color: colors.yellow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: gatedIndicator;
|
||||||
|
visible: gated && !AudioScriptingInterface.clipping
|
||||||
|
|
||||||
|
radius: 4;
|
||||||
|
width: 2 * radius;
|
||||||
|
height: 2 * radius;
|
||||||
|
color: "#0080FF";
|
||||||
|
anchors {
|
||||||
|
right: parent.left;
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: clippingIndicator;
|
||||||
|
visible: AudioScriptingInterface.clipping
|
||||||
|
|
||||||
|
radius: 4;
|
||||||
|
width: 2 * radius;
|
||||||
|
height: 2 * radius;
|
||||||
|
color: colors.red;
|
||||||
|
anchors {
|
||||||
|
left: parent.right;
|
||||||
|
verticalCenter: parent.verticalCenter;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,8 @@ Rectangle {
|
||||||
property bool keyboardEnabled: HMD.active
|
property bool keyboardEnabled: HMD.active
|
||||||
property bool keyboardRaised: false
|
property bool keyboardRaised: false
|
||||||
property string searchScopeString: "Featured"
|
property string searchScopeString: "Featured"
|
||||||
property bool isLoggedIn: false;
|
property bool isLoggedIn: false
|
||||||
property bool supports3DHTML: true;
|
property bool supports3DHTML: true
|
||||||
|
|
||||||
anchors.fill: (typeof parent === undefined) ? undefined : parent
|
anchors.fill: (typeof parent === undefined) ? undefined : parent
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ Rectangle {
|
||||||
licenseInfo.visible = false;
|
licenseInfo.visible = false;
|
||||||
marketBrowseModel.getFirstPage();
|
marketBrowseModel.getFirstPage();
|
||||||
{
|
{
|
||||||
if(root.searchString !== undefined && root.searchString !== "") {
|
if(root.searchString !== undefined && root.searchString !== "") {
|
||||||
root.searchScopeString = "Search Results: \"" + root.searchString + "\"";
|
root.searchScopeString = "Search Results: \"" + root.searchString + "\"";
|
||||||
} else if (root.categoryString !== "") {
|
} else if (root.categoryString !== "") {
|
||||||
root.searchScopeString = root.categoryString;
|
root.searchScopeString = root.categoryString;
|
||||||
|
@ -498,7 +498,7 @@ Rectangle {
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
root.sortString,
|
root.sortString,
|
||||||
false,
|
WalletScriptingInterface.limitedCommerce,
|
||||||
marketBrowseModel.currentPageToRetrieve,
|
marketBrowseModel.currentPageToRetrieve,
|
||||||
marketBrowseModel.itemsPerPage
|
marketBrowseModel.itemsPerPage
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
import Hifi 1.0 as Hifi
|
import Hifi 1.0 as Hifi
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.2
|
import QtQuick.Controls 2.2
|
||||||
import QtGraphicalEffects 1.0
|
|
||||||
import QtWebEngine 1.5
|
|
||||||
import stylesUit 1.0
|
import stylesUit 1.0
|
||||||
import controlsUit 1.0 as HifiControlsUit
|
import controlsUit 1.0 as HifiControlsUit
|
||||||
import "../../../controls" as HifiControls
|
import "../../../controls" as HifiControls
|
||||||
|
@ -424,13 +422,13 @@ Rectangle {
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
size: 14
|
size: 14
|
||||||
|
|
||||||
color: model.link ? hifi.colors.blueHighlight : hifi.colors.baseGray
|
color: (model.link && root.supports3DHTML)? hifi.colors.blueHighlight : hifi.colors.baseGray
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (model.link) {
|
if (model.link && root.supports3DHTML) {
|
||||||
sendToScript({method: 'marketplace_open_link', link: model.link});
|
sendToScript({method: 'marketplace_open_link', link: model.link});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -571,12 +569,14 @@ Rectangle {
|
||||||
text: root.description
|
text: root.description
|
||||||
size: 14
|
size: 14
|
||||||
color: hifi.colors.lightGray
|
color: hifi.colors.lightGray
|
||||||
linkColor: hifi.colors.blueHighlight
|
linkColor: root.supports3DHTML ? hifi.colors.blueHighlight : hifi.colors.lightGray
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
onLinkActivated: {
|
onLinkActivated: {
|
||||||
sendToScript({method: 'marketplace_open_link', link: link});
|
if (root.supports3DHTML) {
|
||||||
|
sendToScript({method: 'marketplace_open_link', link: link});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onHeightChanged: { footer.evalHeight(); }
|
onHeightChanged: { footer.evalHeight(); }
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import QtWebView 1.1
|
|
||||||
|
|
||||||
import "../../controls" as Controls
|
import "../../controls" as Controls
|
||||||
|
|
||||||
Controls.TabletWebView {
|
Controls.TabletWebView {
|
||||||
|
|
|
@ -2,7 +2,6 @@ import QtQuick 2.5
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import QtQml 2.2
|
import QtQml 2.2
|
||||||
import QtWebChannel 1.0
|
|
||||||
|
|
||||||
import "."
|
import "."
|
||||||
import stylesUit 1.0
|
import stylesUit 1.0
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
|
|
||||||
import "../../controls" as Controls
|
import "../../controls" as Controls
|
||||||
|
|
||||||
Controls.TabletWebScreen {
|
Controls.TabletWebScreen {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
|
|
||||||
import "../../controls" as Controls
|
import "../../controls" as Controls
|
||||||
|
|
||||||
Controls.WebView {
|
Controls.WebView {
|
||||||
|
|
|
@ -4980,10 +4980,10 @@ void Application::idle() {
|
||||||
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
||||||
// details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing
|
// details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing
|
||||||
// details normally.
|
// details normally.
|
||||||
#ifndef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
bool showWarnings = getLogger()->extraDebugging();
|
|
||||||
#else
|
|
||||||
bool showWarnings = false;
|
bool showWarnings = false;
|
||||||
|
#else
|
||||||
|
bool showWarnings = getLogger()->extraDebugging();
|
||||||
#endif
|
#endif
|
||||||
PerformanceWarning warn(showWarnings, "idle()");
|
PerformanceWarning warn(showWarnings, "idle()");
|
||||||
|
|
||||||
|
@ -8328,7 +8328,7 @@ void Application::toggleLogDialog() {
|
||||||
bool keepOnTop =_keepLogWindowOnTop.get();
|
bool keepOnTop =_keepLogWindowOnTop.get();
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
_logDialog = new LogDialog(keepOnTop ? qApp->getWindow() : nullptr, getLogger());
|
_logDialog = new LogDialog(keepOnTop ? qApp->getWindow() : nullptr, getLogger());
|
||||||
#else
|
#elif !defined(Q_OS_ANDROID)
|
||||||
_logDialog = new LogDialog(nullptr, getLogger());
|
_logDialog = new LogDialog(nullptr, getLogger());
|
||||||
|
|
||||||
if (keepOnTop) {
|
if (keepOnTop) {
|
||||||
|
|
|
@ -294,13 +294,6 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename
|
||||||
}
|
}
|
||||||
|
|
||||||
mapping.insert(JOINT_FIELD, joints);
|
mapping.insert(JOINT_FIELD, joints);
|
||||||
|
|
||||||
if (!mapping.contains(FREE_JOINT_FIELD)) {
|
|
||||||
mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm");
|
|
||||||
mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm");
|
|
||||||
mapping.insertMulti(FREE_JOINT_FIELD, "RightArm");
|
|
||||||
mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are no blendshape mappings, and we detect that this is likely a mixamo file,
|
// If there are no blendshape mappings, and we detect that this is likely a mixamo file,
|
||||||
// then we can add the default mixamo to "faceshift" mappings
|
// then we can add the default mixamo to "faceshift" mappings
|
||||||
|
|
|
@ -58,11 +58,6 @@ _hfmModel(hfmModel)
|
||||||
form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
|
form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
|
||||||
form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
|
form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
|
||||||
|
|
||||||
form->addRow("Free Joints:", _freeJoints = new QVBoxLayout());
|
|
||||||
QPushButton* newFreeJoint = new QPushButton("New Free Joint");
|
|
||||||
_freeJoints->addWidget(newFreeJoint);
|
|
||||||
connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint()));
|
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
|
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
|
||||||
QDialogButtonBox::Cancel | QDialogButtonBox::Reset);
|
QDialogButtonBox::Cancel | QDialogButtonBox::Reset);
|
||||||
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
|
||||||
|
@ -102,11 +97,6 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
|
||||||
insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
|
insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
|
||||||
insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
|
insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
|
||||||
|
|
||||||
mapping.remove(FREE_JOINT_FIELD);
|
|
||||||
for (int i = 0; i < _freeJoints->count() - 1; i++) {
|
|
||||||
QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget());
|
|
||||||
mapping.insertMulti(FREE_JOINT_FIELD, box->currentText());
|
|
||||||
}
|
|
||||||
mapping.insert(JOINT_FIELD, joints);
|
mapping.insert(JOINT_FIELD, joints);
|
||||||
|
|
||||||
return mapping;
|
return mapping;
|
||||||
|
@ -133,16 +123,6 @@ void ModelPropertiesDialog::reset() {
|
||||||
setJointText(_headJoint, jointHash.value("jointHead").toString());
|
setJointText(_headJoint, jointHash.value("jointHead").toString());
|
||||||
setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
|
setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
|
||||||
setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
|
setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
|
||||||
|
|
||||||
while (_freeJoints->count() > 1) {
|
|
||||||
delete _freeJoints->itemAt(0)->widget();
|
|
||||||
}
|
|
||||||
foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) {
|
|
||||||
QString jointName = joint.toString();
|
|
||||||
if (_hfmModel.jointIndices.contains(jointName)) {
|
|
||||||
createNewFreeJoint(jointName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelPropertiesDialog::chooseTextureDirectory() {
|
void ModelPropertiesDialog::chooseTextureDirectory() {
|
||||||
|
@ -176,20 +156,6 @@ void ModelPropertiesDialog::updatePivotJoint() {
|
||||||
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
|
_pivotJoint->setEnabled(!_pivotAboutCenter->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelPropertiesDialog::createNewFreeJoint(const QString& joint) {
|
|
||||||
QWidget* freeJoint = new QWidget();
|
|
||||||
QHBoxLayout* freeJointLayout = new QHBoxLayout();
|
|
||||||
freeJointLayout->setContentsMargins(QMargins());
|
|
||||||
freeJoint->setLayout(freeJointLayout);
|
|
||||||
QComboBox* jointBox = createJointBox(false);
|
|
||||||
jointBox->setCurrentText(joint);
|
|
||||||
freeJointLayout->addWidget(jointBox, 1);
|
|
||||||
QPushButton* deleteJoint = new QPushButton("Delete");
|
|
||||||
freeJointLayout->addWidget(deleteJoint);
|
|
||||||
freeJoint->connect(deleteJoint, SIGNAL(clicked(bool)), SLOT(deleteLater()));
|
|
||||||
_freeJoints->insertWidget(_freeJoints->count() - 1, freeJoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const {
|
QComboBox* ModelPropertiesDialog::createJointBox(bool withNone) const {
|
||||||
QComboBox* box = new QComboBox();
|
QComboBox* box = new QComboBox();
|
||||||
if (withNone) {
|
if (withNone) {
|
||||||
|
|
|
@ -39,7 +39,6 @@ private slots:
|
||||||
void chooseTextureDirectory();
|
void chooseTextureDirectory();
|
||||||
void chooseScriptDirectory();
|
void chooseScriptDirectory();
|
||||||
void updatePivotJoint();
|
void updatePivotJoint();
|
||||||
void createNewFreeJoint(const QString& joint = QString());
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QComboBox* createJointBox(bool withNone = true) const;
|
QComboBox* createJointBox(bool withNone = true) const;
|
||||||
|
@ -66,7 +65,6 @@ private:
|
||||||
QComboBox* _headJoint = nullptr;
|
QComboBox* _headJoint = nullptr;
|
||||||
QComboBox* _leftHandJoint = nullptr;
|
QComboBox* _leftHandJoint = nullptr;
|
||||||
QComboBox* _rightHandJoint = nullptr;
|
QComboBox* _rightHandJoint = nullptr;
|
||||||
QVBoxLayout* _freeJoints = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ModelPropertiesDialog_h
|
#endif // hifi_ModelPropertiesDialog_h
|
||||||
|
|
|
@ -569,7 +569,7 @@ void AvatarActionHold::lateAvatarUpdate(const AnimPose& prePhysicsRoomPose, cons
|
||||||
}
|
}
|
||||||
|
|
||||||
btTransform worldTrans = rigidBody->getWorldTransform();
|
btTransform worldTrans = rigidBody->getWorldTransform();
|
||||||
AnimPose worldBodyPose(glm::vec3(1), bulletToGLM(worldTrans.getRotation()), bulletToGLM(worldTrans.getOrigin()));
|
AnimPose worldBodyPose(1.0f, bulletToGLM(worldTrans.getRotation()), bulletToGLM(worldTrans.getOrigin()));
|
||||||
|
|
||||||
// transform the body transform into sensor space with the prePhysics sensor-to-world matrix.
|
// transform the body transform into sensor space with the prePhysics sensor-to-world matrix.
|
||||||
// then transform it back into world uisng the postAvatarUpdate sensor-to-world matrix.
|
// then transform it back into world uisng the postAvatarUpdate sensor-to-world matrix.
|
||||||
|
|
|
@ -652,28 +652,25 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
||||||
|
|
||||||
PROFILE_RANGE(simulation_physics, __FUNCTION__);
|
PROFILE_RANGE(simulation_physics, __FUNCTION__);
|
||||||
|
|
||||||
float distance = (float)INT_MAX; // with FLT_MAX bullet rayTest does not return results
|
float bulletDistance = (float)INT_MAX; // with FLT_MAX bullet rayTest does not return results
|
||||||
glm::vec3 rayDirection = glm::normalize(ray.direction);
|
glm::vec3 rayDirection = glm::normalize(ray.direction);
|
||||||
std::vector<MyCharacterController::RayAvatarResult> physicsResults = _myAvatar->getCharacterController()->rayTest(glmToBullet(ray.origin), glmToBullet(rayDirection), distance, QVector<uint>());
|
std::vector<MyCharacterController::RayAvatarResult> physicsResults = _myAvatar->getCharacterController()->rayTest(glmToBullet(ray.origin), glmToBullet(rayDirection), bulletDistance, QVector<uint>());
|
||||||
if (physicsResults.size() > 0) {
|
if (physicsResults.size() > 0) {
|
||||||
glm::vec3 rayDirectionInv = { rayDirection.x != 0.0f ? 1.0f / rayDirection.x : INFINITY,
|
glm::vec3 rayDirectionInv = { rayDirection.x != 0.0f ? 1.0f / rayDirection.x : INFINITY,
|
||||||
rayDirection.y != 0.0f ? 1.0f / rayDirection.y : INFINITY,
|
rayDirection.y != 0.0f ? 1.0f / rayDirection.y : INFINITY,
|
||||||
rayDirection.z != 0.0f ? 1.0f / rayDirection.z : INFINITY };
|
rayDirection.z != 0.0f ? 1.0f / rayDirection.z : INFINITY };
|
||||||
|
|
||||||
MyCharacterController::RayAvatarResult rayAvatarResult;
|
|
||||||
AvatarPointer avatar = nullptr;
|
|
||||||
|
|
||||||
BoxFace face = BoxFace::UNKNOWN_FACE;
|
|
||||||
glm::vec3 surfaceNormal;
|
|
||||||
QVariantMap extraInfo;
|
|
||||||
|
|
||||||
for (auto &hit : physicsResults) {
|
for (auto &hit : physicsResults) {
|
||||||
auto avatarID = hit._intersectWithAvatar;
|
auto avatarID = hit._intersectWithAvatar;
|
||||||
if ((avatarsToInclude.size() > 0 && !avatarsToInclude.contains(avatarID)) ||
|
if ((avatarsToInclude.size() > 0 && !avatarsToInclude.contains(avatarID)) ||
|
||||||
(avatarsToDiscard.size() > 0 && avatarsToDiscard.contains(avatarID))) {
|
(avatarsToDiscard.size() > 0 && avatarsToDiscard.contains(avatarID))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MyCharacterController::RayAvatarResult rayAvatarResult;
|
||||||
|
BoxFace face = BoxFace::UNKNOWN_FACE;
|
||||||
|
QVariantMap extraInfo;
|
||||||
|
AvatarPointer avatar = nullptr;
|
||||||
if (_myAvatar->getSessionUUID() != avatarID) {
|
if (_myAvatar->getSessionUUID() != avatarID) {
|
||||||
auto avatarMap = getHashCopy();
|
auto avatarMap = getHashCopy();
|
||||||
AvatarHash::iterator itr = avatarMap.find(avatarID);
|
AvatarHash::iterator itr = avatarMap.find(avatarID);
|
||||||
|
@ -683,46 +680,45 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
||||||
} else {
|
} else {
|
||||||
avatar = _myAvatar;
|
avatar = _myAvatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hit._isBound) {
|
if (!hit._isBound) {
|
||||||
rayAvatarResult = hit;
|
rayAvatarResult = hit;
|
||||||
} else if (avatar) {
|
} else if (avatar) {
|
||||||
auto &multiSpheres = avatar->getMultiSphereShapes();
|
auto &multiSpheres = avatar->getMultiSphereShapes();
|
||||||
if (multiSpheres.size() > 0) {
|
if (multiSpheres.size() > 0) {
|
||||||
std::vector<MyCharacterController::RayAvatarResult> boxHits;
|
MyCharacterController::RayAvatarResult boxHit;
|
||||||
|
boxHit._distance = FLT_MAX;
|
||||||
|
|
||||||
for (size_t i = 0; i < hit._boundJoints.size(); i++) {
|
for (size_t i = 0; i < hit._boundJoints.size(); i++) {
|
||||||
assert(hit._boundJoints[i] < multiSpheres.size());
|
assert(hit._boundJoints[i] < multiSpheres.size());
|
||||||
auto &mSphere = multiSpheres[hit._boundJoints[i]];
|
auto &mSphere = multiSpheres[hit._boundJoints[i]];
|
||||||
if (mSphere.isValid()) {
|
if (mSphere.isValid()) {
|
||||||
float boundDistance = FLT_MAX;
|
float boundDistance = FLT_MAX;
|
||||||
BoxFace face;
|
BoxFace boundFace = BoxFace::UNKNOWN_FACE;
|
||||||
glm::vec3 surfaceNormal;
|
glm::vec3 boundSurfaceNormal;
|
||||||
auto &bbox = mSphere.getBoundingBox();
|
auto &bbox = mSphere.getBoundingBox();
|
||||||
if (bbox.findRayIntersection(ray.origin, rayDirection, rayDirectionInv, boundDistance, face, surfaceNormal)) {
|
if (bbox.findRayIntersection(ray.origin, rayDirection, rayDirectionInv, boundDistance, boundFace, boundSurfaceNormal)) {
|
||||||
MyCharacterController::RayAvatarResult boxHit;
|
if (boundDistance < boxHit._distance) {
|
||||||
boxHit._distance = boundDistance;
|
boxHit._intersect = true;
|
||||||
boxHit._intersect = true;
|
boxHit._intersectWithAvatar = avatarID;
|
||||||
boxHit._intersectionNormal = surfaceNormal;
|
boxHit._intersectWithJoint = mSphere.getJointIndex();
|
||||||
boxHit._intersectionPoint = ray.origin + boundDistance * rayDirection;
|
boxHit._distance = boundDistance;
|
||||||
boxHit._intersectWithAvatar = avatarID;
|
boxHit._intersectionPoint = ray.origin + boundDistance * rayDirection;
|
||||||
boxHit._intersectWithJoint = mSphere.getJointIndex();
|
boxHit._intersectionNormal = boundSurfaceNormal;
|
||||||
boxHits.push_back(boxHit);
|
face = boundFace;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (boxHits.size() > 0) {
|
if (boxHit._distance < FLT_MAX) {
|
||||||
if (boxHits.size() > 1) {
|
rayAvatarResult = boxHit;
|
||||||
std::sort(boxHits.begin(), boxHits.end(), [](const MyCharacterController::RayAvatarResult& hitA,
|
|
||||||
const MyCharacterController::RayAvatarResult& hitB) {
|
|
||||||
return hitA._distance < hitB._distance;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
rayAvatarResult = boxHits[0];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pickAgainstMesh) {
|
|
||||||
|
if (rayAvatarResult._intersect && pickAgainstMesh) {
|
||||||
glm::vec3 localRayOrigin = avatar->worldToJointPoint(ray.origin, rayAvatarResult._intersectWithJoint);
|
glm::vec3 localRayOrigin = avatar->worldToJointPoint(ray.origin, rayAvatarResult._intersectWithJoint);
|
||||||
glm::vec3 localRayPoint = avatar->worldToJointPoint(ray.origin + rayDirection, rayAvatarResult._intersectWithJoint);
|
glm::vec3 localRayPoint = avatar->worldToJointPoint(ray.origin + rayAvatarResult._distance * rayDirection, rayAvatarResult._intersectWithJoint);
|
||||||
|
|
||||||
auto avatarOrientation = avatar->getWorldOrientation();
|
auto avatarOrientation = avatar->getWorldOrientation();
|
||||||
auto avatarPosition = avatar->getWorldPosition();
|
auto avatarPosition = avatar->getWorldPosition();
|
||||||
|
@ -732,31 +728,37 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
|
||||||
|
|
||||||
auto defaultFrameRayOrigin = jointPosition + jointOrientation * localRayOrigin;
|
auto defaultFrameRayOrigin = jointPosition + jointOrientation * localRayOrigin;
|
||||||
auto defaultFrameRayPoint = jointPosition + jointOrientation * localRayPoint;
|
auto defaultFrameRayPoint = jointPosition + jointOrientation * localRayPoint;
|
||||||
auto defaultFrameRayDirection = defaultFrameRayPoint - defaultFrameRayOrigin;
|
auto defaultFrameRayDirection = glm::normalize(defaultFrameRayPoint - defaultFrameRayOrigin);
|
||||||
|
|
||||||
if (avatar->getSkeletonModel()->findRayIntersectionAgainstSubMeshes(defaultFrameRayOrigin, defaultFrameRayDirection, distance, face, surfaceNormal, extraInfo, true, false)) {
|
float subMeshDistance = FLT_MAX;
|
||||||
auto newDistance = glm::length(vec3FromVariant(extraInfo["worldIntersectionPoint"]) - defaultFrameRayOrigin);
|
BoxFace subMeshFace = BoxFace::UNKNOWN_FACE;
|
||||||
rayAvatarResult._distance = newDistance;
|
glm::vec3 subMeshSurfaceNormal;
|
||||||
rayAvatarResult._intersectionPoint = ray.origin + newDistance * rayDirection;
|
QVariantMap subMeshExtraInfo;
|
||||||
rayAvatarResult._intersectionNormal = surfaceNormal;
|
if (avatar->getSkeletonModel()->findRayIntersectionAgainstSubMeshes(defaultFrameRayOrigin, defaultFrameRayDirection, subMeshDistance, subMeshFace, subMeshSurfaceNormal, subMeshExtraInfo, true, false)) {
|
||||||
extraInfo["worldIntersectionPoint"] = vec3toVariant(rayAvatarResult._intersectionPoint);
|
rayAvatarResult._distance = subMeshDistance;
|
||||||
break;
|
rayAvatarResult._intersectionPoint = ray.origin + subMeshDistance * rayDirection;
|
||||||
|
rayAvatarResult._intersectionNormal = subMeshSurfaceNormal;
|
||||||
|
face = subMeshFace;
|
||||||
|
extraInfo = subMeshExtraInfo;
|
||||||
|
} else {
|
||||||
|
rayAvatarResult._intersect = false;
|
||||||
}
|
}
|
||||||
} else if (rayAvatarResult._intersect){
|
}
|
||||||
|
|
||||||
|
if (rayAvatarResult._intersect) {
|
||||||
|
result.intersects = true;
|
||||||
|
result.avatarID = rayAvatarResult._intersectWithAvatar;
|
||||||
|
result.distance = rayAvatarResult._distance;
|
||||||
|
result.face = face;
|
||||||
|
result.intersection = ray.origin + rayAvatarResult._distance * rayDirection;
|
||||||
|
result.surfaceNormal = rayAvatarResult._intersectionNormal;
|
||||||
|
result.jointIndex = rayAvatarResult._intersectWithJoint;
|
||||||
|
result.extraInfo = extraInfo;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rayAvatarResult._intersect) {
|
|
||||||
result.intersects = true;
|
|
||||||
result.avatarID = rayAvatarResult._intersectWithAvatar;
|
|
||||||
result.distance = rayAvatarResult._distance;
|
|
||||||
result.surfaceNormal = rayAvatarResult._intersectionNormal;
|
|
||||||
result.jointIndex = rayAvatarResult._intersectWithJoint;
|
|
||||||
result.intersection = ray.origin + rayAvatarResult._distance * rayDirection;
|
|
||||||
result.extraInfo = extraInfo;
|
|
||||||
result.face = face;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -915,14 +915,9 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
||||||
auto entityTreeRenderer = qApp->getEntities();
|
auto entityTreeRenderer = qApp->getEntities();
|
||||||
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
|
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
|
||||||
if (entityTree) {
|
if (entityTree) {
|
||||||
bool zoneAllowsFlying = true;
|
std::pair<bool, bool> zoneInteractionProperties;
|
||||||
bool collisionlessAllowed = true;
|
|
||||||
entityTree->withWriteLock([&] {
|
entityTree->withWriteLock([&] {
|
||||||
std::shared_ptr<ZoneEntityItem> zone = entityTreeRenderer->myAvatarZone();
|
zoneInteractionProperties = entityTreeRenderer->getZoneInteractionProperties();
|
||||||
if (zone) {
|
|
||||||
zoneAllowsFlying = zone->getFlyingAllowed();
|
|
||||||
collisionlessAllowed = zone->getGhostingAllowed();
|
|
||||||
}
|
|
||||||
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
|
EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender();
|
||||||
forEachDescendant([&](SpatiallyNestablePointer object) {
|
forEachDescendant([&](SpatiallyNestablePointer object) {
|
||||||
// we need to update attached queryAACubes in our own local tree so point-select always works
|
// we need to update attached queryAACubes in our own local tree so point-select always works
|
||||||
|
@ -935,6 +930,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
bool isPhysicsEnabled = qApp->isPhysicsEnabled();
|
bool isPhysicsEnabled = qApp->isPhysicsEnabled();
|
||||||
|
bool zoneAllowsFlying = zoneInteractionProperties.first;
|
||||||
|
bool collisionlessAllowed = zoneInteractionProperties.second;
|
||||||
_characterController.setFlyingAllowed((zoneAllowsFlying && _enableFlying) || !isPhysicsEnabled);
|
_characterController.setFlyingAllowed((zoneAllowsFlying && _enableFlying) || !isPhysicsEnabled);
|
||||||
_characterController.setCollisionlessAllowed(collisionlessAllowed);
|
_characterController.setCollisionlessAllowed(collisionlessAllowed);
|
||||||
}
|
}
|
||||||
|
@ -2983,7 +2980,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
||||||
auto animSkeleton = _skeletonModel->getRig().getAnimSkeleton();
|
auto animSkeleton = _skeletonModel->getRig().getAnimSkeleton();
|
||||||
|
|
||||||
// the rig is in the skeletonModel frame
|
// the rig is in the skeletonModel frame
|
||||||
AnimPose xform(glm::vec3(1), _skeletonModel->getRotation(), _skeletonModel->getTranslation());
|
AnimPose xform(1.0f, _skeletonModel->getRotation(), _skeletonModel->getTranslation());
|
||||||
|
|
||||||
if (_enableDebugDrawDefaultPose && animSkeleton) {
|
if (_enableDebugDrawDefaultPose && animSkeleton) {
|
||||||
glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f);
|
glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f);
|
||||||
|
@ -3028,7 +3025,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
||||||
updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose);
|
updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose);
|
||||||
|
|
||||||
if (_enableDebugDrawDetailedCollision) {
|
if (_enableDebugDrawDetailedCollision) {
|
||||||
AnimPose rigToWorldPose(glm::vec3(1.0f), getWorldOrientation() * Quaternions::Y_180, getWorldPosition());
|
AnimPose rigToWorldPose(1.0f, getWorldOrientation() * Quaternions::Y_180, getWorldPosition());
|
||||||
const int NUM_DEBUG_COLORS = 8;
|
const int NUM_DEBUG_COLORS = 8;
|
||||||
const glm::vec4 DEBUG_COLORS[NUM_DEBUG_COLORS] = {
|
const glm::vec4 DEBUG_COLORS[NUM_DEBUG_COLORS] = {
|
||||||
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f),
|
||||||
|
@ -4824,7 +4821,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat
|
||||||
swingTwistDecomposition(hipsinWorldSpace, avatarUpWorld, resultingSwingInWorld, resultingTwistInWorld);
|
swingTwistDecomposition(hipsinWorldSpace, avatarUpWorld, resultingSwingInWorld, resultingTwistInWorld);
|
||||||
|
|
||||||
// remove scale present from sensorToWorldMatrix
|
// remove scale present from sensorToWorldMatrix
|
||||||
followWorldPose.scale() = glm::vec3(1.0f);
|
followWorldPose.scale() = 1.0f;
|
||||||
|
|
||||||
if (isActive(Rotation)) {
|
if (isActive(Rotation)) {
|
||||||
//use the hmd reading for the hips follow
|
//use the hmd reading for the hips follow
|
||||||
|
|
|
@ -41,7 +41,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
|
||||||
if (myAvatar->isJointPinned(hipsIndex)) {
|
if (myAvatar->isJointPinned(hipsIndex)) {
|
||||||
Transform avatarTransform = myAvatar->getTransform();
|
Transform avatarTransform = myAvatar->getTransform();
|
||||||
AnimPose result = AnimPose(worldToSensorMat * avatarTransform.getMatrix() * Matrices::Y_180);
|
AnimPose result = AnimPose(worldToSensorMat * avatarTransform.getMatrix() * Matrices::Y_180);
|
||||||
result.scale() = glm::vec3(1.0f, 1.0f, 1.0f);
|
result.scale() = 1.0f;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
|
|
||||||
Rig::ControllerParameters params;
|
Rig::ControllerParameters params;
|
||||||
|
|
||||||
AnimPose avatarToRigPose(glm::vec3(1.0f), Quaternions::Y_180, glm::vec3(0.0f));
|
AnimPose avatarToRigPose(1.0f, Quaternions::Y_180, glm::vec3(0.0f));
|
||||||
|
|
||||||
glm::mat4 rigToAvatarMatrix = Matrices::Y_180;
|
glm::mat4 rigToAvatarMatrix = Matrices::Y_180;
|
||||||
glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition());
|
glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition());
|
||||||
|
@ -127,7 +127,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
// preMult 180 is necessary to convert from avatar to rig coordinates.
|
// preMult 180 is necessary to convert from avatar to rig coordinates.
|
||||||
// postMult 180 is necessary to convert head from -z forward to z forward.
|
// postMult 180 is necessary to convert head from -z forward to z forward.
|
||||||
glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
|
glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
|
||||||
params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f));
|
params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(1.0f, headRot, glm::vec3(0.0f));
|
||||||
params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = 0;
|
params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,20 +72,17 @@ void QmlMarketplace::getMarketplaceItems(
|
||||||
|
|
||||||
void QmlMarketplace::getMarketplaceItem(const QString& marketplaceItemId) {
|
void QmlMarketplace::getMarketplaceItem(const QString& marketplaceItemId) {
|
||||||
QString endpoint = QString("items/") + marketplaceItemId;
|
QString endpoint = QString("items/") + marketplaceItemId;
|
||||||
QUrlQuery request;
|
send(endpoint, "getMarketplaceItemSuccess", "getMarketplaceItemFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional);
|
||||||
send(endpoint, "getMarketplaceItemSuccess", "getMarketplaceItemFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::Optional, request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlMarketplace::marketplaceItemLike(const QString& marketplaceItemId, const bool like) {
|
void QmlMarketplace::marketplaceItemLike(const QString& marketplaceItemId, const bool like) {
|
||||||
QString endpoint = QString("items/") + marketplaceItemId + "/like";
|
QString endpoint = QString("items/") + marketplaceItemId + "/like";
|
||||||
QUrlQuery request;
|
send(endpoint, "marketplaceItemLikeSuccess", "marketplaceItemLikeFailure", like ? QNetworkAccessManager::PostOperation : QNetworkAccessManager::DeleteOperation, AccountManagerAuth::Required);
|
||||||
send(endpoint, "marketplaceItemLikeSuccess", "marketplaceItemLikeFailure", like ? QNetworkAccessManager::PostOperation : QNetworkAccessManager::DeleteOperation, AccountManagerAuth::Required, request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlMarketplace::getMarketplaceCategories() {
|
void QmlMarketplace::getMarketplaceCategories() {
|
||||||
QString endpoint = "categories";
|
QString endpoint = "categories";
|
||||||
QUrlQuery request;
|
send(endpoint, "getMarketplaceCategoriesSuccess", "getMarketplaceCategoriesFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::None);
|
||||||
send(endpoint, "getMarketplaceCategoriesSuccess", "getMarketplaceCategoriesFailure", QNetworkAccessManager::GetOperation, AccountManagerAuth::None, request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,14 +91,13 @@ void QmlMarketplace::send(const QString& endpoint, const QString& success, const
|
||||||
const QString URL = "/api/v1/marketplace/";
|
const QString URL = "/api/v1/marketplace/";
|
||||||
JSONCallbackParameters callbackParams(this, success, fail);
|
JSONCallbackParameters callbackParams(this, success, fail);
|
||||||
|
|
||||||
accountManager->sendRequest(URL + endpoint,
|
accountManager->sendRequest(URL + endpoint + "?" + request.toString(),
|
||||||
authType,
|
authType,
|
||||||
method,
|
method,
|
||||||
callbackParams,
|
callbackParams,
|
||||||
QByteArray(),
|
QByteArray(),
|
||||||
NULL,
|
NULL,
|
||||||
QVariantMap(),
|
QVariantMap());
|
||||||
request);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,12 @@ signals:
|
||||||
void marketplaceItemLikeResult(QJsonObject result);
|
void marketplaceItemLikeResult(QJsonObject result);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, const QUrlQuery & request);
|
void send(const QString& endpoint,
|
||||||
|
const QString& success,
|
||||||
|
const QString& fail,
|
||||||
|
QNetworkAccessManager::Operation method,
|
||||||
|
AccountManagerAuth::Type authType,
|
||||||
|
const QUrlQuery& request = QUrlQuery());
|
||||||
QJsonObject apiResponse(const QString& label, QNetworkReply* reply);
|
QJsonObject apiResponse(const QString& label, QNetworkReply* reply);
|
||||||
QJsonObject failResponse(const QString& label, QNetworkReply* reply);
|
QJsonObject failResponse(const QString& label, QNetworkReply* reply);
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,7 +44,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer<ReceivedMessage> messag
|
||||||
qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
|
qDebug("OctreePacketProcessor::processPacket() packets to process=%d", packetsToProcessCount());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool wasStatsPacket = false;
|
bool wasStatsPacket = false;
|
||||||
|
|
||||||
PacketType octreePacketType = message->getType();
|
PacketType octreePacketType = message->getType();
|
||||||
|
|
|
@ -56,7 +56,8 @@ PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PickResultPointer RayPick::getAvatarIntersection(const PickRay& pick) {
|
PickResultPointer RayPick::getAvatarIntersection(const PickRay& pick) {
|
||||||
RayToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findRayIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), true);
|
bool precisionPicking = !(getFilter().isCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
|
||||||
|
RayToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findRayIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), precisionPicking);
|
||||||
if (avatarRes.intersects) {
|
if (avatarRes.intersects) {
|
||||||
return std::make_shared<RayPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, pick, avatarRes.surfaceNormal, avatarRes.extraInfo);
|
return std::make_shared<RayPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, pick, avatarRes.surfaceNormal, avatarRes.extraInfo);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "AudioClient.h"
|
#include "AudioClient.h"
|
||||||
|
#include "AudioHelpers.h"
|
||||||
#include "ui/AvatarInputs.h"
|
#include "ui/AvatarInputs.h"
|
||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
@ -26,26 +27,9 @@ QString Audio::HMD { "VR" };
|
||||||
Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true };
|
Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true };
|
||||||
|
|
||||||
float Audio::loudnessToLevel(float loudness) {
|
float Audio::loudnessToLevel(float loudness) {
|
||||||
const float LOG2 = log(2.0f);
|
float level = 6.02059991f * fastLog2f(loudness); // level in dBFS
|
||||||
const float METER_LOUDNESS_SCALE = 2.8f / 5.0f;
|
level = (level + 48.0f) * (1/39.0f); // map [-48, -9] dBFS to [0, 1]
|
||||||
const float LOG2_LOUDNESS_FLOOR = 11.0f;
|
return glm::clamp(level, 0.0f, 1.0f);
|
||||||
|
|
||||||
float level = 0.0f;
|
|
||||||
|
|
||||||
loudness += 1.0f;
|
|
||||||
float log2loudness = logf(loudness) / LOG2;
|
|
||||||
|
|
||||||
if (log2loudness <= LOG2_LOUDNESS_FLOOR) {
|
|
||||||
level = (log2loudness / LOG2_LOUDNESS_FLOOR) * METER_LOUDNESS_SCALE;
|
|
||||||
} else {
|
|
||||||
level = (log2loudness - (LOG2_LOUDNESS_FLOOR - 1.0f)) * METER_LOUDNESS_SCALE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level > 1.0f) {
|
|
||||||
level = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return level;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Audio::Audio() : _devices(_contextIsHMD) {
|
Audio::Audio() : _devices(_contextIsHMD) {
|
||||||
|
@ -150,18 +134,33 @@ float Audio::getInputLevel() const {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::onInputLoudnessChanged(float loudness) {
|
bool Audio::isClipping() const {
|
||||||
|
return resultWithReadLock<bool>([&] {
|
||||||
|
return _isClipping;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Audio::onInputLoudnessChanged(float loudness, bool isClipping) {
|
||||||
float level = loudnessToLevel(loudness);
|
float level = loudnessToLevel(loudness);
|
||||||
bool changed = false;
|
bool levelChanged = false;
|
||||||
|
bool isClippingChanged = false;
|
||||||
|
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
if (_inputLevel != level) {
|
if (_inputLevel != level) {
|
||||||
_inputLevel = level;
|
_inputLevel = level;
|
||||||
changed = true;
|
levelChanged = true;
|
||||||
|
}
|
||||||
|
if (_isClipping != isClipping) {
|
||||||
|
_isClipping = isClipping;
|
||||||
|
isClippingChanged = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (changed) {
|
if (levelChanged) {
|
||||||
emit inputLevelChanged(level);
|
emit inputLevelChanged(level);
|
||||||
}
|
}
|
||||||
|
if (isClippingChanged) {
|
||||||
|
emit clippingChanged(isClipping);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Audio::getContext() const {
|
QString Audio::getContext() const {
|
||||||
|
|
|
@ -41,6 +41,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
||||||
* above the noise floor.
|
* above the noise floor.
|
||||||
* @property {number} inputLevel - The loudness of the audio input, range <code>0.0</code> (no sound) –
|
* @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>
|
* <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>.
|
* @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
|
* 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>.
|
* devices, and others might only support values of <code>0.0</code> and <code>1.0</code>.
|
||||||
|
@ -58,6 +59,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
|
||||||
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
|
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
|
||||||
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
|
Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged)
|
||||||
Q_PROPERTY(float inputLevel READ getInputLevel NOTIFY inputLevelChanged)
|
Q_PROPERTY(float inputLevel READ getInputLevel NOTIFY inputLevelChanged)
|
||||||
|
Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged)
|
||||||
Q_PROPERTY(QString context READ getContext NOTIFY contextChanged)
|
Q_PROPERTY(QString context READ getContext NOTIFY contextChanged)
|
||||||
Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop)
|
Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop)
|
||||||
|
|
||||||
|
@ -74,6 +76,7 @@ public:
|
||||||
bool noiseReductionEnabled() const;
|
bool noiseReductionEnabled() const;
|
||||||
float getInputVolume() const;
|
float getInputVolume() const;
|
||||||
float getInputLevel() const;
|
float getInputLevel() const;
|
||||||
|
bool isClipping() const;
|
||||||
QString getContext() const;
|
QString getContext() const;
|
||||||
|
|
||||||
void showMicMeter(bool show);
|
void showMicMeter(bool show);
|
||||||
|
@ -217,6 +220,14 @@ signals:
|
||||||
*/
|
*/
|
||||||
void inputLevelChanged(float level);
|
void inputLevelChanged(float level);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Triggered when the clipping state of the input audio changes.
|
||||||
|
* @function Audio.clippingChanged
|
||||||
|
* @param {boolean} isClipping - <code>true</code> if the audio input is clipping, otherwise <code>false</code>.
|
||||||
|
* @returns {Signal}
|
||||||
|
*/
|
||||||
|
void clippingChanged(bool isClipping);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Triggered when the current context of the audio changes.
|
* Triggered when the current context of the audio changes.
|
||||||
* @function Audio.contextChanged
|
* @function Audio.contextChanged
|
||||||
|
@ -237,7 +248,7 @@ private slots:
|
||||||
void setMuted(bool muted);
|
void setMuted(bool muted);
|
||||||
void enableNoiseReduction(bool enable);
|
void enableNoiseReduction(bool enable);
|
||||||
void setInputVolume(float volume);
|
void setInputVolume(float volume);
|
||||||
void onInputLoudnessChanged(float loudness);
|
void onInputLoudnessChanged(float loudness, bool isClipping);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Audio must live on a separate thread from AudioClient to avoid deadlocks
|
// Audio must live on a separate thread from AudioClient to avoid deadlocks
|
||||||
|
@ -247,6 +258,7 @@ private:
|
||||||
|
|
||||||
float _inputVolume { 1.0f };
|
float _inputVolume { 1.0f };
|
||||||
float _inputLevel { 0.0f };
|
float _inputLevel { 0.0f };
|
||||||
|
bool _isClipping { false };
|
||||||
bool _isMuted { false };
|
bool _isMuted { false };
|
||||||
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
|
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
|
||||||
bool _contextIsHMD { false };
|
bool _contextIsHMD { false };
|
||||||
|
|
|
@ -140,10 +140,10 @@ void AnimClip::copyFromNetworkAnim() {
|
||||||
postRot = animSkeleton.getPostRotationPose(animJoint);
|
postRot = animSkeleton.getPostRotationPose(animJoint);
|
||||||
|
|
||||||
// cancel out scale
|
// cancel out scale
|
||||||
preRot.scale() = glm::vec3(1.0f);
|
preRot.scale() = 1.0f;
|
||||||
postRot.scale() = glm::vec3(1.0f);
|
postRot.scale() = 1.0f;
|
||||||
|
|
||||||
AnimPose rot(glm::vec3(1.0f), hfmAnimRot, glm::vec3());
|
AnimPose rot(1.0f, hfmAnimRot, glm::vec3());
|
||||||
|
|
||||||
// adjust translation offsets, so large translation animatons on the reference skeleton
|
// adjust translation offsets, so large translation animatons on the reference skeleton
|
||||||
// will be adjusted when played on a skeleton with short limbs.
|
// will be adjusted when played on a skeleton with short limbs.
|
||||||
|
@ -155,7 +155,7 @@ void AnimClip::copyFromNetworkAnim() {
|
||||||
boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(hfmZeroTrans);
|
boneLengthScale = glm::length(relDefaultPose.trans()) / glm::length(hfmZeroTrans);
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimPose trans = AnimPose(glm::vec3(1.0f), glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans));
|
AnimPose trans = AnimPose(1.0f, glm::quat(), relDefaultPose.trans() + boneLengthScale * (hfmAnimTrans - hfmZeroTrans));
|
||||||
|
|
||||||
_anim[frame][skeletonJoint] = trans * preRot * rot * postRot;
|
_anim[frame][skeletonJoint] = trans * preRot * rot * postRot;
|
||||||
}
|
}
|
||||||
|
|
|
@ -552,7 +552,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const
|
||||||
AnimPose accum = absolutePoses[_hipsIndex];
|
AnimPose accum = absolutePoses[_hipsIndex];
|
||||||
AnimPose baseParentPose = absolutePoses[_hipsIndex];
|
AnimPose baseParentPose = absolutePoses[_hipsIndex];
|
||||||
for (int i = (int)chainDepth - 1; i >= 0; i--) {
|
for (int i = (int)chainDepth - 1; i >= 0; i--) {
|
||||||
accum = accum * AnimPose(glm::vec3(1.0f), jointChainInfoOut.jointInfoVec[i].rot, jointChainInfoOut.jointInfoVec[i].trans);
|
accum = accum * AnimPose(1.0f, jointChainInfoOut.jointInfoVec[i].rot, jointChainInfoOut.jointInfoVec[i].trans);
|
||||||
postAbsPoses[i] = accum;
|
postAbsPoses[i] = accum;
|
||||||
if (jointChainInfoOut.jointInfoVec[i].jointIndex == topJointIndex) {
|
if (jointChainInfoOut.jointInfoVec[i].jointIndex == topJointIndex) {
|
||||||
topChainIndex = i;
|
topChainIndex = i;
|
||||||
|
@ -734,7 +734,7 @@ void AnimInverseKinematics::computeAndCacheSplineJointInfosForIKTarget(const Ani
|
||||||
glm::mat3 m(u, v, glm::cross(u, v));
|
glm::mat3 m(u, v, glm::cross(u, v));
|
||||||
glm::quat rot = glm::normalize(glm::quat_cast(m));
|
glm::quat rot = glm::normalize(glm::quat_cast(m));
|
||||||
|
|
||||||
AnimPose pose(glm::vec3(1.0f), rot, spline(t));
|
AnimPose pose(1.0f, rot, spline(t));
|
||||||
AnimPose offsetPose = pose.inverse() * defaultPose;
|
AnimPose offsetPose = pose.inverse() * defaultPose;
|
||||||
|
|
||||||
SplineJointInfo splineJointInfo = { index, ratio, offsetPose };
|
SplineJointInfo splineJointInfo = { index, ratio, offsetPose };
|
||||||
|
@ -767,7 +767,7 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
|
||||||
const int baseIndex = _hipsIndex;
|
const int baseIndex = _hipsIndex;
|
||||||
|
|
||||||
// build spline from tip to base
|
// build spline from tip to base
|
||||||
AnimPose tipPose = AnimPose(glm::vec3(1.0f), target.getRotation(), target.getTranslation());
|
AnimPose tipPose = AnimPose(1.0f, target.getRotation(), target.getTranslation());
|
||||||
AnimPose basePose = absolutePoses[baseIndex];
|
AnimPose basePose = absolutePoses[baseIndex];
|
||||||
CubicHermiteSplineFunctorWithArcLength spline;
|
CubicHermiteSplineFunctorWithArcLength spline;
|
||||||
if (target.getIndex() == _headIndex) {
|
if (target.getIndex() == _headIndex) {
|
||||||
|
@ -815,7 +815,7 @@ void AnimInverseKinematics::solveTargetWithSpline(const AnimContext& context, co
|
||||||
glm::mat3 m(u, v, glm::cross(u, v));
|
glm::mat3 m(u, v, glm::cross(u, v));
|
||||||
glm::quat rot = glm::normalize(glm::quat_cast(m));
|
glm::quat rot = glm::normalize(glm::quat_cast(m));
|
||||||
|
|
||||||
AnimPose desiredAbsPose = AnimPose(glm::vec3(1.0f), rot, trans) * splineJointInfo.offsetPose;
|
AnimPose desiredAbsPose = AnimPose(1.0f, rot, trans) * splineJointInfo.offsetPose;
|
||||||
|
|
||||||
// apply flex coefficent
|
// apply flex coefficent
|
||||||
AnimPose flexedAbsPose;
|
AnimPose flexedAbsPose;
|
||||||
|
@ -965,7 +965,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
||||||
}
|
}
|
||||||
|
|
||||||
_relativePoses[_hipsIndex] = parentAbsPose.inverse() * absPose;
|
_relativePoses[_hipsIndex] = parentAbsPose.inverse() * absPose;
|
||||||
_relativePoses[_hipsIndex].scale() = glm::vec3(1.0f);
|
_relativePoses[_hipsIndex].scale() = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is an active jointChainInfo for the hips store the post shifted hips into it.
|
// if there is an active jointChainInfo for the hips store the post shifted hips into it.
|
||||||
|
@ -1753,7 +1753,7 @@ void AnimInverseKinematics::setSecondaryTargets(const AnimContext& context) {
|
||||||
AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix()));
|
AnimPose rigToGeometryPose = AnimPose(glm::inverse(context.getGeometryToRigMatrix()));
|
||||||
for (auto& iter : _secondaryTargetsInRigFrame) {
|
for (auto& iter : _secondaryTargetsInRigFrame) {
|
||||||
AnimPose absPose = rigToGeometryPose * iter.second;
|
AnimPose absPose = rigToGeometryPose * iter.second;
|
||||||
absPose.scale() = glm::vec3(1.0f);
|
absPose.scale() = 1.0f;
|
||||||
|
|
||||||
AnimPose parentAbsPose;
|
AnimPose parentAbsPose;
|
||||||
int parentIndex = _skeleton->getParentIndex(iter.first);
|
int parentIndex = _skeleton->getParentIndex(iter.first);
|
||||||
|
@ -1825,7 +1825,7 @@ void AnimInverseKinematics::debugDrawSpineSplines(const AnimContext& context, co
|
||||||
const int baseIndex = _hipsIndex;
|
const int baseIndex = _hipsIndex;
|
||||||
|
|
||||||
// build spline
|
// build spline
|
||||||
AnimPose tipPose = AnimPose(glm::vec3(1.0f), target.getRotation(), target.getTranslation());
|
AnimPose tipPose = AnimPose(1.0f, target.getRotation(), target.getTranslation());
|
||||||
AnimPose basePose = _skeleton->getAbsolutePose(baseIndex, _relativePoses);
|
AnimPose basePose = _skeleton->getAbsolutePose(baseIndex, _relativePoses);
|
||||||
|
|
||||||
CubicHermiteSplineFunctorWithArcLength spline;
|
CubicHermiteSplineFunctorWithArcLength spline;
|
||||||
|
|
|
@ -172,5 +172,5 @@ AnimPose AnimManipulator::computeRelativePoseFromJointVar(const AnimVariantMap&
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return AnimPose(glm::vec3(1), relRot, relTrans);
|
return AnimPose(1.0f, relRot, relTrans);
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ const AnimPoseVec& AnimPoleVectorConstraint::evaluate(const AnimVariantMap& anim
|
||||||
AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
AnimPose tipPose = ikChain.getAbsolutePoseFromJointIndex(_tipJointIndex);
|
||||||
|
|
||||||
// Look up refVector from animVars, make sure to convert into geom space.
|
// Look up refVector from animVars, make sure to convert into geom space.
|
||||||
glm::vec3 refVector = midPose.xformVectorFast(_referenceVector);
|
glm::vec3 refVector = midPose.xformVector(_referenceVector);
|
||||||
float refVectorLength = glm::length(refVector);
|
float refVectorLength = glm::length(refVector);
|
||||||
|
|
||||||
glm::vec3 axis = basePose.trans() - tipPose.trans();
|
glm::vec3 axis = basePose.trans() - tipPose.trans();
|
||||||
|
|
|
@ -14,15 +14,14 @@
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
#include "AnimUtil.h"
|
#include "AnimUtil.h"
|
||||||
|
|
||||||
const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f),
|
const AnimPose AnimPose::identity = AnimPose(1.0f, glm::quat(), glm::vec3(0.0f));
|
||||||
glm::quat(),
|
|
||||||
glm::vec3(0.0f));
|
|
||||||
|
|
||||||
AnimPose::AnimPose(const glm::mat4& mat) {
|
AnimPose::AnimPose(const glm::mat4& mat) {
|
||||||
static const float EPSILON = 0.0001f;
|
static const float EPSILON = 0.0001f;
|
||||||
_scale = extractScale(mat);
|
glm::vec3 scale = extractScale(mat);
|
||||||
// quat_cast doesn't work so well with scaled matrices, so cancel it out.
|
// quat_cast doesn't work so well with scaled matrices, so cancel it out.
|
||||||
glm::mat4 tmp = glm::scale(mat, 1.0f / _scale);
|
glm::mat4 tmp = glm::scale(mat, 1.0f / scale);
|
||||||
|
_scale = extractUniformScale(scale);
|
||||||
_rot = glm::quat_cast(tmp);
|
_rot = glm::quat_cast(tmp);
|
||||||
float lengthSquared = glm::length2(_rot);
|
float lengthSquared = glm::length2(_rot);
|
||||||
if (glm::abs(lengthSquared - 1.0f) > EPSILON) {
|
if (glm::abs(lengthSquared - 1.0f) > EPSILON) {
|
||||||
|
@ -40,29 +39,22 @@ glm::vec3 AnimPose::xformPoint(const glm::vec3& rhs) const {
|
||||||
return *this * rhs;
|
return *this * rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// really slow, but accurate for transforms with non-uniform scale
|
|
||||||
glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const {
|
glm::vec3 AnimPose::xformVector(const glm::vec3& rhs) const {
|
||||||
glm::vec3 xAxis = _rot * glm::vec3(_scale.x, 0.0f, 0.0f);
|
|
||||||
glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale.y, 0.0f);
|
|
||||||
glm::vec3 zAxis = _rot * glm::vec3(0.0f, 0.0f, _scale.z);
|
|
||||||
glm::mat3 mat(xAxis, yAxis, zAxis);
|
|
||||||
glm::mat3 transInvMat = glm::inverse(glm::transpose(mat));
|
|
||||||
return transInvMat * rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// faster, but does not handle non-uniform scale correctly.
|
|
||||||
glm::vec3 AnimPose::xformVectorFast(const glm::vec3& rhs) const {
|
|
||||||
return _rot * (_scale * rhs);
|
return _rot * (_scale * rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimPose AnimPose::operator*(const AnimPose& rhs) const {
|
AnimPose AnimPose::operator*(const AnimPose& rhs) const {
|
||||||
glm::mat4 result;
|
float scale = _scale * rhs._scale;
|
||||||
glm_mat4u_mul(*this, rhs, result);
|
glm::quat rot = _rot * rhs._rot;
|
||||||
return AnimPose(result);
|
glm::vec3 trans = _trans + (_rot * (_scale * rhs._trans));
|
||||||
|
return AnimPose(scale, rot, trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimPose AnimPose::inverse() const {
|
AnimPose AnimPose::inverse() const {
|
||||||
return AnimPose(glm::inverse(static_cast<glm::mat4>(*this)));
|
float invScale = 1.0f / _scale;
|
||||||
|
glm::quat invRot = glm::inverse(_rot);
|
||||||
|
glm::vec3 invTrans = invScale * (invRot * -_trans);
|
||||||
|
return AnimPose(invScale, invRot, invTrans);
|
||||||
}
|
}
|
||||||
|
|
||||||
// mirror about x-axis without applying negative scale.
|
// mirror about x-axis without applying negative scale.
|
||||||
|
@ -71,11 +63,10 @@ AnimPose AnimPose::mirror() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimPose::operator glm::mat4() const {
|
AnimPose::operator glm::mat4() const {
|
||||||
glm::vec3 xAxis = _rot * glm::vec3(_scale.x, 0.0f, 0.0f);
|
glm::vec3 xAxis = _rot * glm::vec3(_scale, 0.0f, 0.0f);
|
||||||
glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale.y, 0.0f);
|
glm::vec3 yAxis = _rot * glm::vec3(0.0f, _scale, 0.0f);
|
||||||
glm::vec3 zAxis = _rot * glm::vec3(0.0f, 0.0f, _scale.z);
|
glm::vec3 zAxis = _rot * glm::vec3(0.0f, 0.0f, _scale);
|
||||||
return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f),
|
return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f));
|
||||||
glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimPose::blend(const AnimPose& srcPose, float alpha) {
|
void AnimPose::blend(const AnimPose& srcPose, float alpha) {
|
||||||
|
|
|
@ -21,14 +21,13 @@ class AnimPose {
|
||||||
public:
|
public:
|
||||||
AnimPose() {}
|
AnimPose() {}
|
||||||
explicit AnimPose(const glm::mat4& mat);
|
explicit AnimPose(const glm::mat4& mat);
|
||||||
explicit AnimPose(const glm::quat& rotIn) : _scale(1.0f), _rot(rotIn), _trans(0.0f) {}
|
explicit AnimPose(const glm::quat& rotIn) : _rot(rotIn), _trans(0.0f), _scale(1.0f) {}
|
||||||
AnimPose(const glm::quat& rotIn, const glm::vec3& transIn) : _scale(1.0f), _rot(rotIn), _trans(transIn) {}
|
AnimPose(const glm::quat& rotIn, const glm::vec3& transIn) : _rot(rotIn), _trans(transIn), _scale(1.0f) {}
|
||||||
AnimPose(const glm::vec3& scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : _scale(scaleIn), _rot(rotIn), _trans(transIn) {}
|
AnimPose(float scaleIn, const glm::quat& rotIn, const glm::vec3& transIn) : _rot(rotIn), _trans(transIn), _scale(scaleIn) {}
|
||||||
static const AnimPose identity;
|
static const AnimPose identity;
|
||||||
|
|
||||||
glm::vec3 xformPoint(const glm::vec3& rhs) const;
|
glm::vec3 xformPoint(const glm::vec3& rhs) const;
|
||||||
glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow, but accurate for transforms with non-uniform scale
|
glm::vec3 xformVector(const glm::vec3& rhs) const; // really slow, but accurate for transforms with non-uniform scale
|
||||||
glm::vec3 xformVectorFast(const glm::vec3& rhs) const; // faster, but does not handle non-uniform scale correctly.
|
|
||||||
|
|
||||||
glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint
|
glm::vec3 operator*(const glm::vec3& rhs) const; // same as xformPoint
|
||||||
AnimPose operator*(const AnimPose& rhs) const;
|
AnimPose operator*(const AnimPose& rhs) const;
|
||||||
|
@ -37,8 +36,8 @@ public:
|
||||||
AnimPose mirror() const;
|
AnimPose mirror() const;
|
||||||
operator glm::mat4() const;
|
operator glm::mat4() const;
|
||||||
|
|
||||||
const glm::vec3& scale() const { return _scale; }
|
float scale() const { return _scale; }
|
||||||
glm::vec3& scale() { return _scale; }
|
float& scale() { return _scale; }
|
||||||
|
|
||||||
const glm::quat& rot() const { return _rot; }
|
const glm::quat& rot() const { return _rot; }
|
||||||
glm::quat& rot() { return _rot; }
|
glm::quat& rot() { return _rot; }
|
||||||
|
@ -50,13 +49,13 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend QDebug operator<<(QDebug debug, const AnimPose& pose);
|
friend QDebug operator<<(QDebug debug, const AnimPose& pose);
|
||||||
glm::vec3 _scale { 1.0f };
|
|
||||||
glm::quat _rot;
|
glm::quat _rot;
|
||||||
glm::vec3 _trans;
|
glm::vec3 _trans;
|
||||||
|
float _scale { 1.0f }; // uniform scale only.
|
||||||
};
|
};
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug debug, const AnimPose& pose) {
|
inline QDebug operator<<(QDebug debug, const AnimPose& pose) {
|
||||||
debug << "AnimPose, trans = (" << pose.trans().x << pose.trans().y << pose.trans().z << "), rot = (" << pose.rot().x << pose.rot().y << pose.rot().z << pose.rot().w << "), scale = (" << pose.scale().x << pose.scale().y << pose.scale().z << ")";
|
debug << "AnimPose, trans = (" << pose.trans().x << pose.trans().y << pose.trans().z << "), rot = (" << pose.rot().x << pose.rot().y << pose.rot().z << pose.rot().w << "), scale =" << pose.scale();
|
||||||
return debug;
|
return debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ int AnimSkeleton::getChainDepth(int jointIndex) const {
|
||||||
int index = jointIndex;
|
int index = jointIndex;
|
||||||
do {
|
do {
|
||||||
chainDepth++;
|
chainDepth++;
|
||||||
index = _joints[index].parentIndex;
|
index = _parentIndices[index];
|
||||||
} while (index != -1);
|
} while (index != -1);
|
||||||
return chainDepth;
|
return chainDepth;
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,17 +102,12 @@ const AnimPose& AnimSkeleton::getPostRotationPose(int jointIndex) const {
|
||||||
return _relativePostRotationPoses[jointIndex];
|
return _relativePostRotationPoses[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
int AnimSkeleton::getParentIndex(int jointIndex) const {
|
|
||||||
return _joints[jointIndex].parentIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int> AnimSkeleton::getChildrenOfJoint(int jointIndex) const {
|
std::vector<int> AnimSkeleton::getChildrenOfJoint(int jointIndex) const {
|
||||||
// Children and grandchildren, etc.
|
// Children and grandchildren, etc.
|
||||||
std::vector<int> result;
|
std::vector<int> result;
|
||||||
if (jointIndex != -1) {
|
if (jointIndex != -1) {
|
||||||
for (int i = jointIndex + 1; i < (int)_joints.size(); i++) {
|
for (int i = jointIndex + 1; i < (int)_parentIndices.size(); i++) {
|
||||||
if (_joints[i].parentIndex == jointIndex
|
if (_parentIndices[i] == jointIndex || (std::find(result.begin(), result.end(), _parentIndices[i]) != result.end())) {
|
||||||
|| (std::find(result.begin(), result.end(), _joints[i].parentIndex) != result.end())) {
|
|
||||||
result.push_back(i);
|
result.push_back(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +123,7 @@ AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& relati
|
||||||
if (jointIndex < 0 || jointIndex >= (int)relativePoses.size() || jointIndex >= _jointsSize) {
|
if (jointIndex < 0 || jointIndex >= (int)relativePoses.size() || jointIndex >= _jointsSize) {
|
||||||
return AnimPose::identity;
|
return AnimPose::identity;
|
||||||
} else {
|
} else {
|
||||||
return getAbsolutePose(_joints[jointIndex].parentIndex, relativePoses) * relativePoses[jointIndex];
|
return getAbsolutePose(_parentIndices[jointIndex], relativePoses) * relativePoses[jointIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +131,7 @@ void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const {
|
||||||
// poses start off relative and leave in absolute frame
|
// poses start off relative and leave in absolute frame
|
||||||
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
||||||
for (int i = 0; i < lastIndex; ++i) {
|
for (int i = 0; i < lastIndex; ++i) {
|
||||||
int parentIndex = _joints[i].parentIndex;
|
int parentIndex = _parentIndices[i];
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != -1) {
|
||||||
poses[i] = poses[parentIndex] * poses[i];
|
poses[i] = poses[parentIndex] * poses[i];
|
||||||
}
|
}
|
||||||
|
@ -147,7 +142,7 @@ void AnimSkeleton::convertAbsolutePosesToRelative(AnimPoseVec& poses) const {
|
||||||
// poses start off absolute and leave in relative frame
|
// poses start off absolute and leave in relative frame
|
||||||
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
int lastIndex = std::min((int)poses.size(), _jointsSize);
|
||||||
for (int i = lastIndex - 1; i >= 0; --i) {
|
for (int i = lastIndex - 1; i >= 0; --i) {
|
||||||
int parentIndex = _joints[i].parentIndex;
|
int parentIndex = _parentIndices[i];
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != -1) {
|
||||||
poses[i] = poses[parentIndex].inverse() * poses[i];
|
poses[i] = poses[parentIndex].inverse() * poses[i];
|
||||||
}
|
}
|
||||||
|
@ -158,7 +153,7 @@ void AnimSkeleton::convertAbsoluteRotationsToRelative(std::vector<glm::quat>& ro
|
||||||
// poses start off absolute and leave in relative frame
|
// poses start off absolute and leave in relative frame
|
||||||
int lastIndex = std::min((int)rotations.size(), _jointsSize);
|
int lastIndex = std::min((int)rotations.size(), _jointsSize);
|
||||||
for (int i = lastIndex - 1; i >= 0; --i) {
|
for (int i = lastIndex - 1; i >= 0; --i) {
|
||||||
int parentIndex = _joints[i].parentIndex;
|
int parentIndex = _parentIndices[i];
|
||||||
if (parentIndex != -1) {
|
if (parentIndex != -1) {
|
||||||
rotations[i] = glm::inverse(rotations[parentIndex]) * rotations[i];
|
rotations[i] = glm::inverse(rotations[parentIndex]) * rotations[i];
|
||||||
}
|
}
|
||||||
|
@ -197,6 +192,14 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const {
|
||||||
void AnimSkeleton::buildSkeletonFromJoints(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets) {
|
void AnimSkeleton::buildSkeletonFromJoints(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets) {
|
||||||
|
|
||||||
_joints = joints;
|
_joints = joints;
|
||||||
|
|
||||||
|
// build a seperate vector of parentIndices for cache coherency
|
||||||
|
// AnimSkeleton::getParentIndex is called very frequently in tight loops.
|
||||||
|
_parentIndices.reserve(_joints.size());
|
||||||
|
for (auto& joint : _joints) {
|
||||||
|
_parentIndices.push_back(joint.parentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
_jointsSize = (int)joints.size();
|
_jointsSize = (int)joints.size();
|
||||||
// build a cache of bind poses
|
// build a cache of bind poses
|
||||||
|
|
||||||
|
@ -289,8 +292,6 @@ void AnimSkeleton::dump(bool verbose) const {
|
||||||
qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i);
|
qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i);
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
qCDebug(animation) << " hfmJoint =";
|
qCDebug(animation) << " hfmJoint =";
|
||||||
qCDebug(animation) << " isFree =" << _joints[i].isFree;
|
|
||||||
qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage;
|
|
||||||
qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex;
|
qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex;
|
||||||
qCDebug(animation) << " translation =" << _joints[i].translation;
|
qCDebug(animation) << " translation =" << _joints[i].translation;
|
||||||
qCDebug(animation) << " preTransform =" << _joints[i].preTransform;
|
qCDebug(animation) << " preTransform =" << _joints[i].preTransform;
|
||||||
|
|
|
@ -43,7 +43,10 @@ public:
|
||||||
// get post transform which might include FBX offset transformations
|
// get post transform which might include FBX offset transformations
|
||||||
const AnimPose& getPostRotationPose(int jointIndex) const;
|
const AnimPose& getPostRotationPose(int jointIndex) const;
|
||||||
|
|
||||||
int getParentIndex(int jointIndex) const;
|
int getParentIndex(int jointIndex) const {
|
||||||
|
return _parentIndices[jointIndex];
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<int> getChildrenOfJoint(int jointIndex) const;
|
std::vector<int> getChildrenOfJoint(int jointIndex) const;
|
||||||
|
|
||||||
AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& relativePoses) const;
|
AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& relativePoses) const;
|
||||||
|
@ -69,6 +72,7 @@ protected:
|
||||||
void buildSkeletonFromJoints(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets);
|
void buildSkeletonFromJoints(const std::vector<HFMJoint>& joints, const QMap<int, glm::quat> jointOffsets);
|
||||||
|
|
||||||
std::vector<HFMJoint> _joints;
|
std::vector<HFMJoint> _joints;
|
||||||
|
std::vector<int> _parentIndices;
|
||||||
int _jointsSize { 0 };
|
int _jointsSize { 0 };
|
||||||
AnimPoseVec _relativeDefaultPoses;
|
AnimPoseVec _relativeDefaultPoses;
|
||||||
AnimPoseVec _absoluteDefaultPoses;
|
AnimPoseVec _absoluteDefaultPoses;
|
||||||
|
|
|
@ -39,14 +39,13 @@ static int nextRigId = 1;
|
||||||
static std::map<int, Rig*> rigRegistry;
|
static std::map<int, Rig*> rigRegistry;
|
||||||
static std::mutex rigRegistryMutex;
|
static std::mutex rigRegistryMutex;
|
||||||
|
|
||||||
|
static bool isEqual(const float p, float q) {
|
||||||
|
const float EPSILON = 0.00001f;
|
||||||
|
return fabsf(p - q) <= EPSILON;
|
||||||
|
}
|
||||||
|
|
||||||
static bool isEqual(const glm::vec3& u, const glm::vec3& v) {
|
static bool isEqual(const glm::vec3& u, const glm::vec3& v) {
|
||||||
const float EPSILON = 0.0001f;
|
return isEqual(u.x, v.x) && isEqual(u.y, v.y) && isEqual(u.z, v.z);
|
||||||
float uLen = glm::length(u);
|
|
||||||
if (uLen == 0.0f) {
|
|
||||||
return glm::length(v) <= EPSILON;
|
|
||||||
} else {
|
|
||||||
return (glm::length(u - v) / uLen) <= EPSILON;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isEqual(const glm::quat& p, const glm::quat& q) {
|
static bool isEqual(const glm::quat& p, const glm::quat& q) {
|
||||||
|
@ -494,10 +493,8 @@ std::shared_ptr<AnimInverseKinematics> Rig::getAnimInverseKinematicsNode() const
|
||||||
std::shared_ptr<AnimInverseKinematics> result;
|
std::shared_ptr<AnimInverseKinematics> result;
|
||||||
if (_animNode) {
|
if (_animNode) {
|
||||||
_animNode->traverse([&](AnimNode::Pointer node) {
|
_animNode->traverse([&](AnimNode::Pointer node) {
|
||||||
// only report clip nodes as valid roles.
|
if (node->getType() == AnimNodeType::InverseKinematics) {
|
||||||
auto ikNode = std::dynamic_pointer_cast<AnimInverseKinematics>(node);
|
result = std::dynamic_pointer_cast<AnimInverseKinematics>(node);
|
||||||
if (ikNode) {
|
|
||||||
result = ikNode;
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
|
@ -1329,7 +1326,7 @@ static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& sh
|
||||||
}
|
}
|
||||||
if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) {
|
if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) {
|
||||||
// we are within the k-dop so push the point along the minimum displacement found
|
// we are within the k-dop so push the point along the minimum displacement found
|
||||||
displacementOut = shapePose.xformVectorFast(minDisplacement);
|
displacementOut = shapePose.xformVector(minDisplacement);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// point is outside of kdop
|
// point is outside of kdop
|
||||||
|
@ -1338,7 +1335,7 @@ static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& sh
|
||||||
} else {
|
} else {
|
||||||
// point is directly on top of shapeInfo.avgPoint.
|
// point is directly on top of shapeInfo.avgPoint.
|
||||||
// push the point out along the x axis.
|
// push the point out along the x axis.
|
||||||
displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]);
|
displacementOut = shapePose.xformVector(shapeInfo.points[0]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,6 +170,57 @@ static void channelDownmix(int16_t* source, int16_t* dest, int numSamples) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static float computeLoudness(int16_t* samples, int numSamples, int numChannels, bool& isClipping) {
|
||||||
|
|
||||||
|
const int32_t CLIPPING_THRESHOLD = 32392; // -0.1 dBFS
|
||||||
|
const int32_t CLIPPING_DETECTION = 3; // consecutive samples over threshold
|
||||||
|
|
||||||
|
float scale = numSamples ? 1.0f / (numSamples * 32768.0f) : 0.0f;
|
||||||
|
|
||||||
|
int32_t loudness = 0;
|
||||||
|
isClipping = false;
|
||||||
|
|
||||||
|
if (numChannels == 2) {
|
||||||
|
int32_t oversLeft = 0;
|
||||||
|
int32_t oversRight = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < numSamples/2; i++) {
|
||||||
|
int32_t left = std::abs((int32_t)samples[2*i+0]);
|
||||||
|
int32_t right = std::abs((int32_t)samples[2*i+1]);
|
||||||
|
|
||||||
|
loudness += left;
|
||||||
|
loudness += right;
|
||||||
|
|
||||||
|
if (left > CLIPPING_THRESHOLD) {
|
||||||
|
isClipping |= (++oversLeft >= CLIPPING_DETECTION);
|
||||||
|
} else {
|
||||||
|
oversLeft = 0;
|
||||||
|
}
|
||||||
|
if (right > CLIPPING_THRESHOLD) {
|
||||||
|
isClipping |= (++oversRight >= CLIPPING_DETECTION);
|
||||||
|
} else {
|
||||||
|
oversRight = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int32_t overs = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < numSamples; i++) {
|
||||||
|
int32_t sample = std::abs((int32_t)samples[i]);
|
||||||
|
|
||||||
|
loudness += sample;
|
||||||
|
|
||||||
|
if (sample > CLIPPING_THRESHOLD) {
|
||||||
|
isClipping |= (++overs >= CLIPPING_DETECTION);
|
||||||
|
} else {
|
||||||
|
overs = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (float)loudness * scale;
|
||||||
|
}
|
||||||
|
|
||||||
static inline float convertToFloat(int16_t sample) {
|
static inline float convertToFloat(int16_t sample) {
|
||||||
return (float)sample * (1 / 32768.0f);
|
return (float)sample * (1 / 32768.0f);
|
||||||
}
|
}
|
||||||
|
@ -1075,45 +1126,25 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||||
|
|
||||||
void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
||||||
if (!_audioPaused) {
|
if (!_audioPaused) {
|
||||||
if (_muted) {
|
|
||||||
_lastInputLoudness = 0.0f;
|
bool audioGateOpen = false;
|
||||||
_timeSinceLastClip = 0.0f;
|
|
||||||
} else {
|
if (!_muted) {
|
||||||
int16_t* samples = reinterpret_cast<int16_t*>(audioBuffer.data());
|
int16_t* samples = reinterpret_cast<int16_t*>(audioBuffer.data());
|
||||||
int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE;
|
int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE;
|
||||||
int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||||
|
|
||||||
if (_isNoiseGateEnabled) {
|
if (_isNoiseGateEnabled) {
|
||||||
// The audio gate includes DC removal
|
// The audio gate includes DC removal
|
||||||
_audioGate->render(samples, samples, numFrames);
|
audioGateOpen = _audioGate->render(samples, samples, numFrames);
|
||||||
} else {
|
} else {
|
||||||
_audioGate->removeDC(samples, samples, numFrames);
|
audioGateOpen = _audioGate->removeDC(samples, samples, numFrames);
|
||||||
}
|
|
||||||
|
|
||||||
int32_t loudness = 0;
|
|
||||||
assert(numSamples < 65536); // int32_t loudness cannot overflow
|
|
||||||
bool didClip = false;
|
|
||||||
for (int i = 0; i < numSamples; ++i) {
|
|
||||||
const int32_t CLIPPING_THRESHOLD = (int32_t)(AudioConstants::MAX_SAMPLE_VALUE * 0.9f);
|
|
||||||
int32_t sample = std::abs((int32_t)samples[i]);
|
|
||||||
loudness += sample;
|
|
||||||
didClip |= (sample > CLIPPING_THRESHOLD);
|
|
||||||
}
|
|
||||||
_lastInputLoudness = (float)loudness / numSamples;
|
|
||||||
|
|
||||||
if (didClip) {
|
|
||||||
_timeSinceLastClip = 0.0f;
|
|
||||||
} else if (_timeSinceLastClip >= 0.0f) {
|
|
||||||
_timeSinceLastClip += (float)numSamples / (float)AudioConstants::SAMPLE_RATE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit inputReceived(audioBuffer);
|
emit inputReceived(audioBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit inputLoudnessChanged(_lastInputLoudness);
|
// detect gate opening and closing
|
||||||
|
|
||||||
// state machine to detect gate opening and closing
|
|
||||||
bool audioGateOpen = (_lastInputLoudness != 0.0f);
|
|
||||||
bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened
|
bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened
|
||||||
bool closedInLastBlock = _audioGateOpen && !audioGateOpen; // the gate just closed
|
bool closedInLastBlock = _audioGateOpen && !audioGateOpen; // the gate just closed
|
||||||
_audioGateOpen = audioGateOpen;
|
_audioGateOpen = audioGateOpen;
|
||||||
|
@ -1186,10 +1217,27 @@ void AudioClient::handleMicAudioInput() {
|
||||||
static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
|
static int16_t networkAudioSamples[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO];
|
||||||
|
|
||||||
while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) {
|
while (_inputRingBuffer.samplesAvailable() >= inputSamplesRequired) {
|
||||||
if (_muted) {
|
|
||||||
_inputRingBuffer.shiftReadPosition(inputSamplesRequired);
|
_inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired);
|
||||||
} else {
|
|
||||||
_inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired);
|
// detect loudness and clipping on the raw input
|
||||||
|
bool isClipping = false;
|
||||||
|
float inputLoudness = computeLoudness(inputAudioSamples.get(), inputSamplesRequired, _inputFormat.channelCount(), isClipping);
|
||||||
|
|
||||||
|
float tc = (inputLoudness > _lastInputLoudness) ? 0.378f : 0.967f; // 10ms attack, 300ms release @ 100Hz
|
||||||
|
inputLoudness += tc * (_lastInputLoudness - inputLoudness);
|
||||||
|
_lastInputLoudness = inputLoudness;
|
||||||
|
|
||||||
|
if (isClipping) {
|
||||||
|
_timeSinceLastClip = 0.0f;
|
||||||
|
} else if (_timeSinceLastClip >= 0.0f) {
|
||||||
|
_timeSinceLastClip += AudioConstants::NETWORK_FRAME_SECS;
|
||||||
|
}
|
||||||
|
isClipping = (_timeSinceLastClip >= 0.0f) && (_timeSinceLastClip < 2.0f); // 2 second hold time
|
||||||
|
|
||||||
|
emit inputLoudnessChanged(_lastInputLoudness, isClipping);
|
||||||
|
|
||||||
|
if (!_muted) {
|
||||||
possibleResampling(_inputToNetworkResampler,
|
possibleResampling(_inputToNetworkResampler,
|
||||||
inputAudioSamples.get(), networkAudioSamples,
|
inputAudioSamples.get(), networkAudioSamples,
|
||||||
inputSamplesRequired, numNetworkSamples,
|
inputSamplesRequired, numNetworkSamples,
|
||||||
|
|
|
@ -248,7 +248,7 @@ signals:
|
||||||
void noiseReductionChanged(bool noiseReductionEnabled);
|
void noiseReductionChanged(bool noiseReductionEnabled);
|
||||||
void mutedByMixer();
|
void mutedByMixer();
|
||||||
void inputReceived(const QByteArray& inputSamples);
|
void inputReceived(const QByteArray& inputSamples);
|
||||||
void inputLoudnessChanged(float loudness);
|
void inputLoudnessChanged(float loudness, bool isClipping);
|
||||||
void outputBytesToNetwork(int numBytes);
|
void outputBytesToNetwork(int numBytes);
|
||||||
void inputBytesFromNetwork(int numBytes);
|
void inputBytesFromNetwork(int numBytes);
|
||||||
void noiseGateOpened();
|
void noiseGateOpened();
|
||||||
|
|
|
@ -138,8 +138,8 @@ public:
|
||||||
int32_t hysteresis(int32_t peak);
|
int32_t hysteresis(int32_t peak);
|
||||||
int32_t envelope(int32_t attn);
|
int32_t envelope(int32_t attn);
|
||||||
|
|
||||||
virtual void process(int16_t* input, int16_t* output, int numFrames) = 0;
|
virtual bool process(int16_t* input, int16_t* output, int numFrames) = 0;
|
||||||
virtual void removeDC(int16_t* input, int16_t* output, int numFrames) = 0;
|
virtual bool removeDC(int16_t* input, int16_t* output, int numFrames) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
GateImpl::GateImpl(int sampleRate) {
|
GateImpl::GateImpl(int sampleRate) {
|
||||||
|
@ -403,14 +403,15 @@ public:
|
||||||
GateMono(int sampleRate) : GateImpl(sampleRate) {}
|
GateMono(int sampleRate) : GateImpl(sampleRate) {}
|
||||||
|
|
||||||
// mono input/output (in-place is allowed)
|
// mono input/output (in-place is allowed)
|
||||||
void process(int16_t* input, int16_t* output, int numFrames) override;
|
bool process(int16_t* input, int16_t* output, int numFrames) override;
|
||||||
void removeDC(int16_t* input, int16_t* output, int numFrames) override;
|
bool removeDC(int16_t* input, int16_t* output, int numFrames) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<int N>
|
template<int N>
|
||||||
void GateMono<N>::process(int16_t* input, int16_t* output, int numFrames) {
|
bool GateMono<N>::process(int16_t* input, int16_t* output, int numFrames) {
|
||||||
|
|
||||||
clearHistogram();
|
clearHistogram();
|
||||||
|
int32_t mask = 0;
|
||||||
|
|
||||||
for (int n = 0; n < numFrames; n++) {
|
for (int n = 0; n < numFrames; n++) {
|
||||||
|
|
||||||
|
@ -453,15 +454,21 @@ void GateMono<N>::process(int16_t* input, int16_t* output, int numFrames) {
|
||||||
x = MULQ31(x, attn);
|
x = MULQ31(x, attn);
|
||||||
|
|
||||||
// store 16-bit output
|
// store 16-bit output
|
||||||
output[n] = (int16_t)saturateQ30(x);
|
x = saturateQ30(x);
|
||||||
|
output[n] = (int16_t)x;
|
||||||
|
|
||||||
|
mask |= x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update adaptive threshold
|
// update adaptive threshold
|
||||||
processHistogram(numFrames);
|
processHistogram(numFrames);
|
||||||
|
return mask != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int N>
|
template<int N>
|
||||||
void GateMono<N>::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
bool GateMono<N>::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
||||||
|
|
||||||
|
int32_t mask = 0;
|
||||||
|
|
||||||
for (int n = 0; n < numFrames; n++) {
|
for (int n = 0; n < numFrames; n++) {
|
||||||
|
|
||||||
|
@ -471,8 +478,13 @@ void GateMono<N>::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
||||||
_dc.process(x);
|
_dc.process(x);
|
||||||
|
|
||||||
// store 16-bit output
|
// store 16-bit output
|
||||||
output[n] = (int16_t)saturateQ30(x);
|
x = saturateQ30(x);
|
||||||
|
output[n] = (int16_t)x;
|
||||||
|
|
||||||
|
mask |= x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return mask != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -489,14 +501,15 @@ public:
|
||||||
GateStereo(int sampleRate) : GateImpl(sampleRate) {}
|
GateStereo(int sampleRate) : GateImpl(sampleRate) {}
|
||||||
|
|
||||||
// interleaved stereo input/output (in-place is allowed)
|
// interleaved stereo input/output (in-place is allowed)
|
||||||
void process(int16_t* input, int16_t* output, int numFrames) override;
|
bool process(int16_t* input, int16_t* output, int numFrames) override;
|
||||||
void removeDC(int16_t* input, int16_t* output, int numFrames) override;
|
bool removeDC(int16_t* input, int16_t* output, int numFrames) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<int N>
|
template<int N>
|
||||||
void GateStereo<N>::process(int16_t* input, int16_t* output, int numFrames) {
|
bool GateStereo<N>::process(int16_t* input, int16_t* output, int numFrames) {
|
||||||
|
|
||||||
clearHistogram();
|
clearHistogram();
|
||||||
|
int32_t mask = 0;
|
||||||
|
|
||||||
for (int n = 0; n < numFrames; n++) {
|
for (int n = 0; n < numFrames; n++) {
|
||||||
|
|
||||||
|
@ -541,16 +554,23 @@ void GateStereo<N>::process(int16_t* input, int16_t* output, int numFrames) {
|
||||||
x1 = MULQ31(x1, attn);
|
x1 = MULQ31(x1, attn);
|
||||||
|
|
||||||
// store 16-bit output
|
// store 16-bit output
|
||||||
output[2*n+0] = (int16_t)saturateQ30(x0);
|
x0 = saturateQ30(x0);
|
||||||
output[2*n+1] = (int16_t)saturateQ30(x1);
|
x1 = saturateQ30(x1);
|
||||||
|
output[2*n+0] = (int16_t)x0;
|
||||||
|
output[2*n+1] = (int16_t)x1;
|
||||||
|
|
||||||
|
mask |= (x0 | x1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update adaptive threshold
|
// update adaptive threshold
|
||||||
processHistogram(numFrames);
|
processHistogram(numFrames);
|
||||||
|
return mask != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int N>
|
template<int N>
|
||||||
void GateStereo<N>::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
bool GateStereo<N>::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
||||||
|
|
||||||
|
int32_t mask = 0;
|
||||||
|
|
||||||
for (int n = 0; n < numFrames; n++) {
|
for (int n = 0; n < numFrames; n++) {
|
||||||
|
|
||||||
|
@ -561,9 +581,15 @@ void GateStereo<N>::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
||||||
_dc.process(x0, x1);
|
_dc.process(x0, x1);
|
||||||
|
|
||||||
// store 16-bit output
|
// store 16-bit output
|
||||||
output[2*n+0] = (int16_t)saturateQ30(x0);
|
x0 = saturateQ30(x0);
|
||||||
output[2*n+1] = (int16_t)saturateQ30(x1);
|
x1 = saturateQ30(x1);
|
||||||
|
output[2*n+0] = (int16_t)x0;
|
||||||
|
output[2*n+1] = (int16_t)x1;
|
||||||
|
|
||||||
|
mask |= (x0 | x1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return mask != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -580,14 +606,15 @@ public:
|
||||||
GateQuad(int sampleRate) : GateImpl(sampleRate) {}
|
GateQuad(int sampleRate) : GateImpl(sampleRate) {}
|
||||||
|
|
||||||
// interleaved quad input/output (in-place is allowed)
|
// interleaved quad input/output (in-place is allowed)
|
||||||
void process(int16_t* input, int16_t* output, int numFrames) override;
|
bool process(int16_t* input, int16_t* output, int numFrames) override;
|
||||||
void removeDC(int16_t* input, int16_t* output, int numFrames) override;
|
bool removeDC(int16_t* input, int16_t* output, int numFrames) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<int N>
|
template<int N>
|
||||||
void GateQuad<N>::process(int16_t* input, int16_t* output, int numFrames) {
|
bool GateQuad<N>::process(int16_t* input, int16_t* output, int numFrames) {
|
||||||
|
|
||||||
clearHistogram();
|
clearHistogram();
|
||||||
|
int32_t mask = 0;
|
||||||
|
|
||||||
for (int n = 0; n < numFrames; n++) {
|
for (int n = 0; n < numFrames; n++) {
|
||||||
|
|
||||||
|
@ -636,18 +663,27 @@ void GateQuad<N>::process(int16_t* input, int16_t* output, int numFrames) {
|
||||||
x3 = MULQ31(x3, attn);
|
x3 = MULQ31(x3, attn);
|
||||||
|
|
||||||
// store 16-bit output
|
// store 16-bit output
|
||||||
output[4*n+0] = (int16_t)saturateQ30(x0);
|
x0 = saturateQ30(x0);
|
||||||
output[4*n+1] = (int16_t)saturateQ30(x1);
|
x1 = saturateQ30(x1);
|
||||||
output[4*n+2] = (int16_t)saturateQ30(x2);
|
x2 = saturateQ30(x2);
|
||||||
output[4*n+3] = (int16_t)saturateQ30(x3);
|
x3 = saturateQ30(x3);
|
||||||
|
output[4*n+0] = (int16_t)x0;
|
||||||
|
output[4*n+1] = (int16_t)x1;
|
||||||
|
output[4*n+2] = (int16_t)x2;
|
||||||
|
output[4*n+3] = (int16_t)x3;
|
||||||
|
|
||||||
|
mask |= (x0 | x1 | x2 | x3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update adaptive threshold
|
// update adaptive threshold
|
||||||
processHistogram(numFrames);
|
processHistogram(numFrames);
|
||||||
|
return mask != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<int N>
|
template<int N>
|
||||||
void GateQuad<N>::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
bool GateQuad<N>::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
||||||
|
|
||||||
|
int32_t mask = 0;
|
||||||
|
|
||||||
for (int n = 0; n < numFrames; n++) {
|
for (int n = 0; n < numFrames; n++) {
|
||||||
|
|
||||||
|
@ -660,11 +696,19 @@ void GateQuad<N>::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
||||||
_dc.process(x0, x1, x2, x3);
|
_dc.process(x0, x1, x2, x3);
|
||||||
|
|
||||||
// store 16-bit output
|
// store 16-bit output
|
||||||
output[4*n+0] = (int16_t)saturateQ30(x0);
|
x0 = saturateQ30(x0);
|
||||||
output[4*n+1] = (int16_t)saturateQ30(x1);
|
x1 = saturateQ30(x1);
|
||||||
output[4*n+2] = (int16_t)saturateQ30(x2);
|
x2 = saturateQ30(x2);
|
||||||
output[4*n+3] = (int16_t)saturateQ30(x3);
|
x3 = saturateQ30(x3);
|
||||||
|
output[4*n+0] = (int16_t)x0;
|
||||||
|
output[4*n+1] = (int16_t)x1;
|
||||||
|
output[4*n+2] = (int16_t)x2;
|
||||||
|
output[4*n+3] = (int16_t)x3;
|
||||||
|
|
||||||
|
mask |= (x0 | x1 | x2 | x3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return mask != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -721,12 +765,12 @@ AudioGate::~AudioGate() {
|
||||||
delete _impl;
|
delete _impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGate::render(int16_t* input, int16_t* output, int numFrames) {
|
bool AudioGate::render(int16_t* input, int16_t* output, int numFrames) {
|
||||||
_impl->process(input, output, numFrames);
|
return _impl->process(input, output, numFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGate::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
bool AudioGate::removeDC(int16_t* input, int16_t* output, int numFrames) {
|
||||||
_impl->removeDC(input, output, numFrames);
|
return _impl->removeDC(input, output, numFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioGate::setThreshold(float threshold) {
|
void AudioGate::setThreshold(float threshold) {
|
||||||
|
|
|
@ -18,9 +18,12 @@ public:
|
||||||
AudioGate(int sampleRate, int numChannels);
|
AudioGate(int sampleRate, int numChannels);
|
||||||
~AudioGate();
|
~AudioGate();
|
||||||
|
|
||||||
// interleaved int16_t input/output (in-place is allowed)
|
//
|
||||||
void render(int16_t* input, int16_t* output, int numFrames);
|
// Process interleaved int16_t input/output (in-place is allowed).
|
||||||
void removeDC(int16_t* input, int16_t* output, int numFrames);
|
// Returns true when output is non-zero.
|
||||||
|
//
|
||||||
|
bool render(int16_t* input, int16_t* output, int numFrames);
|
||||||
|
bool removeDC(int16_t* input, int16_t* output, int numFrames);
|
||||||
|
|
||||||
void setThreshold(float threshold);
|
void setThreshold(float threshold);
|
||||||
void setRelease(float release);
|
void setRelease(float release);
|
||||||
|
|
|
@ -1736,15 +1736,17 @@ void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||||
void Avatar::computeDetailedShapeInfo(ShapeInfo& shapeInfo, int jointIndex) {
|
void Avatar::computeDetailedShapeInfo(ShapeInfo& shapeInfo, int jointIndex) {
|
||||||
if (jointIndex > -1 && jointIndex < (int)_multiSphereShapes.size()) {
|
if (jointIndex > -1 && jointIndex < (int)_multiSphereShapes.size()) {
|
||||||
auto& data = _multiSphereShapes[jointIndex].getSpheresData();
|
auto& data = _multiSphereShapes[jointIndex].getSpheresData();
|
||||||
std::vector<glm::vec3> positions;
|
if (data.size() > 0) {
|
||||||
std::vector<btScalar> radiuses;
|
std::vector<glm::vec3> positions;
|
||||||
positions.reserve(data.size());
|
std::vector<btScalar> radiuses;
|
||||||
radiuses.reserve(data.size());
|
positions.reserve(data.size());
|
||||||
for (auto& sphere : data) {
|
radiuses.reserve(data.size());
|
||||||
positions.push_back(sphere._position);
|
for (auto& sphere : data) {
|
||||||
radiuses.push_back(sphere._radius);
|
positions.push_back(sphere._position);
|
||||||
|
radiuses.push_back(sphere._radius);
|
||||||
|
}
|
||||||
|
shapeInfo.setMultiSphere(positions, radiuses);
|
||||||
}
|
}
|
||||||
shapeInfo.setMultiSphere(positions, radiuses);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1975,12 +1977,12 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const {
|
||||||
auto& rig = _skeletonModel->getRig();
|
auto& rig = _skeletonModel->getRig();
|
||||||
|
|
||||||
// Normally the model offset transform will contain the avatar scale factor, we explicitly remove it here.
|
// Normally the model offset transform will contain the avatar scale factor, we explicitly remove it here.
|
||||||
AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), rig.getModelOffsetPose().rot(), rig.getModelOffsetPose().trans());
|
AnimPose modelOffsetWithoutAvatarScale(1.0f, rig.getModelOffsetPose().rot(), rig.getModelOffsetPose().trans());
|
||||||
AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * rig.getGeometryOffsetPose();
|
AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * rig.getGeometryOffsetPose();
|
||||||
|
|
||||||
// This factor can be used to scale distances in the geometry frame into the unscaled rig frame.
|
// This factor can be used to scale distances in the geometry frame into the unscaled rig frame.
|
||||||
// Typically it will be the unit conversion from cm to m.
|
// Typically it will be the unit conversion from cm to m.
|
||||||
float scaleFactor = geomToRigWithoutAvatarScale.scale().x; // in practice this always a uniform scale factor.
|
float scaleFactor = geomToRigWithoutAvatarScale.scale();
|
||||||
|
|
||||||
int headTopJoint = rig.indexOfJoint("HeadTop_End");
|
int headTopJoint = rig.indexOfJoint("HeadTop_End");
|
||||||
int headJoint = rig.indexOfJoint("Head");
|
int headJoint = rig.indexOfJoint("Head");
|
||||||
|
|
|
@ -249,14 +249,6 @@ bool SkeletonModel::getRightHandPosition(glm::vec3& position) const {
|
||||||
return getJointPositionInWorldFrame(getRightHandJointIndex(), position);
|
return getJointPositionInWorldFrame(getRightHandJointIndex(), position);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SkeletonModel::getLeftShoulderPosition(glm::vec3& position) const {
|
|
||||||
return getJointPositionInWorldFrame(getLastFreeJointIndex(getLeftHandJointIndex()), position);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkeletonModel::getRightShoulderPosition(glm::vec3& position) const {
|
|
||||||
return getJointPositionInWorldFrame(getLastFreeJointIndex(getRightHandJointIndex()), position);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const {
|
bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const {
|
||||||
return isActive() && getJointPositionInWorldFrame(_rig.indexOfJoint("Head"), headPosition);
|
return isActive() && getJointPositionInWorldFrame(_rig.indexOfJoint("Head"), headPosition);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,17 +57,9 @@ public:
|
||||||
/// \return true whether or not the position was found
|
/// \return true whether or not the position was found
|
||||||
bool getRightHandPosition(glm::vec3& position) const;
|
bool getRightHandPosition(glm::vec3& position) const;
|
||||||
|
|
||||||
/// Gets the position of the left shoulder.
|
|
||||||
/// \return whether or not the left shoulder joint was found
|
|
||||||
bool getLeftShoulderPosition(glm::vec3& position) const;
|
|
||||||
|
|
||||||
/// Returns the extended length from the left hand to its last free ancestor.
|
/// Returns the extended length from the left hand to its last free ancestor.
|
||||||
float getLeftArmLength() const;
|
float getLeftArmLength() const;
|
||||||
|
|
||||||
/// Gets the position of the right shoulder.
|
|
||||||
/// \return whether or not the right shoulder joint was found
|
|
||||||
bool getRightShoulderPosition(glm::vec3& position) const;
|
|
||||||
|
|
||||||
/// Returns the position of the head joint.
|
/// Returns the position of the head joint.
|
||||||
/// \return whether or not the head was found
|
/// \return whether or not the head was found
|
||||||
bool getHeadPosition(glm::vec3& headPosition) const;
|
bool getHeadPosition(glm::vec3& headPosition) const;
|
||||||
|
|
|
@ -107,11 +107,10 @@ int ClientTraitsHandler::sendChangedTraitsToMixer() {
|
||||||
|
|
||||||
if (initialSend || *simpleIt == Updated) {
|
if (initialSend || *simpleIt == Updated) {
|
||||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||||
bytesWritten += _owningAvatar->packTrait(traitType, *traitsPacketList);
|
|
||||||
|
|
||||||
// keep track of our skeleton version in case we get an override back
|
// keep track of our skeleton version in case we get an override back
|
||||||
_currentSkeletonVersion = _currentTraitVersion;
|
_currentSkeletonVersion = _currentTraitVersion;
|
||||||
}
|
}
|
||||||
|
bytesWritten += _owningAvatar->packTrait(traitType, *traitsPacketList);
|
||||||
}
|
}
|
||||||
|
|
||||||
++simpleIt;
|
++simpleIt;
|
||||||
|
|
|
@ -1289,6 +1289,17 @@ CalculateEntityLoadingPriority EntityTreeRenderer::_calculateEntityLoadingPriori
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::pair<bool, bool> EntityTreeRenderer::getZoneInteractionProperties() {
|
||||||
|
for (auto& zone : _layeredZones) {
|
||||||
|
// Only domain entities control flying allowed and ghosting allowed
|
||||||
|
if (zone.zone && zone.zone->isDomainEntity()) {
|
||||||
|
return { zone.zone->getFlyingAllowed(), zone.zone->getGhostingAllowed() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { true, true };
|
||||||
|
}
|
||||||
|
|
||||||
bool EntityTreeRenderer::wantsKeyboardFocus(const EntityItemID& id) const {
|
bool EntityTreeRenderer::wantsKeyboardFocus(const EntityItemID& id) const {
|
||||||
auto renderable = renderableForEntityId(id);
|
auto renderable = renderableForEntityId(id);
|
||||||
if (!renderable) {
|
if (!renderable) {
|
||||||
|
|
|
@ -105,7 +105,7 @@ public:
|
||||||
// For Scene.shouldRenderEntities
|
// For Scene.shouldRenderEntities
|
||||||
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
||||||
|
|
||||||
std::shared_ptr<ZoneEntityItem> myAvatarZone() { return _layeredZones.getZone(); }
|
std::pair<bool, bool> getZoneInteractionProperties();
|
||||||
|
|
||||||
bool wantsKeyboardFocus(const EntityItemID& id) const;
|
bool wantsKeyboardFocus(const EntityItemID& id) const;
|
||||||
QObject* getEventHandler(const EntityItemID& id);
|
QObject* getEventHandler(const EntityItemID& id);
|
||||||
|
|
|
@ -959,23 +959,6 @@ QStringList RenderableModelEntityItem::getJointNames() const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableModelEntityItem::setAnimationURL(const QString& url) {
|
|
||||||
QString oldURL = getAnimationURL();
|
|
||||||
ModelEntityItem::setAnimationURL(url);
|
|
||||||
if (oldURL != getAnimationURL()) {
|
|
||||||
_needsAnimationReset = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RenderableModelEntityItem::needsAnimationReset() const {
|
|
||||||
return _needsAnimationReset;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString RenderableModelEntityItem::getAnimationURLAndReset() {
|
|
||||||
_needsAnimationReset = false;
|
|
||||||
return getAnimationURL();
|
|
||||||
}
|
|
||||||
|
|
||||||
scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel() {
|
scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel() {
|
||||||
auto model = resultWithReadLock<ModelPointer>([this]{ return _model; });
|
auto model = resultWithReadLock<ModelPointer>([this]{ return _model; });
|
||||||
|
|
||||||
|
@ -1264,7 +1247,7 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_lastTextures != entity->getTextures()) {
|
if (_textures != entity->getTextures()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1418,15 +1401,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
||||||
entity->_originalTexturesRead = true;
|
entity->_originalTexturesRead = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_lastTextures != entity->getTextures()) {
|
if (_textures != entity->getTextures()) {
|
||||||
|
QVariantMap newTextures;
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
_texturesLoaded = false;
|
_texturesLoaded = false;
|
||||||
_lastTextures = entity->getTextures();
|
_textures = entity->getTextures();
|
||||||
|
newTextures = parseTexturesToMap(_textures, entity->_originalTextures);
|
||||||
});
|
});
|
||||||
auto newTextures = parseTexturesToMap(_lastTextures, entity->_originalTextures);
|
model->setTextures(newTextures);
|
||||||
if (newTextures != model->getTextures()) {
|
|
||||||
model->setTextures(newTextures);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (entity->_needsJointSimulation) {
|
if (entity->_needsJointSimulation) {
|
||||||
entity->copyAnimationJointDataToModel();
|
entity->copyAnimationJointDataToModel();
|
||||||
|
@ -1494,11 +1476,17 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
||||||
if (_animating) {
|
if (_animating) {
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, "Animate");
|
DETAILED_PROFILE_RANGE(simulation_physics, "Animate");
|
||||||
|
|
||||||
if (_animation && entity->needsAnimationReset()) {
|
auto animationURL = entity->getAnimationURL();
|
||||||
//(_animation->getURL().toString() != entity->getAnimationURL())) { // bad check
|
bool animationChanged = _animationURL != animationURL;
|
||||||
// the joints have been mapped before but we have a new animation to load
|
if (animationChanged) {
|
||||||
_animation.reset();
|
_animationURL = animationURL;
|
||||||
_jointMappingCompleted = false;
|
|
||||||
|
if (_animation) {
|
||||||
|
//(_animation->getURL().toString() != entity->getAnimationURL())) { // bad check
|
||||||
|
// the joints have been mapped before but we have a new animation to load
|
||||||
|
_animation.reset();
|
||||||
|
_jointMappingCompleted = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_jointMappingCompleted) {
|
if (!_jointMappingCompleted) {
|
||||||
|
@ -1563,7 +1551,7 @@ void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const Mode
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_animation) {
|
if (!_animation) {
|
||||||
_animation = DependencyManager::get<AnimationCache>()->getAnimation(entity->getAnimationURLAndReset());
|
_animation = DependencyManager::get<AnimationCache>()->getAnimation(_animationURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_animation && _animation->isLoaded()) {
|
if (_animation && _animation->isLoaded()) {
|
||||||
|
|
|
@ -113,10 +113,6 @@ public:
|
||||||
virtual int getJointIndex(const QString& name) const override;
|
virtual int getJointIndex(const QString& name) const override;
|
||||||
virtual QStringList getJointNames() const override;
|
virtual QStringList getJointNames() const override;
|
||||||
|
|
||||||
void setAnimationURL(const QString& url) override;
|
|
||||||
bool needsAnimationReset() const;
|
|
||||||
QString getAnimationURLAndReset();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool needsUpdateModelBounds() const;
|
bool needsUpdateModelBounds() const;
|
||||||
void autoResizeJointArrays();
|
void autoResizeJointArrays();
|
||||||
|
@ -131,7 +127,6 @@ private:
|
||||||
bool _originalTexturesRead { false };
|
bool _originalTexturesRead { false };
|
||||||
bool _dimensionsInitialized { true };
|
bool _dimensionsInitialized { true };
|
||||||
bool _needsJointSimulation { false };
|
bool _needsJointSimulation { false };
|
||||||
bool _needsAnimationReset { false };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace render { namespace entities {
|
namespace render { namespace entities {
|
||||||
|
@ -181,7 +176,7 @@ private:
|
||||||
|
|
||||||
bool _hasModel { false };
|
bool _hasModel { false };
|
||||||
ModelPointer _model;
|
ModelPointer _model;
|
||||||
QString _lastTextures;
|
QString _textures;
|
||||||
bool _texturesLoaded { false };
|
bool _texturesLoaded { false };
|
||||||
int _lastKnownCurrentFrame { -1 };
|
int _lastKnownCurrentFrame { -1 };
|
||||||
#ifdef MODEL_ENTITY_USE_FADE_EFFECT
|
#ifdef MODEL_ENTITY_USE_FADE_EFFECT
|
||||||
|
@ -190,12 +185,12 @@ private:
|
||||||
|
|
||||||
const void* _collisionMeshKey { nullptr };
|
const void* _collisionMeshKey { nullptr };
|
||||||
|
|
||||||
// used on client side
|
QUrl _parsedModelURL;
|
||||||
bool _jointMappingCompleted { false };
|
bool _jointMappingCompleted { false };
|
||||||
QVector<int> _jointMapping; // domain is index into model-joints, range is index into animation-joints
|
QVector<int> _jointMapping; // domain is index into model-joints, range is index into animation-joints
|
||||||
AnimationPointer _animation;
|
AnimationPointer _animation;
|
||||||
QUrl _parsedModelURL;
|
|
||||||
bool _animating { false };
|
bool _animating { false };
|
||||||
|
QString _animationURL;
|
||||||
uint64_t _lastAnimated { 0 };
|
uint64_t _lastAnimated { 0 };
|
||||||
|
|
||||||
render::ItemKey _itemKey { render::ItemKey::Builder().withTypeMeta() };
|
render::ItemKey _itemKey { render::ItemKey::Builder().withTypeMeta() };
|
||||||
|
|
|
@ -198,24 +198,33 @@ void ZoneEntityRenderer::removeFromScene(const ScenePointer& scene, Transaction&
|
||||||
void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) {
|
||||||
DependencyManager::get<EntityTreeRenderer>()->updateZone(entity->getID());
|
DependencyManager::get<EntityTreeRenderer>()->updateZone(entity->getID());
|
||||||
|
|
||||||
|
auto position = entity->getWorldPosition();
|
||||||
|
auto rotation = entity->getWorldOrientation();
|
||||||
|
auto dimensions = entity->getScaledDimensions();
|
||||||
|
bool rotationChanged = rotation != _lastRotation;
|
||||||
|
bool transformChanged = rotationChanged || position != _lastPosition || dimensions != _lastDimensions;
|
||||||
|
|
||||||
|
auto proceduralUserData = entity->getUserData();
|
||||||
|
bool proceduralUserDataChanged = _proceduralUserData != proceduralUserData;
|
||||||
|
|
||||||
// FIXME one of the bools here could become true between being fetched and being reset,
|
// FIXME one of the bools here could become true between being fetched and being reset,
|
||||||
// resulting in a lost update
|
// resulting in a lost update
|
||||||
bool keyLightChanged = entity->keyLightPropertiesChanged();
|
bool keyLightChanged = entity->keyLightPropertiesChanged() || rotationChanged;
|
||||||
bool ambientLightChanged = entity->ambientLightPropertiesChanged();
|
bool ambientLightChanged = entity->ambientLightPropertiesChanged() || transformChanged;
|
||||||
bool skyboxChanged = entity->skyboxPropertiesChanged();
|
bool skyboxChanged = entity->skyboxPropertiesChanged() || proceduralUserDataChanged;
|
||||||
bool hazeChanged = entity->hazePropertiesChanged();
|
bool hazeChanged = entity->hazePropertiesChanged();
|
||||||
bool bloomChanged = entity->bloomPropertiesChanged();
|
bool bloomChanged = entity->bloomPropertiesChanged();
|
||||||
|
|
||||||
entity->resetRenderingPropertiesChanged();
|
entity->resetRenderingPropertiesChanged();
|
||||||
_lastPosition = entity->getWorldPosition();
|
|
||||||
_lastRotation = entity->getWorldOrientation();
|
|
||||||
_lastDimensions = entity->getScaledDimensions();
|
|
||||||
|
|
||||||
_keyLightProperties = entity->getKeyLightProperties();
|
if (transformChanged) {
|
||||||
_ambientLightProperties = entity->getAmbientLightProperties();
|
_lastPosition = entity->getWorldPosition();
|
||||||
_skyboxProperties = entity->getSkyboxProperties();
|
_lastRotation = entity->getWorldOrientation();
|
||||||
_hazeProperties = entity->getHazeProperties();
|
_lastDimensions = entity->getScaledDimensions();
|
||||||
_bloomProperties = entity->getBloomProperties();
|
}
|
||||||
|
|
||||||
|
if (proceduralUserDataChanged) {
|
||||||
|
_proceduralUserData = entity->getUserData();
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if (_lastShapeURL != _typedEntity->getCompoundShapeURL()) {
|
if (_lastShapeURL != _typedEntity->getCompoundShapeURL()) {
|
||||||
|
@ -239,21 +248,29 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
||||||
updateKeyZoneItemFromEntity(entity);
|
updateKeyZoneItemFromEntity(entity);
|
||||||
|
|
||||||
if (keyLightChanged) {
|
if (keyLightChanged) {
|
||||||
|
_keyLightProperties = entity->getKeyLightProperties();
|
||||||
updateKeySunFromEntity(entity);
|
updateKeySunFromEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ambientLightChanged) {
|
if (ambientLightChanged) {
|
||||||
|
_ambientLightProperties = entity->getAmbientLightProperties();
|
||||||
updateAmbientLightFromEntity(entity);
|
updateAmbientLightFromEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skyboxChanged || _proceduralUserData != entity->getUserData()) {
|
if (skyboxChanged) {
|
||||||
|
_skyboxProperties = entity->getSkyboxProperties();
|
||||||
updateKeyBackgroundFromEntity(entity);
|
updateKeyBackgroundFromEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hazeChanged) {
|
if (hazeChanged) {
|
||||||
|
_hazeProperties = entity->getHazeProperties();
|
||||||
updateHazeFromEntity(entity);
|
updateHazeFromEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bloomChanged) {
|
||||||
|
_bloomProperties = entity->getBloomProperties();
|
||||||
|
updateBloomFromEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
bool visuallyReady = true;
|
bool visuallyReady = true;
|
||||||
uint32_t skyboxMode = entity->getSkyboxMode();
|
uint32_t skyboxMode = entity->getSkyboxMode();
|
||||||
|
@ -264,10 +281,6 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
||||||
}
|
}
|
||||||
|
|
||||||
entity->setVisuallyReady(visuallyReady);
|
entity->setVisuallyReady(visuallyReady);
|
||||||
|
|
||||||
if (bloomChanged) {
|
|
||||||
updateBloomFromEntity(entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||||
|
@ -344,7 +357,7 @@ void ZoneEntityRenderer::updateKeySunFromEntity(const TypedEntityPointer& entity
|
||||||
// Set the keylight
|
// Set the keylight
|
||||||
sunLight->setColor(ColorUtils::toVec3(_keyLightProperties.getColor()));
|
sunLight->setColor(ColorUtils::toVec3(_keyLightProperties.getColor()));
|
||||||
sunLight->setIntensity(_keyLightProperties.getIntensity());
|
sunLight->setIntensity(_keyLightProperties.getIntensity());
|
||||||
sunLight->setDirection(entity->getTransform().getRotation() * _keyLightProperties.getDirection());
|
sunLight->setDirection(_lastRotation * _keyLightProperties.getDirection());
|
||||||
sunLight->setCastShadows(_keyLightProperties.getCastShadows());
|
sunLight->setCastShadows(_keyLightProperties.getCastShadows());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +369,6 @@ void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer&
|
||||||
ambientLight->setPosition(_lastPosition);
|
ambientLight->setPosition(_lastPosition);
|
||||||
ambientLight->setOrientation(_lastRotation);
|
ambientLight->setOrientation(_lastRotation);
|
||||||
|
|
||||||
|
|
||||||
// Set the ambient light
|
// Set the ambient light
|
||||||
ambientLight->setAmbientIntensity(_ambientLightProperties.getAmbientIntensity());
|
ambientLight->setAmbientIntensity(_ambientLightProperties.getAmbientIntensity());
|
||||||
|
|
||||||
|
@ -395,8 +407,6 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity)
|
||||||
haze->setHazeAttenuateKeyLight(_hazeProperties.getHazeAttenuateKeyLight());
|
haze->setHazeAttenuateKeyLight(_hazeProperties.getHazeAttenuateKeyLight());
|
||||||
haze->setHazeKeyLightRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeKeyLightRange()));
|
haze->setHazeKeyLightRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeKeyLightRange()));
|
||||||
haze->setHazeKeyLightAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(_hazeProperties.getHazeKeyLightAltitude()));
|
haze->setHazeKeyLightAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(_hazeProperties.getHazeKeyLightAltitude()));
|
||||||
|
|
||||||
haze->setTransform(entity->getTransform().getMatrix());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity) {
|
void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity) {
|
||||||
|
@ -414,13 +424,13 @@ void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer&
|
||||||
|
|
||||||
editBackground();
|
editBackground();
|
||||||
setSkyboxColor(toGlm(_skyboxProperties.getColor()));
|
setSkyboxColor(toGlm(_skyboxProperties.getColor()));
|
||||||
setProceduralUserData(entity->getUserData());
|
setProceduralUserData(_proceduralUserData);
|
||||||
setSkyboxURL(_skyboxProperties.getURL());
|
setSkyboxURL(_skyboxProperties.getURL());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneEntityRenderer::updateKeyZoneItemFromEntity(const TypedEntityPointer& entity) {
|
void ZoneEntityRenderer::updateKeyZoneItemFromEntity(const TypedEntityPointer& entity) {
|
||||||
// Update rotation values
|
// Update rotation values
|
||||||
editSkybox()->setOrientation(entity->getTransform().getRotation());
|
editSkybox()->setOrientation(_lastRotation);
|
||||||
|
|
||||||
/* TODO: Implement the sun model behavior / Keep this code here for reference, this is how we
|
/* TODO: Implement the sun model behavior / Keep this code here for reference, this is how we
|
||||||
{
|
{
|
||||||
|
@ -540,9 +550,6 @@ void ZoneEntityRenderer::setSkyboxColor(const glm::vec3& color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneEntityRenderer::setProceduralUserData(const QString& userData) {
|
void ZoneEntityRenderer::setProceduralUserData(const QString& userData) {
|
||||||
if (_proceduralUserData != userData) {
|
std::dynamic_pointer_cast<ProceduralSkybox>(editSkybox())->parse(userData);
|
||||||
_proceduralUserData = userData;
|
|
||||||
std::dynamic_pointer_cast<ProceduralSkybox>(editSkybox())->parse(_proceduralUserData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1414,8 +1414,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
* @property {Entities.Bloom} bloom - The bloom properties of the zone.
|
* @property {Entities.Bloom} bloom - The bloom properties of the zone.
|
||||||
*
|
*
|
||||||
* @property {boolean} flyingAllowed=true - If <code>true</code> then visitors can fly in the zone; otherwise they cannot.
|
* @property {boolean} flyingAllowed=true - If <code>true</code> then visitors can fly in the zone; otherwise they cannot.
|
||||||
|
* Only works on domain entities.
|
||||||
* @property {boolean} ghostingAllowed=true - If <code>true</code> then visitors with avatar collisions turned off will not
|
* @property {boolean} ghostingAllowed=true - If <code>true</code> then visitors with avatar collisions turned off will not
|
||||||
* collide with content in the zone; otherwise visitors will always collide with content in the zone.
|
* collide with content in the zone; otherwise visitors will always collide with content in the zone. Only works on domain entities.
|
||||||
|
|
||||||
* @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the
|
* @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the
|
||||||
* zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to
|
* zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to
|
||||||
|
|
|
@ -43,14 +43,15 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString ModelEntityItem::getTextures() const {
|
const QString ModelEntityItem::getTextures() const {
|
||||||
QReadLocker locker(&_texturesLock);
|
return resultWithReadLock<QString>([&] {
|
||||||
auto textures = _textures;
|
return _textures;
|
||||||
return textures;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelEntityItem::setTextures(const QString& textures) {
|
void ModelEntityItem::setTextures(const QString& textures) {
|
||||||
QWriteLocker locker(&_texturesLock);
|
withWriteLock([&] {
|
||||||
_textures = textures;
|
_textures = textures;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
|
EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
|
||||||
|
|
|
@ -163,7 +163,6 @@ protected:
|
||||||
|
|
||||||
AnimationPropertyGroup _animationProperties;
|
AnimationPropertyGroup _animationProperties;
|
||||||
|
|
||||||
mutable QReadWriteLock _texturesLock;
|
|
||||||
QString _textures;
|
QString _textures;
|
||||||
|
|
||||||
ShapeType _shapeType = SHAPE_TYPE_NONE;
|
ShapeType _shapeType = SHAPE_TYPE_NONE;
|
||||||
|
|
|
@ -119,7 +119,7 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode);
|
||||||
|
|
||||||
somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged ||
|
somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged ||
|
||||||
_stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged;
|
_skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged;
|
||||||
|
|
||||||
return somethingChanged;
|
return somethingChanged;
|
||||||
}
|
}
|
||||||
|
@ -394,7 +394,6 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() {
|
||||||
_skyboxPropertiesChanged = false;
|
_skyboxPropertiesChanged = false;
|
||||||
_hazePropertiesChanged = false;
|
_hazePropertiesChanged = false;
|
||||||
_bloomPropertiesChanged = false;
|
_bloomPropertiesChanged = false;
|
||||||
_stagePropertiesChanged = false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,8 +102,6 @@ public:
|
||||||
bool hazePropertiesChanged() const { return _hazePropertiesChanged; }
|
bool hazePropertiesChanged() const { return _hazePropertiesChanged; }
|
||||||
bool bloomPropertiesChanged() const { return _bloomPropertiesChanged; }
|
bool bloomPropertiesChanged() const { return _bloomPropertiesChanged; }
|
||||||
|
|
||||||
bool stagePropertiesChanged() const { return _stagePropertiesChanged; }
|
|
||||||
|
|
||||||
void resetRenderingPropertiesChanged();
|
void resetRenderingPropertiesChanged();
|
||||||
|
|
||||||
virtual bool supportsDetailedIntersection() const override { return true; }
|
virtual bool supportsDetailedIntersection() const override { return true; }
|
||||||
|
@ -155,7 +153,6 @@ protected:
|
||||||
bool _skyboxPropertiesChanged { false };
|
bool _skyboxPropertiesChanged { false };
|
||||||
bool _hazePropertiesChanged{ false };
|
bool _hazePropertiesChanged{ false };
|
||||||
bool _bloomPropertiesChanged { false };
|
bool _bloomPropertiesChanged { false };
|
||||||
bool _stagePropertiesChanged { false };
|
|
||||||
|
|
||||||
static bool _drawZoneBoundaries;
|
static bool _drawZoneBoundaries;
|
||||||
static bool _zonesArePickable;
|
static bool _zonesArePickable;
|
||||||
|
|
|
@ -384,43 +384,6 @@ QByteArray fileOnUrl(const QByteArray& filepath, const QString& url) {
|
||||||
return filepath.mid(filepath.lastIndexOf('/') + 1);
|
return filepath.mid(filepath.lastIndexOf('/') + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<QString, QString> getJointNameMapping(const QVariantHash& mapping) {
|
|
||||||
static const QString JOINT_NAME_MAPPING_FIELD = "jointMap";
|
|
||||||
QMap<QString, QString> hfmToHifiJointNameMap;
|
|
||||||
if (!mapping.isEmpty() && mapping.contains(JOINT_NAME_MAPPING_FIELD) && mapping[JOINT_NAME_MAPPING_FIELD].type() == QVariant::Hash) {
|
|
||||||
auto jointNames = mapping[JOINT_NAME_MAPPING_FIELD].toHash();
|
|
||||||
for (auto itr = jointNames.begin(); itr != jointNames.end(); itr++) {
|
|
||||||
hfmToHifiJointNameMap.insert(itr.key(), itr.value().toString());
|
|
||||||
qCDebug(modelformat) << "the mapped key " << itr.key() << " has a value of " << hfmToHifiJointNameMap[itr.key()];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hfmToHifiJointNameMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMap<QString, glm::quat> getJointRotationOffsets(const QVariantHash& mapping) {
|
|
||||||
QMap<QString, glm::quat> jointRotationOffsets;
|
|
||||||
static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset";
|
|
||||||
if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) {
|
|
||||||
auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash();
|
|
||||||
for (auto itr = offsets.begin(); itr != offsets.end(); itr++) {
|
|
||||||
QString jointName = itr.key();
|
|
||||||
QString line = itr.value().toString();
|
|
||||||
auto quatCoords = line.split(',');
|
|
||||||
if (quatCoords.size() == 4) {
|
|
||||||
float quatX = quatCoords[0].mid(1).toFloat();
|
|
||||||
float quatY = quatCoords[1].toFloat();
|
|
||||||
float quatZ = quatCoords[2].toFloat();
|
|
||||||
float quatW = quatCoords[3].mid(0, quatCoords[3].size() - 1).toFloat();
|
|
||||||
if (!isNaN(quatX) && !isNaN(quatY) && !isNaN(quatZ) && !isNaN(quatW)) {
|
|
||||||
glm::quat rotationOffset = glm::quat(quatW, quatX, quatY, quatZ);
|
|
||||||
jointRotationOffsets.insert(jointName, rotationOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return jointRotationOffsets;
|
|
||||||
}
|
|
||||||
|
|
||||||
HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QString& url) {
|
HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QString& url) {
|
||||||
const FBXNode& node = _rootNode;
|
const FBXNode& node = _rootNode;
|
||||||
QMap<QString, ExtractedMesh> meshes;
|
QMap<QString, ExtractedMesh> meshes;
|
||||||
|
@ -444,8 +407,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
||||||
|
|
||||||
std::map<QString, HFMLight> lights;
|
std::map<QString, HFMLight> lights;
|
||||||
|
|
||||||
QVariantHash joints = mapping.value("joint").toHash();
|
|
||||||
|
|
||||||
QVariantHash blendshapeMappings = mapping.value("bs").toHash();
|
QVariantHash blendshapeMappings = mapping.value("bs").toHash();
|
||||||
|
|
||||||
QMultiHash<QByteArray, WeightedIndex> blendshapeIndices;
|
QMultiHash<QByteArray, WeightedIndex> blendshapeIndices;
|
||||||
|
@ -473,8 +434,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
||||||
HFMModel& hfmModel = *hfmModelPtr;
|
HFMModel& hfmModel = *hfmModelPtr;
|
||||||
|
|
||||||
hfmModel.originalURL = url;
|
hfmModel.originalURL = url;
|
||||||
hfmModel.hfmToHifiJointNameMapping.clear();
|
|
||||||
hfmModel.hfmToHifiJointNameMapping = getJointNameMapping(mapping);
|
|
||||||
|
|
||||||
float unitScaleFactor = 1.0f;
|
float unitScaleFactor = 1.0f;
|
||||||
glm::vec3 ambientColor;
|
glm::vec3 ambientColor;
|
||||||
|
@ -1287,26 +1246,14 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert the models to joints
|
// convert the models to joints
|
||||||
QVariantList freeJoints = mapping.values("freeJoint");
|
|
||||||
hfmModel.hasSkeletonJoints = false;
|
hfmModel.hasSkeletonJoints = false;
|
||||||
|
|
||||||
foreach (const QString& modelID, modelIDs) {
|
foreach (const QString& modelID, modelIDs) {
|
||||||
const FBXModel& fbxModel = fbxModels[modelID];
|
const FBXModel& fbxModel = fbxModels[modelID];
|
||||||
HFMJoint joint;
|
HFMJoint joint;
|
||||||
joint.isFree = freeJoints.contains(fbxModel.name);
|
|
||||||
joint.parentIndex = fbxModel.parentIndex;
|
joint.parentIndex = fbxModel.parentIndex;
|
||||||
|
|
||||||
// get the indices of all ancestors starting with the first free one (if any)
|
|
||||||
int jointIndex = hfmModel.joints.size();
|
int jointIndex = hfmModel.joints.size();
|
||||||
joint.freeLineage.append(jointIndex);
|
|
||||||
int lastFreeIndex = joint.isFree ? 0 : -1;
|
|
||||||
for (int index = joint.parentIndex; index != -1; index = hfmModel.joints.at(index).parentIndex) {
|
|
||||||
if (hfmModel.joints.at(index).isFree) {
|
|
||||||
lastFreeIndex = joint.freeLineage.size();
|
|
||||||
}
|
|
||||||
joint.freeLineage.append(index);
|
|
||||||
}
|
|
||||||
joint.freeLineage.remove(lastFreeIndex + 1, joint.freeLineage.size() - lastFreeIndex - 1);
|
|
||||||
joint.translation = fbxModel.translation; // these are usually in centimeters
|
joint.translation = fbxModel.translation; // these are usually in centimeters
|
||||||
joint.preTransform = fbxModel.preTransform;
|
joint.preTransform = fbxModel.preTransform;
|
||||||
joint.preRotation = fbxModel.preRotation;
|
joint.preRotation = fbxModel.preRotation;
|
||||||
|
@ -1341,14 +1288,10 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
||||||
}
|
}
|
||||||
joint.inverseBindRotation = joint.inverseDefaultRotation;
|
joint.inverseBindRotation = joint.inverseDefaultRotation;
|
||||||
joint.name = fbxModel.name;
|
joint.name = fbxModel.name;
|
||||||
if (hfmModel.hfmToHifiJointNameMapping.contains(hfmModel.hfmToHifiJointNameMapping.key(joint.name))) {
|
|
||||||
joint.name = hfmModel.hfmToHifiJointNameMapping.key(fbxModel.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
joint.bindTransformFoundInCluster = false;
|
joint.bindTransformFoundInCluster = false;
|
||||||
|
|
||||||
hfmModel.joints.append(joint);
|
hfmModel.joints.append(joint);
|
||||||
hfmModel.jointIndices.insert(joint.name, hfmModel.joints.size());
|
|
||||||
|
|
||||||
QString rotationID = localRotations.value(modelID);
|
QString rotationID = localRotations.value(modelID);
|
||||||
AnimationCurve xRotCurve = animationCurves.value(xComponents.value(rotationID));
|
AnimationCurve xRotCurve = animationCurves.value(xComponents.value(rotationID));
|
||||||
|
@ -1704,7 +1647,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
||||||
generateBoundryLinesForDop14(joint.shapeInfo.dots, joint.shapeInfo.avgPoint, joint.shapeInfo.debugLines);
|
generateBoundryLinesForDop14(joint.shapeInfo.dots, joint.shapeInfo.avgPoint, joint.shapeInfo.debugLines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hfmModel.palmDirection = parseVec3(mapping.value("palmDirection", "0, -1, 0").toString());
|
|
||||||
|
|
||||||
// attempt to map any meshes to a named model
|
// attempt to map any meshes to a named model
|
||||||
for (QHash<QString, int>::const_iterator m = meshIDsToMeshIndices.constBegin();
|
for (QHash<QString, int>::const_iterator m = meshIDsToMeshIndices.constBegin();
|
||||||
|
@ -1722,21 +1664,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto offsets = getJointRotationOffsets(mapping);
|
|
||||||
hfmModel.jointRotationOffsets.clear();
|
|
||||||
for (auto itr = offsets.begin(); itr != offsets.end(); itr++) {
|
|
||||||
QString jointName = itr.key();
|
|
||||||
glm::quat rotationOffset = itr.value();
|
|
||||||
int jointIndex = hfmModel.getJointIndex(jointName);
|
|
||||||
if (hfmModel.hfmToHifiJointNameMapping.contains(jointName)) {
|
|
||||||
jointIndex = hfmModel.getJointIndex(jointName);
|
|
||||||
}
|
|
||||||
if (jointIndex != -1) {
|
|
||||||
hfmModel.jointRotationOffsets.insert(jointIndex, rotationOffset);
|
|
||||||
}
|
|
||||||
qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hfmModelPtr;
|
return hfmModelPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,11 +82,6 @@ FST* FST::createFSTFromModel(const QString& fstPath, const QString& modelFilePat
|
||||||
}
|
}
|
||||||
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
|
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
|
||||||
|
|
||||||
mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm");
|
|
||||||
mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm");
|
|
||||||
mapping.insertMulti(FREE_JOINT_FIELD, "RightArm");
|
|
||||||
mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm");
|
|
||||||
|
|
||||||
|
|
||||||
// If there are no blendshape mappings, and we detect that this is likely a mixamo file,
|
// If there are no blendshape mappings, and we detect that this is likely a mixamo file,
|
||||||
// then we can add the default mixamo to "faceshift" mappings
|
// then we can add the default mixamo to "faceshift" mappings
|
||||||
|
|
|
@ -84,7 +84,7 @@ void FSTReader::writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it)
|
||||||
|
|
||||||
QByteArray FSTReader::writeMapping(const QVariantHash& mapping) {
|
QByteArray FSTReader::writeMapping(const QVariantHash& mapping) {
|
||||||
static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << TYPE_FIELD << SCALE_FIELD << FILENAME_FIELD
|
static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << TYPE_FIELD << SCALE_FIELD << FILENAME_FIELD
|
||||||
<< MARKETPLACE_ID_FIELD << TEXDIR_FIELD << SCRIPT_FIELD << JOINT_FIELD << FREE_JOINT_FIELD
|
<< MARKETPLACE_ID_FIELD << TEXDIR_FIELD << SCRIPT_FIELD << JOINT_FIELD
|
||||||
<< BLENDSHAPE_FIELD << JOINT_INDEX_FIELD;
|
<< BLENDSHAPE_FIELD << JOINT_INDEX_FIELD;
|
||||||
QBuffer buffer;
|
QBuffer buffer;
|
||||||
buffer.open(QIODevice::WriteOnly);
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
@ -92,7 +92,7 @@ QByteArray FSTReader::writeMapping(const QVariantHash& mapping) {
|
||||||
for (auto key : PREFERED_ORDER) {
|
for (auto key : PREFERED_ORDER) {
|
||||||
auto it = mapping.find(key);
|
auto it = mapping.find(key);
|
||||||
if (it != mapping.constEnd()) {
|
if (it != mapping.constEnd()) {
|
||||||
if (key == FREE_JOINT_FIELD || key == SCRIPT_FIELD) { // writeVariant does not handle strings added using insertMulti.
|
if (key == SCRIPT_FIELD) { // writeVariant does not handle strings added using insertMulti.
|
||||||
for (auto multi : mapping.values(key)) {
|
for (auto multi : mapping.values(key)) {
|
||||||
buffer.write(key.toUtf8());
|
buffer.write(key.toUtf8());
|
||||||
buffer.write(" = ");
|
buffer.write(" = ");
|
||||||
|
|
|
@ -27,7 +27,6 @@ static const QString TRANSLATION_X_FIELD = "tx";
|
||||||
static const QString TRANSLATION_Y_FIELD = "ty";
|
static const QString TRANSLATION_Y_FIELD = "ty";
|
||||||
static const QString TRANSLATION_Z_FIELD = "tz";
|
static const QString TRANSLATION_Z_FIELD = "tz";
|
||||||
static const QString JOINT_FIELD = "joint";
|
static const QString JOINT_FIELD = "joint";
|
||||||
static const QString FREE_JOINT_FIELD = "freeJoint";
|
|
||||||
static const QString BLENDSHAPE_FIELD = "bs";
|
static const QString BLENDSHAPE_FIELD = "bs";
|
||||||
static const QString SCRIPT_FIELD = "script";
|
static const QString SCRIPT_FIELD = "script";
|
||||||
static const QString JOINT_NAME_MAPPING_FIELD = "jointMap";
|
static const QString JOINT_NAME_MAPPING_FIELD = "jointMap";
|
||||||
|
|
50
libraries/fbx/src/GLTFSerializer.cpp
Normal file → Executable file
50
libraries/fbx/src/GLTFSerializer.cpp
Normal file → Executable file
|
@ -352,9 +352,14 @@ bool GLTFSerializer::addImage(const QJsonObject& object) {
|
||||||
|
|
||||||
QString mime;
|
QString mime;
|
||||||
getStringVal(object, "uri", image.uri, image.defined);
|
getStringVal(object, "uri", image.uri, image.defined);
|
||||||
|
if (image.uri.contains("data:image/png;base64,")) {
|
||||||
|
image.mimeType = getImageMimeType("image/png");
|
||||||
|
} else if (image.uri.contains("data:image/jpeg;base64,")) {
|
||||||
|
image.mimeType = getImageMimeType("image/jpeg");
|
||||||
|
}
|
||||||
if (getStringVal(object, "mimeType", mime, image.defined)) {
|
if (getStringVal(object, "mimeType", mime, image.defined)) {
|
||||||
image.mimeType = getImageMimeType(mime);
|
image.mimeType = getImageMimeType(mime);
|
||||||
}
|
}
|
||||||
getIntVal(object, "bufferView", image.bufferView, image.defined);
|
getIntVal(object, "bufferView", image.bufferView, image.defined);
|
||||||
|
|
||||||
_file.images.push_back(image);
|
_file.images.push_back(image);
|
||||||
|
@ -719,7 +724,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
|
||||||
|
|
||||||
//Build default joints
|
//Build default joints
|
||||||
hfmModel.joints.resize(1);
|
hfmModel.joints.resize(1);
|
||||||
hfmModel.joints[0].isFree = false;
|
|
||||||
hfmModel.joints[0].parentIndex = -1;
|
hfmModel.joints[0].parentIndex = -1;
|
||||||
hfmModel.joints[0].distanceToParent = 0;
|
hfmModel.joints[0].distanceToParent = 0;
|
||||||
hfmModel.joints[0].translation = glm::vec3(0, 0, 0);
|
hfmModel.joints[0].translation = glm::vec3(0, 0, 0);
|
||||||
|
@ -831,6 +835,22 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const QUrl& url) {
|
||||||
for (int n = 0; n < normals.size(); n = n + 3) {
|
for (int n = 0; n < normals.size(); n = n + 3) {
|
||||||
mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2]));
|
mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2]));
|
||||||
}
|
}
|
||||||
|
} else if (key == "COLOR_0") {
|
||||||
|
QVector<float> colors;
|
||||||
|
success = addArrayOfType(buffer.blob,
|
||||||
|
bufferview.byteOffset + accBoffset,
|
||||||
|
accessor.count,
|
||||||
|
colors,
|
||||||
|
accessor.type,
|
||||||
|
accessor.componentType);
|
||||||
|
if (!success) {
|
||||||
|
qWarning(modelformat) << "There was a problem reading glTF COLOR_0 data for model " << _url;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int stride = (accessor.type == GLTFAccessorType::VEC4) ? 4 : 3;
|
||||||
|
for (int n = 0; n < colors.size() - 3; n += stride) {
|
||||||
|
mesh.colors.push_back(glm::vec3(colors[n], colors[n + 1], colors[n + 2]));
|
||||||
|
}
|
||||||
} else if (key == "TEXCOORD_0") {
|
} else if (key == "TEXCOORD_0") {
|
||||||
QVector<float> texcoords;
|
QVector<float> texcoords;
|
||||||
success = addArrayOfType(buffer.blob,
|
success = addArrayOfType(buffer.blob,
|
||||||
|
@ -926,7 +946,6 @@ HFMModel::Pointer GLTFSerializer::read(const QByteArray& data, const QVariantHas
|
||||||
//_file.dump();
|
//_file.dump();
|
||||||
auto hfmModelPtr = std::make_shared<HFMModel>();
|
auto hfmModelPtr = std::make_shared<HFMModel>();
|
||||||
HFMModel& hfmModel = *hfmModelPtr;
|
HFMModel& hfmModel = *hfmModelPtr;
|
||||||
|
|
||||||
buildGeometry(hfmModel, _url);
|
buildGeometry(hfmModel, _url);
|
||||||
|
|
||||||
//hfmDebugDump(data);
|
//hfmDebugDump(data);
|
||||||
|
@ -939,10 +958,15 @@ HFMModel::Pointer GLTFSerializer::read(const QByteArray& data, const QVariantHas
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLTFSerializer::readBinary(const QString& url, QByteArray& outdata) {
|
bool GLTFSerializer::readBinary(const QString& url, QByteArray& outdata) {
|
||||||
QUrl binaryUrl = _url.resolved(url);
|
|
||||||
|
|
||||||
bool success;
|
bool success;
|
||||||
std::tie<bool, QByteArray>(success, outdata) = requestData(binaryUrl);
|
|
||||||
|
if (url.contains("data:application/octet-stream;base64,")) {
|
||||||
|
outdata = requestEmbeddedData(url);
|
||||||
|
success = !outdata.isEmpty();
|
||||||
|
} else {
|
||||||
|
QUrl binaryUrl = _url.resolved(url);
|
||||||
|
std::tie<bool, QByteArray>(success, outdata) = requestData(binaryUrl);
|
||||||
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
@ -975,6 +999,11 @@ std::tuple<bool, QByteArray> GLTFSerializer::requestData(QUrl& url) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray GLTFSerializer::requestEmbeddedData(const QString& url) {
|
||||||
|
QString binaryUrl = url.split(",")[1];
|
||||||
|
return binaryUrl.isEmpty() ? QByteArray() : QByteArray::fromBase64(binaryUrl.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QNetworkReply* GLTFSerializer::request(QUrl& url, bool isTest) {
|
QNetworkReply* GLTFSerializer::request(QUrl& url, bool isTest) {
|
||||||
if (!qApp) {
|
if (!qApp) {
|
||||||
|
@ -1006,11 +1035,16 @@ HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) {
|
||||||
|
|
||||||
if (texture.defined["source"]) {
|
if (texture.defined["source"]) {
|
||||||
QString url = _file.images[texture.source].uri;
|
QString url = _file.images[texture.source].uri;
|
||||||
|
|
||||||
QString fname = QUrl(url).fileName();
|
QString fname = QUrl(url).fileName();
|
||||||
QUrl textureUrl = _url.resolved(url);
|
QUrl textureUrl = _url.resolved(url);
|
||||||
qCDebug(modelformat) << "fname: " << fname;
|
qCDebug(modelformat) << "fname: " << fname;
|
||||||
fbxtex.name = fname;
|
fbxtex.name = fname;
|
||||||
fbxtex.filename = textureUrl.toEncoded();
|
fbxtex.filename = textureUrl.toEncoded();
|
||||||
|
|
||||||
|
if (url.contains("data:image/jpeg;base64,") || url.contains("data:image/png;base64,")) {
|
||||||
|
fbxtex.content = requestEmbeddedData(url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return fbxtex;
|
return fbxtex;
|
||||||
}
|
}
|
||||||
|
@ -1187,8 +1221,6 @@ void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) {
|
||||||
qCDebug(modelformat) << " hasSkeletonJoints =" << hfmModel.hasSkeletonJoints;
|
qCDebug(modelformat) << " hasSkeletonJoints =" << hfmModel.hasSkeletonJoints;
|
||||||
qCDebug(modelformat) << " offset =" << hfmModel.offset;
|
qCDebug(modelformat) << " offset =" << hfmModel.offset;
|
||||||
|
|
||||||
qCDebug(modelformat) << " palmDirection = " << hfmModel.palmDirection;
|
|
||||||
|
|
||||||
qCDebug(modelformat) << " neckPivot = " << hfmModel.neckPivot;
|
qCDebug(modelformat) << " neckPivot = " << hfmModel.neckPivot;
|
||||||
|
|
||||||
qCDebug(modelformat) << " bindExtents.size() = " << hfmModel.bindExtents.size();
|
qCDebug(modelformat) << " bindExtents.size() = " << hfmModel.bindExtents.size();
|
||||||
|
@ -1301,8 +1333,6 @@ void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) {
|
||||||
qCDebug(modelformat) << " shapeInfo.dots =" << joint.shapeInfo.dots;
|
qCDebug(modelformat) << " shapeInfo.dots =" << joint.shapeInfo.dots;
|
||||||
qCDebug(modelformat) << " shapeInfo.points =" << joint.shapeInfo.points;
|
qCDebug(modelformat) << " shapeInfo.points =" << joint.shapeInfo.points;
|
||||||
|
|
||||||
qCDebug(modelformat) << " isFree =" << joint.isFree;
|
|
||||||
qCDebug(modelformat) << " freeLineage" << joint.freeLineage;
|
|
||||||
qCDebug(modelformat) << " parentIndex" << joint.parentIndex;
|
qCDebug(modelformat) << " parentIndex" << joint.parentIndex;
|
||||||
qCDebug(modelformat) << " distanceToParent" << joint.distanceToParent;
|
qCDebug(modelformat) << " distanceToParent" << joint.distanceToParent;
|
||||||
qCDebug(modelformat) << " translation" << joint.translation;
|
qCDebug(modelformat) << " translation" << joint.translation;
|
||||||
|
|
2
libraries/fbx/src/GLTFSerializer.h
Normal file → Executable file
2
libraries/fbx/src/GLTFSerializer.h
Normal file → Executable file
|
@ -772,6 +772,8 @@ private:
|
||||||
QVector<glm::vec3>& out_vertices, QVector<glm::vec3>& out_normals);
|
QVector<glm::vec3>& out_vertices, QVector<glm::vec3>& out_normals);
|
||||||
|
|
||||||
std::tuple<bool, QByteArray> requestData(QUrl& url);
|
std::tuple<bool, QByteArray> requestData(QUrl& url);
|
||||||
|
QByteArray requestEmbeddedData(const QString& url);
|
||||||
|
|
||||||
QNetworkReply* request(QUrl& url, bool isTest);
|
QNetworkReply* request(QUrl& url, bool isTest);
|
||||||
bool doesResourceExist(const QString& url);
|
bool doesResourceExist(const QString& url);
|
||||||
|
|
||||||
|
|
|
@ -687,7 +687,6 @@ HFMModel::Pointer OBJSerializer::read(const QByteArray& data, const QVariantHash
|
||||||
mesh.meshIndex = 0;
|
mesh.meshIndex = 0;
|
||||||
|
|
||||||
hfmModel.joints.resize(1);
|
hfmModel.joints.resize(1);
|
||||||
hfmModel.joints[0].isFree = false;
|
|
||||||
hfmModel.joints[0].parentIndex = -1;
|
hfmModel.joints[0].parentIndex = -1;
|
||||||
hfmModel.joints[0].distanceToParent = 0;
|
hfmModel.joints[0].distanceToParent = 0;
|
||||||
hfmModel.joints[0].translation = glm::vec3(0, 0, 0);
|
hfmModel.joints[0].translation = glm::vec3(0, 0, 0);
|
||||||
|
@ -1048,8 +1047,7 @@ void hfmDebugDump(const HFMModel& hfmModel) {
|
||||||
qCDebug(modelformat) << " joints.count() =" << hfmModel.joints.count();
|
qCDebug(modelformat) << " joints.count() =" << hfmModel.joints.count();
|
||||||
|
|
||||||
foreach (HFMJoint joint, hfmModel.joints) {
|
foreach (HFMJoint joint, hfmModel.joints) {
|
||||||
qCDebug(modelformat) << " isFree =" << joint.isFree;
|
|
||||||
qCDebug(modelformat) << " freeLineage" << joint.freeLineage;
|
|
||||||
qCDebug(modelformat) << " parentIndex" << joint.parentIndex;
|
qCDebug(modelformat) << " parentIndex" << joint.parentIndex;
|
||||||
qCDebug(modelformat) << " distanceToParent" << joint.distanceToParent;
|
qCDebug(modelformat) << " distanceToParent" << joint.distanceToParent;
|
||||||
qCDebug(modelformat) << " translation" << joint.translation;
|
qCDebug(modelformat) << " translation" << joint.translation;
|
||||||
|
|
|
@ -182,12 +182,3 @@ void Haze::setHazeBackgroundBlend(const float hazeBackgroundBlend) {
|
||||||
_hazeParametersBuffer.edit<Parameters>().hazeBackgroundBlend = newBlend;
|
_hazeParametersBuffer.edit<Parameters>().hazeBackgroundBlend = newBlend;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Haze::setTransform(const glm::mat4& transform) {
|
|
||||||
auto& params = _hazeParametersBuffer.get<Parameters>();
|
|
||||||
|
|
||||||
if (params.transform != transform) {
|
|
||||||
_hazeParametersBuffer.edit<Parameters>().transform = transform;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -92,8 +92,6 @@ namespace graphics {
|
||||||
|
|
||||||
void setHazeBackgroundBlend(const float hazeBackgroundBlend);
|
void setHazeBackgroundBlend(const float hazeBackgroundBlend);
|
||||||
|
|
||||||
void setTransform(const glm::mat4& transform);
|
|
||||||
|
|
||||||
using UniformBufferView = gpu::BufferView;
|
using UniformBufferView = gpu::BufferView;
|
||||||
UniformBufferView getHazeParametersBuffer() const { return _hazeParametersBuffer; }
|
UniformBufferView getHazeParametersBuffer() const { return _hazeParametersBuffer; }
|
||||||
|
|
||||||
|
@ -101,30 +99,32 @@ namespace graphics {
|
||||||
class Parameters {
|
class Parameters {
|
||||||
public:
|
public:
|
||||||
// DO NOT CHANGE ORDER HERE WITHOUT UNDERSTANDING THE std140 LAYOUT
|
// DO NOT CHANGE ORDER HERE WITHOUT UNDERSTANDING THE std140 LAYOUT
|
||||||
glm::vec3 hazeColor{ INITIAL_HAZE_COLOR };
|
glm::vec3 hazeColor { INITIAL_HAZE_COLOR };
|
||||||
float hazeGlareBlend{ convertGlareAngleToPower(INITIAL_HAZE_GLARE_ANGLE) };
|
float hazeGlareBlend { convertGlareAngleToPower(INITIAL_HAZE_GLARE_ANGLE) };
|
||||||
|
|
||||||
glm::vec3 hazeGlareColor{ INITIAL_HAZE_GLARE_COLOR };
|
glm::vec3 hazeGlareColor { INITIAL_HAZE_GLARE_COLOR };
|
||||||
float hazeBaseReference{ INITIAL_HAZE_BASE_REFERENCE };
|
float hazeBaseReference { INITIAL_HAZE_BASE_REFERENCE };
|
||||||
|
|
||||||
glm::vec3 colorModulationFactor;
|
glm::vec3 colorModulationFactor;
|
||||||
int hazeMode{ 0 }; // bit 0 - set to activate haze attenuation of fragment color
|
int hazeMode { 0 }; // bit 0 - set to activate haze attenuation of fragment color
|
||||||
// bit 1 - set to add the effect of altitude to the haze attenuation
|
// bit 1 - set to add the effect of altitude to the haze attenuation
|
||||||
// bit 2 - set to activate directional light attenuation mode
|
// bit 2 - set to activate directional light attenuation mode
|
||||||
// bit 3 - set to blend between blend-in and blend-out colours
|
// bit 3 - set to blend between blend-in and blend-out colours
|
||||||
|
|
||||||
glm::mat4 transform;
|
// Padding required to align the struct
|
||||||
|
#if defined(__clang__)
|
||||||
|
__attribute__((unused))
|
||||||
|
#endif
|
||||||
|
vec3 __padding;
|
||||||
|
|
||||||
// Amount of background (skybox) to display, overriding the haze effect for the background
|
// Amount of background (skybox) to display, overriding the haze effect for the background
|
||||||
float hazeBackgroundBlend{ INITIAL_HAZE_BACKGROUND_BLEND };
|
float hazeBackgroundBlend { INITIAL_HAZE_BACKGROUND_BLEND };
|
||||||
// The haze attenuation exponents used by both fragment and directional light attenuation
|
// The haze attenuation exponents used by both fragment and directional light attenuation
|
||||||
float hazeRangeFactor{ convertHazeRangeToHazeRangeFactor(INITIAL_HAZE_RANGE) };
|
float hazeRangeFactor { convertHazeRangeToHazeRangeFactor(INITIAL_HAZE_RANGE) };
|
||||||
float hazeHeightFactor{ convertHazeAltitudeToHazeAltitudeFactor(INITIAL_HAZE_HEIGHT) };
|
float hazeHeightFactor { convertHazeAltitudeToHazeAltitudeFactor(INITIAL_HAZE_HEIGHT) };
|
||||||
float hazeKeyLightRangeFactor{ convertHazeRangeToHazeRangeFactor(INITIAL_KEY_LIGHT_RANGE) };
|
float hazeKeyLightRangeFactor { convertHazeRangeToHazeRangeFactor(INITIAL_KEY_LIGHT_RANGE) };
|
||||||
|
|
||||||
float hazeKeyLightAltitudeFactor{ convertHazeAltitudeToHazeAltitudeFactor(INITIAL_KEY_LIGHT_ALTITUDE) };
|
float hazeKeyLightAltitudeFactor { convertHazeAltitudeToHazeAltitudeFactor(INITIAL_KEY_LIGHT_ALTITUDE) };
|
||||||
// Padding required to align the structure to sizeof(vec4)
|
|
||||||
vec3 __padding;
|
|
||||||
|
|
||||||
Parameters() {}
|
Parameters() {}
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,7 +87,7 @@ void Material::setUnlit(bool value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::setAlbedo(const glm::vec3& albedo, bool isSRGB) {
|
void Material::setAlbedo(const glm::vec3& albedo, bool isSRGB) {
|
||||||
_key.setAlbedo(glm::any(glm::greaterThan(albedo, glm::vec3(0.0f))));
|
_key.setAlbedo(true);
|
||||||
_albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo);
|
_albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,8 +75,6 @@ struct JointShapeInfo {
|
||||||
class Joint {
|
class Joint {
|
||||||
public:
|
public:
|
||||||
JointShapeInfo shapeInfo;
|
JointShapeInfo shapeInfo;
|
||||||
QVector<int> freeLineage;
|
|
||||||
bool isFree;
|
|
||||||
int parentIndex;
|
int parentIndex;
|
||||||
float distanceToParent;
|
float distanceToParent;
|
||||||
|
|
||||||
|
@ -291,8 +289,6 @@ public:
|
||||||
|
|
||||||
glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file
|
glm::mat4 offset; // This includes offset, rotation, and scale as specified by the FST file
|
||||||
|
|
||||||
glm::vec3 palmDirection;
|
|
||||||
|
|
||||||
glm::vec3 neckPivot;
|
glm::vec3 neckPivot;
|
||||||
|
|
||||||
Extents bindExtents;
|
Extents bindExtents;
|
||||||
|
@ -319,7 +315,6 @@ public:
|
||||||
QList<QString> blendshapeChannelNames;
|
QList<QString> blendshapeChannelNames;
|
||||||
|
|
||||||
QMap<int, glm::quat> jointRotationOffsets;
|
QMap<int, glm::quat> jointRotationOffsets;
|
||||||
QMap<QString, QString> hfmToHifiJointNameMapping;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,13 +19,14 @@
|
||||||
#include "CalculateMeshTangentsTask.h"
|
#include "CalculateMeshTangentsTask.h"
|
||||||
#include "CalculateBlendshapeNormalsTask.h"
|
#include "CalculateBlendshapeNormalsTask.h"
|
||||||
#include "CalculateBlendshapeTangentsTask.h"
|
#include "CalculateBlendshapeTangentsTask.h"
|
||||||
|
#include "PrepareJointsTask.h"
|
||||||
|
|
||||||
namespace baker {
|
namespace baker {
|
||||||
|
|
||||||
class GetModelPartsTask {
|
class GetModelPartsTask {
|
||||||
public:
|
public:
|
||||||
using Input = hfm::Model::Pointer;
|
using Input = hfm::Model::Pointer;
|
||||||
using Output = VaryingSet5<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, QHash<QString, hfm::Material>>;
|
using Output = VaryingSet6<std::vector<hfm::Mesh>, hifi::URL, baker::MeshIndicesToModelNames, baker::BlendshapesPerMesh, QHash<QString, hfm::Material>, std::vector<hfm::Joint>>;
|
||||||
using JobModel = Job::ModelIO<GetModelPartsTask, Input, Output>;
|
using JobModel = Job::ModelIO<GetModelPartsTask, Input, Output>;
|
||||||
|
|
||||||
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
||||||
|
@ -39,6 +40,7 @@ namespace baker {
|
||||||
blendshapesPerMesh.push_back(hfmModelIn->meshes[i].blendshapes.toStdVector());
|
blendshapesPerMesh.push_back(hfmModelIn->meshes[i].blendshapes.toStdVector());
|
||||||
}
|
}
|
||||||
output.edit4() = hfmModelIn->materials;
|
output.edit4() = hfmModelIn->materials;
|
||||||
|
output.edit5() = hfmModelIn->joints.toStdVector();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,23 +101,29 @@ namespace baker {
|
||||||
|
|
||||||
class BuildModelTask {
|
class BuildModelTask {
|
||||||
public:
|
public:
|
||||||
using Input = VaryingSet2<hfm::Model::Pointer, std::vector<hfm::Mesh>>;
|
using Input = VaryingSet5<hfm::Model::Pointer, std::vector<hfm::Mesh>, std::vector<hfm::Joint>, QMap<int, glm::quat> /*jointRotationOffsets*/, QHash<QString, int> /*jointIndices*/>;
|
||||||
using Output = hfm::Model::Pointer;
|
using Output = hfm::Model::Pointer;
|
||||||
using JobModel = Job::ModelIO<BuildModelTask, Input, Output>;
|
using JobModel = Job::ModelIO<BuildModelTask, Input, Output>;
|
||||||
|
|
||||||
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
void run(const BakeContextPointer& context, const Input& input, Output& output) {
|
||||||
auto hfmModelOut = input.get0();
|
auto hfmModelOut = input.get0();
|
||||||
hfmModelOut->meshes = QVector<hfm::Mesh>::fromStdVector(input.get1());
|
hfmModelOut->meshes = QVector<hfm::Mesh>::fromStdVector(input.get1());
|
||||||
|
hfmModelOut->joints = QVector<hfm::Joint>::fromStdVector(input.get2());
|
||||||
|
hfmModelOut->jointRotationOffsets = input.get3();
|
||||||
|
hfmModelOut->jointIndices = input.get4();
|
||||||
output = hfmModelOut;
|
output = hfmModelOut;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class BakerEngineBuilder {
|
class BakerEngineBuilder {
|
||||||
public:
|
public:
|
||||||
using Input = hfm::Model::Pointer;
|
using Input = VaryingSet2<hfm::Model::Pointer, QVariantHash>;
|
||||||
using Output = hfm::Model::Pointer;
|
using Output = hfm::Model::Pointer;
|
||||||
using JobModel = Task::ModelIO<BakerEngineBuilder, Input, Output>;
|
using JobModel = Task::ModelIO<BakerEngineBuilder, Input, Output>;
|
||||||
void build(JobModel& model, const Varying& hfmModelIn, Varying& hfmModelOut) {
|
void build(JobModel& model, const Varying& input, Varying& hfmModelOut) {
|
||||||
|
const auto& hfmModelIn = input.getN<Input>(0);
|
||||||
|
const auto& mapping = input.getN<Input>(1);
|
||||||
|
|
||||||
// Split up the inputs from hfm::Model
|
// Split up the inputs from hfm::Model
|
||||||
const auto modelPartsIn = model.addJob<GetModelPartsTask>("GetModelParts", hfmModelIn);
|
const auto modelPartsIn = model.addJob<GetModelPartsTask>("GetModelParts", hfmModelIn);
|
||||||
const auto meshesIn = modelPartsIn.getN<GetModelPartsTask::Output>(0);
|
const auto meshesIn = modelPartsIn.getN<GetModelPartsTask::Output>(0);
|
||||||
|
@ -123,6 +131,7 @@ namespace baker {
|
||||||
const auto meshIndicesToModelNames = modelPartsIn.getN<GetModelPartsTask::Output>(2);
|
const auto meshIndicesToModelNames = modelPartsIn.getN<GetModelPartsTask::Output>(2);
|
||||||
const auto blendshapesPerMeshIn = modelPartsIn.getN<GetModelPartsTask::Output>(3);
|
const auto blendshapesPerMeshIn = modelPartsIn.getN<GetModelPartsTask::Output>(3);
|
||||||
const auto materials = modelPartsIn.getN<GetModelPartsTask::Output>(4);
|
const auto materials = modelPartsIn.getN<GetModelPartsTask::Output>(4);
|
||||||
|
const auto jointsIn = modelPartsIn.getN<GetModelPartsTask::Output>(5);
|
||||||
|
|
||||||
// Calculate normals and tangents for meshes and blendshapes if they do not exist
|
// Calculate normals and tangents for meshes and blendshapes if they do not exist
|
||||||
// Note: Normals are never calculated here for OBJ models. OBJ files optionally define normals on a per-face basis, so for consistency normals are calculated beforehand in OBJSerializer.
|
// Note: Normals are never calculated here for OBJ models. OBJ files optionally define normals on a per-face basis, so for consistency normals are calculated beforehand in OBJSerializer.
|
||||||
|
@ -138,19 +147,27 @@ namespace baker {
|
||||||
const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh).asVarying();
|
const auto buildGraphicsMeshInputs = BuildGraphicsMeshTask::Input(meshesIn, url, meshIndicesToModelNames, normalsPerMesh, tangentsPerMesh).asVarying();
|
||||||
const auto graphicsMeshes = model.addJob<BuildGraphicsMeshTask>("BuildGraphicsMesh", buildGraphicsMeshInputs);
|
const auto graphicsMeshes = model.addJob<BuildGraphicsMeshTask>("BuildGraphicsMesh", buildGraphicsMeshInputs);
|
||||||
|
|
||||||
|
// Prepare joint information
|
||||||
|
const auto prepareJointsInputs = PrepareJointsTask::Input(jointsIn, mapping).asVarying();
|
||||||
|
const auto jointInfoOut = model.addJob<PrepareJointsTask>("PrepareJoints", prepareJointsInputs);
|
||||||
|
const auto jointsOut = jointInfoOut.getN<PrepareJointsTask::Output>(0);
|
||||||
|
const auto jointRotationOffsets = jointInfoOut.getN<PrepareJointsTask::Output>(1);
|
||||||
|
const auto jointIndices = jointInfoOut.getN<PrepareJointsTask::Output>(2);
|
||||||
|
|
||||||
// Combine the outputs into a new hfm::Model
|
// Combine the outputs into a new hfm::Model
|
||||||
const auto buildBlendshapesInputs = BuildBlendshapesTask::Input(blendshapesPerMeshIn, normalsPerBlendshapePerMesh, tangentsPerBlendshapePerMesh).asVarying();
|
const auto buildBlendshapesInputs = BuildBlendshapesTask::Input(blendshapesPerMeshIn, normalsPerBlendshapePerMesh, tangentsPerBlendshapePerMesh).asVarying();
|
||||||
const auto blendshapesPerMeshOut = model.addJob<BuildBlendshapesTask>("BuildBlendshapes", buildBlendshapesInputs);
|
const auto blendshapesPerMeshOut = model.addJob<BuildBlendshapesTask>("BuildBlendshapes", buildBlendshapesInputs);
|
||||||
const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying();
|
const auto buildMeshesInputs = BuildMeshesTask::Input(meshesIn, graphicsMeshes, normalsPerMesh, tangentsPerMesh, blendshapesPerMeshOut).asVarying();
|
||||||
const auto meshesOut = model.addJob<BuildMeshesTask>("BuildMeshes", buildMeshesInputs);
|
const auto meshesOut = model.addJob<BuildMeshesTask>("BuildMeshes", buildMeshesInputs);
|
||||||
const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut).asVarying();
|
const auto buildModelInputs = BuildModelTask::Input(hfmModelIn, meshesOut, jointsOut, jointRotationOffsets, jointIndices).asVarying();
|
||||||
hfmModelOut = model.addJob<BuildModelTask>("BuildModel", buildModelInputs);
|
hfmModelOut = model.addJob<BuildModelTask>("BuildModel", buildModelInputs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Baker::Baker(const hfm::Model::Pointer& hfmModel) :
|
Baker::Baker(const hfm::Model::Pointer& hfmModel, const QVariantHash& mapping) :
|
||||||
_engine(std::make_shared<Engine>(BakerEngineBuilder::JobModel::create("Baker"), std::make_shared<BakeContext>())) {
|
_engine(std::make_shared<Engine>(BakerEngineBuilder::JobModel::create("Baker"), std::make_shared<BakeContext>())) {
|
||||||
_engine->feedInput<BakerEngineBuilder::Input>(hfmModel);
|
_engine->feedInput<BakerEngineBuilder::Input>(0, hfmModel);
|
||||||
|
_engine->feedInput<BakerEngineBuilder::Input>(1, mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Baker::run() {
|
void Baker::run() {
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#ifndef hifi_baker_Baker_h
|
#ifndef hifi_baker_Baker_h
|
||||||
#define hifi_baker_Baker_h
|
#define hifi_baker_Baker_h
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
#include <hfm/HFM.h>
|
#include <hfm/HFM.h>
|
||||||
|
|
||||||
#include "Engine.h"
|
#include "Engine.h"
|
||||||
|
@ -19,7 +21,7 @@
|
||||||
namespace baker {
|
namespace baker {
|
||||||
class Baker {
|
class Baker {
|
||||||
public:
|
public:
|
||||||
Baker(const hfm::Model::Pointer& hfmModel);
|
Baker(const hfm::Model::Pointer& hfmModel, const QVariantHash& mapping);
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,17 @@
|
||||||
|
|
||||||
#include "ModelMath.h"
|
#include "ModelMath.h"
|
||||||
|
|
||||||
|
bool needTangents(const hfm::Mesh& mesh, const QHash<QString, hfm::Material>& materials) {
|
||||||
|
// Check if we actually need to calculate the tangents
|
||||||
|
for (const auto& meshPart : mesh.parts) {
|
||||||
|
auto materialIt = materials.find(meshPart.materialID);
|
||||||
|
if (materialIt != materials.end() && (*materialIt).needTangentSpace()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CalculateMeshTangentsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
|
void CalculateMeshTangentsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
|
||||||
const auto& normalsPerMesh = input.get0();
|
const auto& normalsPerMesh = input.get0();
|
||||||
const std::vector<hfm::Mesh>& meshes = input.get1();
|
const std::vector<hfm::Mesh>& meshes = input.get1();
|
||||||
|
@ -28,38 +39,20 @@ void CalculateMeshTangentsTask::run(const baker::BakeContextPointer& context, co
|
||||||
auto& tangentsOut = tangentsPerMeshOut[tangentsPerMeshOut.size()-1];
|
auto& tangentsOut = tangentsPerMeshOut[tangentsPerMeshOut.size()-1];
|
||||||
|
|
||||||
// Check if we already have tangents and therefore do not need to do any calculation
|
// Check if we already have tangents and therefore do not need to do any calculation
|
||||||
|
// Otherwise confirm if we have the normals needed, and need to calculate the tangents
|
||||||
if (!tangentsIn.empty()) {
|
if (!tangentsIn.empty()) {
|
||||||
tangentsOut = tangentsIn.toStdVector();
|
tangentsOut = tangentsIn.toStdVector();
|
||||||
continue;
|
} else if (!normals.empty() && needTangents(mesh, materials)) {
|
||||||
|
tangentsOut.resize(normals.size());
|
||||||
|
baker::calculateTangents(mesh,
|
||||||
|
[&mesh, &normals, &tangentsOut](int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec2* outTexCoords, glm::vec3& outNormal) {
|
||||||
|
outVertices[0] = mesh.vertices[firstIndex];
|
||||||
|
outVertices[1] = mesh.vertices[secondIndex];
|
||||||
|
outNormal = normals[firstIndex];
|
||||||
|
outTexCoords[0] = mesh.texCoords[firstIndex];
|
||||||
|
outTexCoords[1] = mesh.texCoords[secondIndex];
|
||||||
|
return &(tangentsOut[firstIndex]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have normals, and if not then tangents can't be calculated
|
|
||||||
if (normals.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we actually need to calculate the tangents
|
|
||||||
bool needTangents = false;
|
|
||||||
for (const auto& meshPart : mesh.parts) {
|
|
||||||
auto materialIt = materials.find(meshPart.materialID);
|
|
||||||
if (materialIt != materials.end() && (*materialIt).needTangentSpace()) {
|
|
||||||
needTangents = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (needTangents) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
tangentsOut.resize(normals.size());
|
|
||||||
baker::calculateTangents(mesh,
|
|
||||||
[&mesh, &normals, &tangentsOut](int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec2* outTexCoords, glm::vec3& outNormal) {
|
|
||||||
outVertices[0] = mesh.vertices[firstIndex];
|
|
||||||
outVertices[1] = mesh.vertices[secondIndex];
|
|
||||||
outNormal = normals[firstIndex];
|
|
||||||
outTexCoords[0] = mesh.texCoords[firstIndex];
|
|
||||||
outTexCoords[1] = mesh.texCoords[secondIndex];
|
|
||||||
return &(tangentsOut[firstIndex]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
86
libraries/model-baker/src/model-baker/PrepareJointsTask.cpp
Normal file
86
libraries/model-baker/src/model-baker/PrepareJointsTask.cpp
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
//
|
||||||
|
// PrepareJointsTask.cpp
|
||||||
|
// model-baker/src/model-baker
|
||||||
|
//
|
||||||
|
// Created by Sabrina Shanman on 2019/01/25.
|
||||||
|
// 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 "PrepareJointsTask.h"
|
||||||
|
|
||||||
|
#include "ModelBakerLogging.h"
|
||||||
|
|
||||||
|
QMap<QString, QString> getJointNameMapping(const QVariantHash& mapping) {
|
||||||
|
static const QString JOINT_NAME_MAPPING_FIELD = "jointMap";
|
||||||
|
QMap<QString, QString> hfmToHifiJointNameMap;
|
||||||
|
if (!mapping.isEmpty() && mapping.contains(JOINT_NAME_MAPPING_FIELD) && mapping[JOINT_NAME_MAPPING_FIELD].type() == QVariant::Hash) {
|
||||||
|
auto jointNames = mapping[JOINT_NAME_MAPPING_FIELD].toHash();
|
||||||
|
for (auto itr = jointNames.begin(); itr != jointNames.end(); itr++) {
|
||||||
|
hfmToHifiJointNameMap.insert(itr.key(), itr.value().toString());
|
||||||
|
qCDebug(model_baker) << "the mapped key " << itr.key() << " has a value of " << hfmToHifiJointNameMap[itr.key()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hfmToHifiJointNameMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, glm::quat> getJointRotationOffsets(const QVariantHash& mapping) {
|
||||||
|
QMap<QString, glm::quat> jointRotationOffsets;
|
||||||
|
static const QString JOINT_ROTATION_OFFSET_FIELD = "jointRotationOffset";
|
||||||
|
if (!mapping.isEmpty() && mapping.contains(JOINT_ROTATION_OFFSET_FIELD) && mapping[JOINT_ROTATION_OFFSET_FIELD].type() == QVariant::Hash) {
|
||||||
|
auto offsets = mapping[JOINT_ROTATION_OFFSET_FIELD].toHash();
|
||||||
|
for (auto itr = offsets.begin(); itr != offsets.end(); itr++) {
|
||||||
|
QString jointName = itr.key();
|
||||||
|
QString line = itr.value().toString();
|
||||||
|
auto quatCoords = line.split(',');
|
||||||
|
if (quatCoords.size() == 4) {
|
||||||
|
float quatX = quatCoords[0].mid(1).toFloat();
|
||||||
|
float quatY = quatCoords[1].toFloat();
|
||||||
|
float quatZ = quatCoords[2].toFloat();
|
||||||
|
float quatW = quatCoords[3].mid(0, quatCoords[3].size() - 1).toFloat();
|
||||||
|
if (!isNaN(quatX) && !isNaN(quatY) && !isNaN(quatZ) && !isNaN(quatW)) {
|
||||||
|
glm::quat rotationOffset = glm::quat(quatW, quatX, quatY, quatZ);
|
||||||
|
jointRotationOffsets.insert(jointName, rotationOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jointRotationOffsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareJointsTask::run(const baker::BakeContextPointer& context, const Input& input, Output& output) {
|
||||||
|
const auto& jointsIn = input.get0();
|
||||||
|
const auto& mapping = input.get1();
|
||||||
|
auto& jointsOut = output.edit0();
|
||||||
|
auto& jointRotationOffsets = output.edit1();
|
||||||
|
auto& jointIndices = output.edit2();
|
||||||
|
|
||||||
|
// Get joint renames
|
||||||
|
auto jointNameMapping = getJointNameMapping(mapping);
|
||||||
|
// Apply joint metadata from FST file mappings
|
||||||
|
for (const auto& jointIn : jointsIn) {
|
||||||
|
jointsOut.push_back(jointIn);
|
||||||
|
auto& jointOut = jointsOut.back();
|
||||||
|
|
||||||
|
auto jointNameMapKey = jointNameMapping.key(jointIn.name);
|
||||||
|
if (jointNameMapping.contains(jointNameMapKey)) {
|
||||||
|
jointOut.name = jointNameMapKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
jointIndices.insert(jointOut.name, (int)jointsOut.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get joint rotation offsets from FST file mappings
|
||||||
|
auto offsets = getJointRotationOffsets(mapping);
|
||||||
|
for (auto itr = offsets.begin(); itr != offsets.end(); itr++) {
|
||||||
|
QString jointName = itr.key();
|
||||||
|
int jointIndex = jointIndices.value(jointName) - 1;
|
||||||
|
if (jointIndex != -1) {
|
||||||
|
glm::quat rotationOffset = itr.value();
|
||||||
|
jointRotationOffsets.insert(jointIndex, rotationOffset);
|
||||||
|
qCDebug(model_baker) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
libraries/model-baker/src/model-baker/PrepareJointsTask.h
Normal file
30
libraries/model-baker/src/model-baker/PrepareJointsTask.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// PrepareJointsTask.h
|
||||||
|
// model-baker/src/model-baker
|
||||||
|
//
|
||||||
|
// Created by Sabrina Shanman on 2019/01/25.
|
||||||
|
// 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_PrepareJointsTask_h
|
||||||
|
#define hifi_PrepareJointsTask_h
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
#include <hfm/HFM.h>
|
||||||
|
|
||||||
|
#include "Engine.h"
|
||||||
|
|
||||||
|
class PrepareJointsTask {
|
||||||
|
public:
|
||||||
|
using Input = baker::VaryingSet2<std::vector<hfm::Joint>, QVariantHash /*mapping*/>;
|
||||||
|
using Output = baker::VaryingSet3<std::vector<hfm::Joint>, QMap<int, glm::quat> /*jointRotationOffsets*/, QHash<QString, int> /*jointIndices*/>;
|
||||||
|
using JobModel = baker::Job::ModelIO<PrepareJointsTask, Input, Output>;
|
||||||
|
|
||||||
|
void run(const baker::BakeContextPointer& context, const Input& input, Output& output);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_PrepareJointsTask_h
|
|
@ -233,7 +233,7 @@ void GeometryReader::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition",
|
QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition",
|
||||||
Q_ARG(HFMModel::Pointer, hfmModel));
|
Q_ARG(HFMModel::Pointer, hfmModel), Q_ARG(QVariantHash, _mapping));
|
||||||
} catch (const std::exception&) {
|
} catch (const std::exception&) {
|
||||||
auto resource = _resource.toStrongRef();
|
auto resource = _resource.toStrongRef();
|
||||||
if (resource) {
|
if (resource) {
|
||||||
|
@ -261,7 +261,7 @@ public:
|
||||||
virtual void downloadFinished(const QByteArray& data) override;
|
virtual void downloadFinished(const QByteArray& data) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel);
|
Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, QVariantHash mapping);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ModelLoader _modelLoader;
|
ModelLoader _modelLoader;
|
||||||
|
@ -277,9 +277,9 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) {
|
||||||
QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mapping, data, _combineParts, _request->getWebMediaType()));
|
QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mapping, data, _combineParts, _request->getWebMediaType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel) {
|
void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel, QVariantHash mapping) {
|
||||||
// Do processing on the model
|
// Do processing on the model
|
||||||
baker::Baker modelBaker(hfmModel);
|
baker::Baker modelBaker(hfmModel, mapping);
|
||||||
modelBaker.run();
|
modelBaker.run();
|
||||||
|
|
||||||
// Assume ownership of the processed HFMModel
|
// Assume ownership of the processed HFMModel
|
||||||
|
|
|
@ -208,7 +208,7 @@ void AccountManager::setSessionID(const QUuid& sessionID) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::Type authType, const QUrlQuery & query) {
|
QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::Type authType) {
|
||||||
QNetworkRequest networkRequest;
|
QNetworkRequest networkRequest;
|
||||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
|
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
|
||||||
|
@ -217,17 +217,22 @@ QNetworkRequest AccountManager::createRequest(QString path, AccountManagerAuth::
|
||||||
uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit());
|
uuidStringWithoutCurlyBraces(_sessionID).toLocal8Bit());
|
||||||
|
|
||||||
QUrl requestURL = _authURL;
|
QUrl requestURL = _authURL;
|
||||||
|
|
||||||
if (requestURL.isEmpty()) { // Assignment client doesn't set _authURL.
|
if (requestURL.isEmpty()) { // Assignment client doesn't set _authURL.
|
||||||
requestURL = getMetaverseServerURL();
|
requestURL = getMetaverseServerURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int queryStringLocation = path.indexOf("?");
|
||||||
if (path.startsWith("/")) {
|
if (path.startsWith("/")) {
|
||||||
requestURL.setPath(path);
|
requestURL.setPath(path.left(queryStringLocation));
|
||||||
} else {
|
} else {
|
||||||
requestURL.setPath("/" + path);
|
requestURL.setPath("/" + path.left(queryStringLocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryStringLocation >= 0) {
|
||||||
|
QUrlQuery query(path.mid(queryStringLocation+1));
|
||||||
|
requestURL.setQuery(query);
|
||||||
}
|
}
|
||||||
requestURL.setQuery(query);
|
|
||||||
|
|
||||||
if (authType != AccountManagerAuth::None ) {
|
if (authType != AccountManagerAuth::None ) {
|
||||||
if (hasValidAccessToken()) {
|
if (hasValidAccessToken()) {
|
||||||
|
@ -253,8 +258,7 @@ void AccountManager::sendRequest(const QString& path,
|
||||||
const JSONCallbackParameters& callbackParams,
|
const JSONCallbackParameters& callbackParams,
|
||||||
const QByteArray& dataByteArray,
|
const QByteArray& dataByteArray,
|
||||||
QHttpMultiPart* dataMultiPart,
|
QHttpMultiPart* dataMultiPart,
|
||||||
const QVariantMap& propertyMap,
|
const QVariantMap& propertyMap) {
|
||||||
QUrlQuery query) {
|
|
||||||
|
|
||||||
if (thread() != QThread::currentThread()) {
|
if (thread() != QThread::currentThread()) {
|
||||||
QMetaObject::invokeMethod(this, "sendRequest",
|
QMetaObject::invokeMethod(this, "sendRequest",
|
||||||
|
@ -264,14 +268,13 @@ void AccountManager::sendRequest(const QString& path,
|
||||||
Q_ARG(const JSONCallbackParameters&, callbackParams),
|
Q_ARG(const JSONCallbackParameters&, callbackParams),
|
||||||
Q_ARG(const QByteArray&, dataByteArray),
|
Q_ARG(const QByteArray&, dataByteArray),
|
||||||
Q_ARG(QHttpMultiPart*, dataMultiPart),
|
Q_ARG(QHttpMultiPart*, dataMultiPart),
|
||||||
Q_ARG(QVariantMap, propertyMap),
|
Q_ARG(QVariantMap, propertyMap));
|
||||||
Q_ARG(QUrlQuery, query));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
|
|
||||||
QNetworkRequest networkRequest = createRequest(path, authType, query);
|
QNetworkRequest networkRequest = createRequest(path, authType);
|
||||||
|
|
||||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||||
qCDebug(networking) << "Making a request to" << qPrintable(networkRequest.url().toString());
|
qCDebug(networking) << "Making a request to" << qPrintable(networkRequest.url().toString());
|
||||||
|
|
|
@ -61,15 +61,14 @@ class AccountManager : public QObject, public Dependency {
|
||||||
public:
|
public:
|
||||||
AccountManager(UserAgentGetter userAgentGetter = DEFAULT_USER_AGENT_GETTER);
|
AccountManager(UserAgentGetter userAgentGetter = DEFAULT_USER_AGENT_GETTER);
|
||||||
|
|
||||||
QNetworkRequest createRequest(QString path, AccountManagerAuth::Type authType, const QUrlQuery & query = QUrlQuery());
|
QNetworkRequest createRequest(QString path, AccountManagerAuth::Type authType);
|
||||||
Q_INVOKABLE void sendRequest(const QString& path,
|
Q_INVOKABLE void sendRequest(const QString& path,
|
||||||
AccountManagerAuth::Type authType,
|
AccountManagerAuth::Type authType,
|
||||||
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
||||||
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
|
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
|
||||||
const QByteArray& dataByteArray = QByteArray(),
|
const QByteArray& dataByteArray = QByteArray(),
|
||||||
QHttpMultiPart* dataMultiPart = NULL,
|
QHttpMultiPart* dataMultiPart = NULL,
|
||||||
const QVariantMap& propertyMap = QVariantMap(),
|
const QVariantMap& propertyMap = QVariantMap());
|
||||||
QUrlQuery query = QUrlQuery());
|
|
||||||
|
|
||||||
void setIsAgent(bool isAgent) { _isAgent = isAgent; }
|
void setIsAgent(bool isAgent) { _isAgent = isAgent; }
|
||||||
|
|
||||||
|
|
|
@ -683,7 +683,7 @@ void CharacterController::updateState() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) {
|
if (_pendingFlags & PENDING_FLAG_RECOMPUTE_FLYING) {
|
||||||
SET_STATE(CharacterController::State::Hover, "recomputeFlying");
|
SET_STATE(CharacterController::State::Hover, "recomputeFlying");
|
||||||
_hasSupport = false;
|
_hasSupport = false;
|
||||||
_stepHeight = _minStepHeight; // clears memory of last step obstacle
|
_stepHeight = _minStepHeight; // clears memory of last step obstacle
|
||||||
_pendingFlags &= ~PENDING_FLAG_RECOMPUTE_FLYING;
|
_pendingFlags &= ~PENDING_FLAG_RECOMPUTE_FLYING;
|
||||||
|
@ -795,15 +795,13 @@ void CharacterController::updateState() {
|
||||||
// Transition to hover if we are above the fall threshold
|
// Transition to hover if we are above the fall threshold
|
||||||
SET_STATE(State::Hover, "above fall threshold");
|
SET_STATE(State::Hover, "above fall threshold");
|
||||||
}
|
}
|
||||||
} else if (!rayHasHit && !_hasSupport) {
|
|
||||||
SET_STATE(State::Hover, "no ground detected");
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::Hover:
|
case State::Hover:
|
||||||
btScalar horizontalSpeed = (velocity - velocity.dot(_currentUp) * _currentUp).length();
|
btScalar horizontalSpeed = (velocity - velocity.dot(_currentUp) * _currentUp).length();
|
||||||
bool flyingFast = horizontalSpeed > (MAX_WALKING_SPEED * 0.75f);
|
bool flyingFast = horizontalSpeed > (MAX_WALKING_SPEED * 0.75f);
|
||||||
if (!_flyingAllowed && rayHasHit) {
|
if (!_flyingAllowed) {
|
||||||
SET_STATE(State::InAir, "flying not allowed");
|
SET_STATE(State::InAir, "flying not allowed");
|
||||||
} else if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) {
|
} else if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) {
|
||||||
SET_STATE(State::InAir, "near ground");
|
SET_STATE(State::InAir, "near ground");
|
||||||
|
|
|
@ -104,13 +104,13 @@ void ProceduralData::parse(const QJsonObject& proceduralData) {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
Procedural::Procedural() {
|
Procedural::Procedural() {
|
||||||
_opaqueState->setCullMode(gpu::State::CULL_BACK);
|
_opaqueState->setCullMode(gpu::State::CULL_NONE);
|
||||||
_opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
_opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
_opaqueState->setBlendFunction(false,
|
_opaqueState->setBlendFunction(false,
|
||||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||||
|
|
||||||
_transparentState->setCullMode(gpu::State::CULL_BACK);
|
_transparentState->setCullMode(gpu::State::CULL_NONE);
|
||||||
_transparentState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
_transparentState->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
_transparentState->setBlendFunction(true,
|
_transparentState->setBlendFunction(true,
|
||||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||||
|
|
|
@ -374,7 +374,7 @@ void AnimDebugDraw::update() {
|
||||||
glm::vec4 color = std::get<3>(iter.second);
|
glm::vec4 color = std::get<3>(iter.second);
|
||||||
|
|
||||||
for (int i = 0; i < skeleton->getNumJoints(); i++) {
|
for (int i = 0; i < skeleton->getNumJoints(); i++) {
|
||||||
const float radius = BONE_RADIUS / (absPoses[i].scale().x * rootPose.scale().x);
|
const float radius = BONE_RADIUS / (absPoses[i].scale() * rootPose.scale());
|
||||||
|
|
||||||
// draw bone
|
// draw bone
|
||||||
addBone(rootPose, absPoses[i], radius, color, v);
|
addBone(rootPose, absPoses[i], radius, color, v);
|
||||||
|
@ -394,16 +394,16 @@ void AnimDebugDraw::update() {
|
||||||
glm::vec3 pos = std::get<1>(iter.second);
|
glm::vec3 pos = std::get<1>(iter.second);
|
||||||
glm::vec4 color = std::get<2>(iter.second);
|
glm::vec4 color = std::get<2>(iter.second);
|
||||||
const float radius = POSE_RADIUS;
|
const float radius = POSE_RADIUS;
|
||||||
addBone(AnimPose::identity, AnimPose(glm::vec3(1), rot, pos), radius, color, v);
|
addBone(AnimPose::identity, AnimPose(1.0f, rot, pos), radius, color, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimPose myAvatarPose(glm::vec3(1), DebugDraw::getInstance().getMyAvatarRot(), DebugDraw::getInstance().getMyAvatarPos());
|
AnimPose myAvatarPose(1.0f, DebugDraw::getInstance().getMyAvatarRot(), DebugDraw::getInstance().getMyAvatarPos());
|
||||||
for (auto& iter : myAvatarMarkerMap) {
|
for (auto& iter : myAvatarMarkerMap) {
|
||||||
glm::quat rot = std::get<0>(iter.second);
|
glm::quat rot = std::get<0>(iter.second);
|
||||||
glm::vec3 pos = std::get<1>(iter.second);
|
glm::vec3 pos = std::get<1>(iter.second);
|
||||||
glm::vec4 color = std::get<2>(iter.second);
|
glm::vec4 color = std::get<2>(iter.second);
|
||||||
const float radius = POSE_RADIUS;
|
const float radius = POSE_RADIUS;
|
||||||
addBone(myAvatarPose, AnimPose(glm::vec3(1), rot, pos), radius, color, v);
|
addBone(myAvatarPose, AnimPose(1.0f, rot, pos), radius, color, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw rays from shared DebugDraw singleton
|
// draw rays from shared DebugDraw singleton
|
||||||
|
|
|
@ -122,7 +122,7 @@ void CauterizedModel::updateClusterMatrices() {
|
||||||
|
|
||||||
if (_useDualQuaternionSkinning) {
|
if (_useDualQuaternionSkinning) {
|
||||||
auto jointPose = _rig.getJointPose(cluster.jointIndex);
|
auto jointPose = _rig.getJointPose(cluster.jointIndex);
|
||||||
Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans());
|
Transform jointTransform(jointPose.rot(), glm::vec3(jointPose.scale()), jointPose.trans());
|
||||||
Transform clusterTransform;
|
Transform clusterTransform;
|
||||||
Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform);
|
Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform);
|
||||||
state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform);
|
state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform);
|
||||||
|
@ -138,7 +138,7 @@ void CauterizedModel::updateClusterMatrices() {
|
||||||
if (!_cauterizeBoneSet.empty()) {
|
if (!_cauterizeBoneSet.empty()) {
|
||||||
|
|
||||||
AnimPose cauterizePose = _rig.getJointPose(_rig.indexOfJoint("Neck"));
|
AnimPose cauterizePose = _rig.getJointPose(_rig.indexOfJoint("Neck"));
|
||||||
cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f);
|
cauterizePose.scale() = 0.0001f;
|
||||||
|
|
||||||
static const glm::mat4 zeroScale(
|
static const glm::mat4 zeroScale(
|
||||||
glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f),
|
glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f),
|
||||||
|
@ -161,7 +161,7 @@ void CauterizedModel::updateClusterMatrices() {
|
||||||
// not cauterized so just copy the value from the non-cauterized version.
|
// not cauterized so just copy the value from the non-cauterized version.
|
||||||
state.clusterDualQuaternions[j] = _meshStates[i].clusterDualQuaternions[j];
|
state.clusterDualQuaternions[j] = _meshStates[i].clusterDualQuaternions[j];
|
||||||
} else {
|
} else {
|
||||||
Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans());
|
Transform jointTransform(cauterizePose.rot(), glm::vec3(cauterizePose.scale()), cauterizePose.trans());
|
||||||
Transform clusterTransform;
|
Transform clusterTransform;
|
||||||
Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform);
|
Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform);
|
||||||
state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform);
|
state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform);
|
||||||
|
|
|
@ -28,7 +28,7 @@ struct HazeParams {
|
||||||
vec3 colorModulationFactor;
|
vec3 colorModulationFactor;
|
||||||
int hazeMode;
|
int hazeMode;
|
||||||
|
|
||||||
mat4 transform;
|
vec3 spare;
|
||||||
float backgroundBlend;
|
float backgroundBlend;
|
||||||
|
|
||||||
float hazeRangeFactor;
|
float hazeRangeFactor;
|
||||||
|
|
|
@ -1116,10 +1116,6 @@ int Model::getParentJointIndex(int jointIndex) const {
|
||||||
return (isActive() && jointIndex != -1) ? getHFMModel().joints.at(jointIndex).parentIndex : -1;
|
return (isActive() && jointIndex != -1) ? getHFMModel().joints.at(jointIndex).parentIndex : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Model::getLastFreeJointIndex(int jointIndex) const {
|
|
||||||
return (isActive() && jointIndex != -1) ? getHFMModel().joints.at(jointIndex).freeLineage.last() : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Model::setTextures(const QVariantMap& textures) {
|
void Model::setTextures(const QVariantMap& textures) {
|
||||||
if (isLoaded()) {
|
if (isLoaded()) {
|
||||||
_needsFixupInScene = true;
|
_needsFixupInScene = true;
|
||||||
|
@ -1368,7 +1364,7 @@ void Model::updateClusterMatrices() {
|
||||||
|
|
||||||
if (_useDualQuaternionSkinning) {
|
if (_useDualQuaternionSkinning) {
|
||||||
auto jointPose = _rig.getJointPose(cluster.jointIndex);
|
auto jointPose = _rig.getJointPose(cluster.jointIndex);
|
||||||
Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans());
|
Transform jointTransform(jointPose.rot(), glm::vec3(jointPose.scale()), jointPose.trans());
|
||||||
Transform clusterTransform;
|
Transform clusterTransform;
|
||||||
Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform);
|
Transform::mult(clusterTransform, jointTransform, _rig.getAnimSkeleton()->getClusterBindMatricesOriginalValues(meshIndex, clusterIndex).inverseBindTransform);
|
||||||
state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform);
|
state.clusterDualQuaternions[j] = Model::TransformDualQuaternion(clusterTransform);
|
||||||
|
|
|
@ -298,9 +298,9 @@ public:
|
||||||
TransformDualQuaternion() {}
|
TransformDualQuaternion() {}
|
||||||
TransformDualQuaternion(const glm::mat4& m) {
|
TransformDualQuaternion(const glm::mat4& m) {
|
||||||
AnimPose p(m);
|
AnimPose p(m);
|
||||||
_scale.x = p.scale().x;
|
_scale.x = p.scale();
|
||||||
_scale.y = p.scale().y;
|
_scale.y = p.scale();
|
||||||
_scale.z = p.scale().z;
|
_scale.z = p.scale();
|
||||||
_scale.w = 0.0f;
|
_scale.w = 0.0f;
|
||||||
_dq = DualQuaternion(p.rot(), p.trans());
|
_dq = DualQuaternion(p.rot(), p.trans());
|
||||||
}
|
}
|
||||||
|
@ -379,9 +379,6 @@ protected:
|
||||||
/// Clear the joint states
|
/// Clear the joint states
|
||||||
void clearJointState(int index);
|
void clearJointState(int index);
|
||||||
|
|
||||||
/// Returns the index of the last free ancestor of the indexed joint, or -1 if not found.
|
|
||||||
int getLastFreeJointIndex(int jointIndex) const;
|
|
||||||
|
|
||||||
/// \param jointIndex index of joint in model structure
|
/// \param jointIndex index of joint in model structure
|
||||||
/// \param position[out] position of joint in model-frame
|
/// \param position[out] position of joint in model-frame
|
||||||
/// \return true if joint exists
|
/// \return true if joint exists
|
||||||
|
|
|
@ -60,7 +60,7 @@ protected:
|
||||||
|
|
||||||
class CubicHermiteSplineFunctorWithArcLength : public CubicHermiteSplineFunctor {
|
class CubicHermiteSplineFunctorWithArcLength : public CubicHermiteSplineFunctor {
|
||||||
public:
|
public:
|
||||||
enum Constants { NUM_SUBDIVISIONS = 30 };
|
enum Constants { NUM_SUBDIVISIONS = 15 };
|
||||||
|
|
||||||
CubicHermiteSplineFunctorWithArcLength() : CubicHermiteSplineFunctor() {
|
CubicHermiteSplineFunctorWithArcLength() : CubicHermiteSplineFunctor() {
|
||||||
memset(_values, 0, sizeof(float) * (NUM_SUBDIVISIONS + 1));
|
memset(_values, 0, sizeof(float) * (NUM_SUBDIVISIONS + 1));
|
||||||
|
@ -71,11 +71,13 @@ public:
|
||||||
float alpha = 0.0f;
|
float alpha = 0.0f;
|
||||||
float accum = 0.0f;
|
float accum = 0.0f;
|
||||||
_values[0] = 0.0f;
|
_values[0] = 0.0f;
|
||||||
|
glm::vec3 prevValue = this->operator()(alpha);
|
||||||
for (int i = 1; i < NUM_SUBDIVISIONS + 1; i++) {
|
for (int i = 1; i < NUM_SUBDIVISIONS + 1; i++) {
|
||||||
accum += glm::distance(this->operator()(alpha),
|
glm::vec3 nextValue = this->operator()(alpha + DELTA);
|
||||||
this->operator()(alpha + DELTA));
|
accum += glm::distance(prevValue, nextValue);
|
||||||
alpha += DELTA;
|
alpha += DELTA;
|
||||||
_values[i] = accum;
|
_values[i] = accum;
|
||||||
|
prevValue = nextValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1247,30 +1247,30 @@ void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures) {
|
QVariantMap parseTexturesToMap(QString newTextures, const QVariantMap& defaultTextures) {
|
||||||
// If textures are unset, revert to original textures
|
// If textures are unset, revert to original textures
|
||||||
if (textures.isEmpty()) {
|
if (newTextures.isEmpty()) {
|
||||||
return defaultTextures;
|
return defaultTextures;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy: a ,\n-delimited list of filename:"texturepath"
|
// Legacy: a ,\n-delimited list of filename:"texturepath"
|
||||||
if (*textures.cbegin() != '{') {
|
if (*newTextures.cbegin() != '{') {
|
||||||
textures = "{\"" + textures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}";
|
newTextures = "{\"" + newTextures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonParseError error;
|
QJsonParseError error;
|
||||||
QJsonDocument texturesJson = QJsonDocument::fromJson(textures.toUtf8(), &error);
|
QJsonDocument newTexturesJson = QJsonDocument::fromJson(newTextures.toUtf8(), &error);
|
||||||
// If textures are invalid, revert to original textures
|
// If textures are invalid, revert to original textures
|
||||||
if (error.error != QJsonParseError::NoError) {
|
if (error.error != QJsonParseError::NoError) {
|
||||||
qWarning() << "Could not evaluate textures property value:" << textures;
|
qWarning() << "Could not evaluate textures property value:" << newTextures;
|
||||||
return defaultTextures;
|
return defaultTextures;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap texturesMap = texturesJson.toVariant().toMap();
|
QVariantMap newTexturesMap = newTexturesJson.toVariant().toMap();
|
||||||
// If textures are unset, revert to original textures
|
QVariantMap toReturn = defaultTextures;
|
||||||
if (texturesMap.isEmpty()) {
|
for (auto& texture : newTexturesMap.keys()) {
|
||||||
return defaultTextures;
|
toReturn[texture] = newTexturesMap[texture];
|
||||||
}
|
}
|
||||||
|
|
||||||
return texturesJson.toVariant().toMap();
|
return toReturn;
|
||||||
}
|
}
|
|
@ -152,7 +152,8 @@ void ShapeInfo::setSphere(float radius) {
|
||||||
void ShapeInfo::setMultiSphere(const std::vector<glm::vec3>& centers, const std::vector<float>& radiuses) {
|
void ShapeInfo::setMultiSphere(const std::vector<glm::vec3>& centers, const std::vector<float>& radiuses) {
|
||||||
_url = "";
|
_url = "";
|
||||||
_type = SHAPE_TYPE_MULTISPHERE;
|
_type = SHAPE_TYPE_MULTISPHERE;
|
||||||
assert(centers.size() == radiuses.size() && centers.size() > 0);
|
assert(centers.size() == radiuses.size());
|
||||||
|
assert(centers.size() > 0);
|
||||||
for (size_t i = 0; i < centers.size(); i++) {
|
for (size_t i = 0; i < centers.size(); i++) {
|
||||||
SphereData sphere = SphereData(centers[i], radiuses[i]);
|
SphereData sphere = SphereData(centers[i], radiuses[i]);
|
||||||
_sphereCollection.push_back(sphere);
|
_sphereCollection.push_back(sphere);
|
||||||
|
|
|
@ -238,7 +238,10 @@ void OffscreenQmlSurface::clearFocusItem() {
|
||||||
|
|
||||||
void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
|
void OffscreenQmlSurface::initializeEngine(QQmlEngine* engine) {
|
||||||
Parent::initializeEngine(engine);
|
Parent::initializeEngine(engine);
|
||||||
QQmlFileSelector* fileSelector = new QQmlFileSelector(engine);
|
auto fileSelector = QQmlFileSelector::get(engine);
|
||||||
|
if (!fileSelector) {
|
||||||
|
fileSelector = new QQmlFileSelector(engine);
|
||||||
|
}
|
||||||
fileSelector->setExtraSelectors(FileUtils::getFileSelectors());
|
fileSelector->setExtraSelectors(FileUtils::getFileSelectors());
|
||||||
|
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
|
|
65
prebuild.py
65
prebuild.py
|
@ -35,9 +35,50 @@ import re
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import functools
|
import functools
|
||||||
|
import subprocess
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from uuid import uuid4
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
print = functools.partial(print, flush=True)
|
print = functools.partial(print, flush=True)
|
||||||
|
|
||||||
|
class TrackableLogger(logging.Logger):
|
||||||
|
guid = str(uuid4())
|
||||||
|
|
||||||
|
def _log(self, msg, *args, **kwargs):
|
||||||
|
x = {'guid': self.guid}
|
||||||
|
if 'extra' in kwargs:
|
||||||
|
kwargs['extra'].update(x)
|
||||||
|
else:
|
||||||
|
kwargs['extra'] = x
|
||||||
|
super()._log(msg, *args, **kwargs)
|
||||||
|
|
||||||
|
logging.setLoggerClass(TrackableLogger)
|
||||||
|
logger = logging.getLogger('prebuild')
|
||||||
|
|
||||||
|
def headSha():
|
||||||
|
repo_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
git = subprocess.Popen(
|
||||||
|
'git rev-parse --short HEAD',
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||||
|
shell=True, cwd=repo_dir, universal_newlines=True,
|
||||||
|
)
|
||||||
|
stdout, _ = git.communicate()
|
||||||
|
sha = stdout.split('\n')[0]
|
||||||
|
if not sha:
|
||||||
|
raise RuntimeError("couldn't find git sha")
|
||||||
|
return sha
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def timer(name):
|
||||||
|
''' Print the elapsed time a context's execution takes to execute '''
|
||||||
|
start = time.time()
|
||||||
|
yield
|
||||||
|
# Please take care when modifiying this print statement.
|
||||||
|
# Log parsing logic may depend on it.
|
||||||
|
logger.info('%s took %.3f secs' % (name, time.time() - start))
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
# our custom ports, relative to the script location
|
# our custom ports, relative to the script location
|
||||||
defaultPortsPath = hifi_utils.scriptRelative('cmake', 'ports')
|
defaultPortsPath = hifi_utils.scriptRelative('cmake', 'ports')
|
||||||
|
@ -50,6 +91,7 @@ def parse_args():
|
||||||
parser.add_argument('--vcpkg-root', type=str, help='The location of the vcpkg distribution')
|
parser.add_argument('--vcpkg-root', type=str, help='The location of the vcpkg distribution')
|
||||||
parser.add_argument('--build-root', required=True, type=str, help='The location of the cmake build')
|
parser.add_argument('--build-root', required=True, type=str, help='The location of the cmake build')
|
||||||
parser.add_argument('--ports-path', type=str, default=defaultPortsPath)
|
parser.add_argument('--ports-path', type=str, default=defaultPortsPath)
|
||||||
|
parser.add_argument('--ci-build', action='store_true')
|
||||||
if True:
|
if True:
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
else:
|
else:
|
||||||
|
@ -66,11 +108,19 @@ def main():
|
||||||
del os.environ[var]
|
del os.environ[var]
|
||||||
|
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|
||||||
|
if args.ci_build:
|
||||||
|
logging.basicConfig(datefmt='%s', format='%(asctime)s %(guid)s %(message)s', level=logging.INFO)
|
||||||
|
|
||||||
|
logger.info('sha=%s' % headSha())
|
||||||
|
logger.info('start')
|
||||||
|
|
||||||
# Only allow one instance of the program to run at a time
|
# Only allow one instance of the program to run at a time
|
||||||
pm = hifi_vcpkg.VcpkgRepo(args)
|
pm = hifi_vcpkg.VcpkgRepo(args)
|
||||||
with hifi_singleton.Singleton(pm.lockFile) as lock:
|
with hifi_singleton.Singleton(pm.lockFile) as lock:
|
||||||
if not pm.upToDate():
|
with timer('Bootstraping'):
|
||||||
pm.bootstrap()
|
if not pm.upToDate():
|
||||||
|
pm.bootstrap()
|
||||||
|
|
||||||
# Always write the tag, even if we changed nothing. This
|
# Always write the tag, even if we changed nothing. This
|
||||||
# allows vcpkg to reclaim disk space by identifying directories with
|
# allows vcpkg to reclaim disk space by identifying directories with
|
||||||
|
@ -80,11 +130,13 @@ def main():
|
||||||
# Grab our required dependencies:
|
# Grab our required dependencies:
|
||||||
# * build host tools, like spirv-cross and scribe
|
# * build host tools, like spirv-cross and scribe
|
||||||
# * build client dependencies like openssl and nvtt
|
# * build client dependencies like openssl and nvtt
|
||||||
pm.setupDependencies()
|
with timer('Setting up dependencies'):
|
||||||
|
pm.setupDependencies()
|
||||||
|
|
||||||
# wipe out the build directories (after writing the tag, since failure
|
# wipe out the build directories (after writing the tag, since failure
|
||||||
# here shouldn't invalidte the vcpkg install)
|
# here shouldn't invalidte the vcpkg install)
|
||||||
pm.cleanBuilds()
|
with timer('Cleaning builds'):
|
||||||
|
pm.cleanBuilds()
|
||||||
|
|
||||||
# If we're running in android mode, we also need to grab a bunch of additional binaries
|
# If we're running in android mode, we also need to grab a bunch of additional binaries
|
||||||
# (this logic is all migrated from the old setupDependencies tasks in gradle)
|
# (this logic is all migrated from the old setupDependencies tasks in gradle)
|
||||||
|
@ -98,7 +150,10 @@ def main():
|
||||||
hifi_android.QtPackager(appPath, qtPath).bundle()
|
hifi_android.QtPackager(appPath, qtPath).bundle()
|
||||||
|
|
||||||
# Write the vcpkg config to the build directory last
|
# Write the vcpkg config to the build directory last
|
||||||
pm.writeConfig()
|
with timer('Writing configuration'):
|
||||||
|
pm.writeConfig()
|
||||||
|
|
||||||
|
logger.info('end')
|
||||||
|
|
||||||
print(sys.argv)
|
print(sys.argv)
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -681,6 +681,9 @@ function loaded() {
|
||||||
if (isNullOrEmpty(valueB)) {
|
if (isNullOrEmpty(valueB)) {
|
||||||
return (isDefaultSort ? -1 : 1) * (isAscendingSort ? 1 : -1);
|
return (isDefaultSort ? -1 : 1) * (isAscendingSort ? 1 : -1);
|
||||||
}
|
}
|
||||||
|
if (typeof(valueA) === "string") {
|
||||||
|
return valueA.localeCompare(valueB);
|
||||||
|
}
|
||||||
return valueA < valueB ? -1 : 1;
|
return valueA < valueB ? -1 : 1;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -135,6 +135,7 @@ EntityShape.prototype = {
|
||||||
overlayProperties.canCastShadows = false;
|
overlayProperties.canCastShadows = false;
|
||||||
overlayProperties.parentID = this.entityID;
|
overlayProperties.parentID = this.entityID;
|
||||||
overlayProperties.collisionless = true;
|
overlayProperties.collisionless = true;
|
||||||
|
overlayProperties.ignorePickIntersection = true;
|
||||||
this.entity = Entities.addEntity(overlayProperties, "local");
|
this.entity = Entities.addEntity(overlayProperties, "local");
|
||||||
var PROJECTED_MATERIALS = false;
|
var PROJECTED_MATERIALS = false;
|
||||||
this.materialEntity = Entities.addEntity({
|
this.materialEntity = Entities.addEntity({
|
||||||
|
@ -146,6 +147,7 @@ EntityShape.prototype = {
|
||||||
priority: 1,
|
priority: 1,
|
||||||
materialMappingMode: PROJECTED_MATERIALS ? "projected" : "uv",
|
materialMappingMode: PROJECTED_MATERIALS ? "projected" : "uv",
|
||||||
materialURL: Script.resolvePath("../assets/images/materials/GridPattern.json"),
|
materialURL: Script.resolvePath("../assets/images/materials/GridPattern.json"),
|
||||||
|
ignorePickIntersection: true,
|
||||||
}, "local");
|
}, "local");
|
||||||
},
|
},
|
||||||
update: function() {
|
update: function() {
|
||||||
|
|
|
@ -22,9 +22,11 @@
|
||||||
#include <ResourceRequestObserver.h>
|
#include <ResourceRequestObserver.h>
|
||||||
#include <StatTracker.h>
|
#include <StatTracker.h>
|
||||||
#include <test-utils/QTestExtensions.h>
|
#include <test-utils/QTestExtensions.h>
|
||||||
|
#include <test-utils/GLMTestUtils.h>
|
||||||
|
|
||||||
QTEST_MAIN(AnimTests)
|
QTEST_MAIN(AnimTests)
|
||||||
|
|
||||||
|
|
||||||
const float TEST_EPSILON = 0.001f;
|
const float TEST_EPSILON = 0.001f;
|
||||||
|
|
||||||
void AnimTests::initTestCase() {
|
void AnimTests::initTestCase() {
|
||||||
|
@ -372,16 +374,10 @@ void AnimTests::testAnimPose() {
|
||||||
const glm::quat ROT_Y_180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0, 0.0f));
|
const glm::quat ROT_Y_180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0, 0.0f));
|
||||||
const glm::quat ROT_Z_30 = glm::angleAxis(PI / 6.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
const glm::quat ROT_Z_30 = glm::angleAxis(PI / 6.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
std::vector<glm::vec3> scaleVec = {
|
std::vector<float> scaleVec = {
|
||||||
glm::vec3(1),
|
1.0f,
|
||||||
glm::vec3(2.0f, 1.0f, 1.0f),
|
2.0f,
|
||||||
glm::vec3(1.0f, 0.5f, 1.0f),
|
0.5f
|
||||||
glm::vec3(1.0f, 1.0f, 1.5f),
|
|
||||||
glm::vec3(2.0f, 0.5f, 1.5f),
|
|
||||||
glm::vec3(-2.0f, 0.5f, 1.5f),
|
|
||||||
glm::vec3(2.0f, -0.5f, 1.5f),
|
|
||||||
glm::vec3(2.0f, 0.5f, -1.5f),
|
|
||||||
glm::vec3(-2.0f, -0.5f, -1.5f),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<glm::quat> rotVec = {
|
std::vector<glm::quat> rotVec = {
|
||||||
|
@ -411,7 +407,7 @@ void AnimTests::testAnimPose() {
|
||||||
for (auto& trans : transVec) {
|
for (auto& trans : transVec) {
|
||||||
|
|
||||||
// build a matrix the old fashioned way.
|
// build a matrix the old fashioned way.
|
||||||
glm::mat4 scaleMat = glm::scale(glm::mat4(), scale);
|
glm::mat4 scaleMat = glm::scale(glm::mat4(), glm::vec3(scale));
|
||||||
glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans);
|
glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans);
|
||||||
glm::mat4 rawMat = rotTransMat * scaleMat;
|
glm::mat4 rawMat = rotTransMat * scaleMat;
|
||||||
|
|
||||||
|
@ -429,7 +425,7 @@ void AnimTests::testAnimPose() {
|
||||||
for (auto& trans : transVec) {
|
for (auto& trans : transVec) {
|
||||||
|
|
||||||
// build a matrix the old fashioned way.
|
// build a matrix the old fashioned way.
|
||||||
glm::mat4 scaleMat = glm::scale(glm::mat4(), scale);
|
glm::mat4 scaleMat = glm::scale(glm::mat4(), glm::vec3(scale));
|
||||||
glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans);
|
glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans);
|
||||||
glm::mat4 rawMat = rotTransMat * scaleMat;
|
glm::mat4 rawMat = rotTransMat * scaleMat;
|
||||||
|
|
||||||
|
@ -445,6 +441,145 @@ void AnimTests::testAnimPose() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimTests::testAnimPoseMultiply() {
|
||||||
|
const float PI = (float)M_PI;
|
||||||
|
const glm::quat ROT_X_90 = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
|
const glm::quat ROT_Y_180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0, 0.0f));
|
||||||
|
const glm::quat ROT_Z_30 = glm::angleAxis(PI / 6.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
|
std::vector<float> scaleVec = {
|
||||||
|
1.0f,
|
||||||
|
2.0f,
|
||||||
|
0.5f,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<glm::quat> rotVec = {
|
||||||
|
glm::quat(),
|
||||||
|
ROT_X_90,
|
||||||
|
ROT_Y_180,
|
||||||
|
ROT_Z_30,
|
||||||
|
ROT_X_90 * ROT_Y_180 * ROT_Z_30,
|
||||||
|
-ROT_Y_180
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<glm::vec3> transVec = {
|
||||||
|
glm::vec3(),
|
||||||
|
glm::vec3(10.0f, 0.0f, 0.0f),
|
||||||
|
glm::vec3(0.0f, 5.0f, 0.0f),
|
||||||
|
glm::vec3(0.0f, 0.0f, 7.5f),
|
||||||
|
glm::vec3(10.0f, 5.0f, 7.5f),
|
||||||
|
glm::vec3(-10.0f, 5.0f, 7.5f),
|
||||||
|
glm::vec3(10.0f, -5.0f, 7.5f),
|
||||||
|
glm::vec3(10.0f, 5.0f, -7.5f)
|
||||||
|
};
|
||||||
|
|
||||||
|
const float TEST_EPSILON = 0.001f;
|
||||||
|
|
||||||
|
std::vector<glm::mat4> matrixVec;
|
||||||
|
std::vector<AnimPose> poseVec;
|
||||||
|
|
||||||
|
for (auto& scale : scaleVec) {
|
||||||
|
for (auto& rot : rotVec) {
|
||||||
|
for (auto& trans : transVec) {
|
||||||
|
|
||||||
|
// build a matrix the old fashioned way.
|
||||||
|
glm::mat4 scaleMat = glm::scale(glm::mat4(), glm::vec3(scale));
|
||||||
|
glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans);
|
||||||
|
glm::mat4 rawMat = rotTransMat * scaleMat;
|
||||||
|
|
||||||
|
matrixVec.push_back(rawMat);
|
||||||
|
|
||||||
|
// use an anim pose to build a matrix by parts.
|
||||||
|
AnimPose pose(scale, rot, trans);
|
||||||
|
poseVec.push_back(pose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < matrixVec.size(); i++) {
|
||||||
|
for (int j = 0; j < matrixVec.size(); j++) {
|
||||||
|
|
||||||
|
// multiply the matrices together
|
||||||
|
glm::mat4 matrix = matrixVec[i] * matrixVec[j];
|
||||||
|
|
||||||
|
// convert to matrix (note this will remove sheer from the matrix)
|
||||||
|
AnimPose resultA(matrix);
|
||||||
|
|
||||||
|
// multiply the poses together directly
|
||||||
|
AnimPose resultB = poseVec[i] * poseVec[j];
|
||||||
|
|
||||||
|
/*
|
||||||
|
qDebug() << "matrixVec[" << i << "] =" << matrixVec[i];
|
||||||
|
qDebug() << "matrixVec[" << j << "] =" << matrixVec[j];
|
||||||
|
qDebug() << "matrixResult =" << resultA;
|
||||||
|
|
||||||
|
qDebug() << "poseVec[" << i << "] =" << poseVec[i];
|
||||||
|
qDebug() << "poseVec[" << j << "] =" << poseVec[j];
|
||||||
|
qDebug() << "poseResult =" << resultB;
|
||||||
|
*/
|
||||||
|
|
||||||
|
// compare results.
|
||||||
|
QCOMPARE_WITH_ABS_ERROR(resultA.scale(), resultB.scale(), TEST_EPSILON);
|
||||||
|
QCOMPARE_WITH_ABS_ERROR(resultA.rot(), resultB.rot(), TEST_EPSILON);
|
||||||
|
QCOMPARE_WITH_ABS_ERROR(resultA.trans(), resultB.trans(), TEST_EPSILON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimTests::testAnimPoseInverse() {
|
||||||
|
const float PI = (float)M_PI;
|
||||||
|
const glm::quat ROT_X_90 = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
|
const glm::quat ROT_Y_180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0, 0.0f));
|
||||||
|
const glm::quat ROT_Z_30 = glm::angleAxis(PI / 6.0f, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
|
|
||||||
|
std::vector<float> scaleVec = {
|
||||||
|
1.0f,
|
||||||
|
2.0f,
|
||||||
|
0.5f
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<glm::quat> rotVec = {
|
||||||
|
glm::quat(),
|
||||||
|
ROT_X_90,
|
||||||
|
ROT_Y_180,
|
||||||
|
ROT_Z_30,
|
||||||
|
ROT_X_90 * ROT_Y_180 * ROT_Z_30,
|
||||||
|
-ROT_Y_180
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<glm::vec3> transVec = {
|
||||||
|
glm::vec3(),
|
||||||
|
glm::vec3(10.0f, 0.0f, 0.0f),
|
||||||
|
glm::vec3(0.0f, 5.0f, 0.0f),
|
||||||
|
glm::vec3(0.0f, 0.0f, 7.5f),
|
||||||
|
glm::vec3(10.0f, 5.0f, 7.5f),
|
||||||
|
glm::vec3(-10.0f, 5.0f, 7.5f),
|
||||||
|
glm::vec3(10.0f, -5.0f, 7.5f),
|
||||||
|
glm::vec3(10.0f, 5.0f, -7.5f)
|
||||||
|
};
|
||||||
|
|
||||||
|
const float TEST_EPSILON = 0.001f;
|
||||||
|
|
||||||
|
for (auto& scale : scaleVec) {
|
||||||
|
for (auto& rot : rotVec) {
|
||||||
|
for (auto& trans : transVec) {
|
||||||
|
|
||||||
|
// build a matrix the old fashioned way.
|
||||||
|
glm::mat4 scaleMat = glm::scale(glm::mat4(), glm::vec3(scale));
|
||||||
|
glm::mat4 rotTransMat = createMatFromQuatAndPos(rot, trans);
|
||||||
|
glm::mat4 rawMat = glm::inverse(rotTransMat * scaleMat);
|
||||||
|
|
||||||
|
// use an anim pose to build a matrix by parts.
|
||||||
|
AnimPose pose(scale, rot, trans);
|
||||||
|
glm::mat4 poseMat = pose.inverse();
|
||||||
|
|
||||||
|
QCOMPARE_WITH_ABS_ERROR(rawMat, poseMat, TEST_EPSILON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void AnimTests::testExpressionTokenizer() {
|
void AnimTests::testExpressionTokenizer() {
|
||||||
QString str = "(10 + x) >= 20.1 && (y != !z)";
|
QString str = "(10 + x) >= 20.1 && (y != !z)";
|
||||||
AnimExpression e("x");
|
AnimExpression e("x");
|
||||||
|
|
|
@ -27,6 +27,8 @@ private slots:
|
||||||
void testVariant();
|
void testVariant();
|
||||||
void testAccumulateTime();
|
void testAccumulateTime();
|
||||||
void testAnimPose();
|
void testAnimPose();
|
||||||
|
void testAnimPoseMultiply();
|
||||||
|
void testAnimPoseInverse();
|
||||||
void testExpressionTokenizer();
|
void testExpressionTokenizer();
|
||||||
void testExpressionParser();
|
void testExpressionParser();
|
||||||
void testExpressionEvaluator();
|
void testExpressionEvaluator();
|
||||||
|
|
|
@ -19,20 +19,36 @@ function(check_test name)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
if (BUILD_TOOLS)
|
if (BUILD_TOOLS)
|
||||||
set(ALL_TOOLS
|
# Allow different tools for production builds
|
||||||
udt-test
|
if (RELEASE_TYPE STREQUAL "PRODUCTION")
|
||||||
vhacd-util
|
set(ALL_TOOLS
|
||||||
frame-optimizer
|
udt-test
|
||||||
gpu-frame-player
|
vhacd-util
|
||||||
ice-client
|
frame-optimizer
|
||||||
ktx-tool
|
gpu-frame-player
|
||||||
ac-client
|
ice-client
|
||||||
skeleton-dump
|
ktx-tool
|
||||||
atp-client
|
ac-client
|
||||||
oven
|
skeleton-dump
|
||||||
nitpick
|
atp-client
|
||||||
)
|
oven
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set(ALL_TOOLS
|
||||||
|
udt-test
|
||||||
|
vhacd-util
|
||||||
|
frame-optimizer
|
||||||
|
gpu-frame-player
|
||||||
|
ice-client
|
||||||
|
ktx-tool
|
||||||
|
ac-client
|
||||||
|
skeleton-dump
|
||||||
|
atp-client
|
||||||
|
oven
|
||||||
|
nitpick
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
foreach(TOOL ${ALL_TOOLS})
|
foreach(TOOL ${ALL_TOOLS})
|
||||||
check_test(${TOOL})
|
check_test(${TOOL})
|
||||||
if (${BUILD_TOOL_RESULT})
|
if (${BUILD_TOOL_RESULT})
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue