merge master

This commit is contained in:
luiscuenca 2019-10-02 13:24:32 -07:00
commit 059bcc10cc
No known key found for this signature in database
GPG key ID: 2387ECD129A6961D
95 changed files with 2597 additions and 589 deletions

View file

@ -22,6 +22,7 @@
#include <AccountManager.h>
#include <AddressManager.h>
#include <Assignment.h>
#include <CrashAnnotations.h>
#include <LogHandler.h>
#include <LogUtils.h>
#include <LimitedNodeList.h>
@ -144,6 +145,7 @@ AssignmentClient::~AssignmentClient() {
}
void AssignmentClient::aboutToQuit() {
crash::annotations::setShutdownState(true);
stopAssignmentClient();
}
@ -173,6 +175,7 @@ void AssignmentClient::sendStatusPacketToACM() {
void AssignmentClient::sendAssignmentRequest() {
if (!_currentAssignment && !_isAssigned) {
crash::annotations::setShutdownState(false);
auto nodeList = DependencyManager::get<NodeList>();
@ -289,6 +292,8 @@ void AssignmentClient::handleAuthenticationRequest() {
}
void AssignmentClient::assignmentCompleted() {
crash::annotations::setShutdownState(true);
// we expect that to be here the previous assignment has completely cleaned up
assert(_currentAssignment.isNull());

View file

@ -38,7 +38,7 @@ MixerAvatar::MixerAvatar() {
_pendingEvent = false;
_verifyState = verificationFailed;
_needsIdentityUpdate = true;
qCDebug(avatars) << "Dynamic verification TIMED-OUT for " << getDisplayName() << getSessionUUID();
qCDebug(avatars) << "Dynamic verification TIMED-OUT for" << getDisplayName() << getSessionUUID();
} else {
qCDebug(avatars) << "Ignoring timeout of avatar challenge";
}
@ -287,7 +287,7 @@ void MixerAvatar::processCertifyEvents() {
<< ":" << _dynamicMarketResponse;
}
} else {
qCDebug(avatars) << "Get owner status failed for " << getDisplayName() << _marketplaceIdFromURL <<
qCDebug(avatars) << "Get owner status failed for" << getDisplayName() << _marketplaceIdFromURL <<
"message:" << responseJson["message"].toString();
_verifyState = error;
}
@ -356,7 +356,7 @@ void MixerAvatar::processChallengeResponse(ReceivedMessage& response) {
_verifyState = challengeResult ? verificationSucceeded : verificationFailed;
_needsIdentityUpdate = true;
if (_verifyState == verificationFailed) {
qCDebug(avatars) << "Dynamic verification FAILED for " << getDisplayName() << getSessionUUID();
qCDebug(avatars) << "Dynamic verification FAILED for" << getDisplayName() << getSessionUUID();
} else {
qCDebug(avatars) << "Dynamic verification SUCCEEDED for" << getDisplayName() << getSessionUUID();
}

View file

@ -20,7 +20,7 @@ macro(SETUP_HIFI_LIBRARY)
foreach(SRC ${AVX_SRCS})
if (WIN32)
set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS /arch:AVX)
elseif (APPLE OR UNIX)
elseif (APPLE OR (UNIX AND NOT ANDROID))
set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS -mavx)
endif()
endforeach()
@ -30,7 +30,7 @@ macro(SETUP_HIFI_LIBRARY)
foreach(SRC ${AVX2_SRCS})
if (WIN32)
set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS /arch:AVX2)
elseif (APPLE OR UNIX)
elseif (APPLE OR (UNIX AND NOT ANDROID))
set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS "-mavx2 -mfma")
endif()
endforeach()
@ -44,7 +44,7 @@ macro(SETUP_HIFI_LIBRARY)
if (COMPILER_SUPPORTS_AVX512)
set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS /arch:AVX512)
endif()
elseif (APPLE OR UNIX)
elseif (APPLE OR (UNIX AND NOT ANDROID))
check_cxx_compiler_flag("-mavx512f" COMPILER_SUPPORTS_AVX512)
if (COMPILER_SUPPORTS_AVX512)
set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS -mavx512f)

View file

@ -250,14 +250,14 @@ $(document).ready(function(){
// set focus to the first input in the new row
$target.closest('table').find('tr.inputs input:first').focus();
}
} else {
var tableRows = sibling.parent();
var tableBody = tableRows.parent();
var tableRows = sibling.parent();
var tableBody = tableRows.parent();
// if theres no more siblings, we should jump to a new row
if (sibling.next().length == 0 && tableRows.nextAll().length == 1) {
tableBody.find("." + Settings.ADD_ROW_BUTTON_CLASS).click();
// if theres no more siblings, we should jump to a new row
if (sibling.next().length == 0 && tableRows.nextAll().length == 1) {
tableBody.find("." + Settings.ADD_ROW_BUTTON_CLASS).click();
}
}
}

View file

@ -32,6 +32,7 @@
#include <AccountManager.h>
#include <AssetClient.h>
#include <BuildInfo.h>
#include <CrashAnnotations.h>
#include <DependencyManager.h>
#include <HifiConfigVariantMap.h>
#include <HTTPConnection.h>
@ -185,6 +186,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
qDebug() << "[VERSION] BUILD_GLOBAL_SERVICES:" << BuildInfo::BUILD_GLOBAL_SERVICES;
qDebug() << "[VERSION] We will be using this name to find ICE servers:" << _iceServerAddr;
connect(this, &QCoreApplication::aboutToQuit, this, &DomainServer::aboutToQuit);
// make sure we have a fresh AccountManager instance
// (need this since domain-server can restart itself and maintain static variables)
@ -432,6 +434,10 @@ DomainServer::~DomainServer() {
DependencyManager::destroy<LimitedNodeList>();
}
void DomainServer::aboutToQuit() {
crash::annotations::setShutdownState(true);
}
void DomainServer::queuedQuit(QString quitMessage, int exitCode) {
if (!quitMessage.isEmpty()) {
qWarning() << qPrintable(quitMessage);

View file

@ -136,6 +136,8 @@ private slots:
void tokenGrantFinished();
void profileRequestFinished();
void aboutToQuit();
signals:
void iceServerChanged();
void userConnected();

View file

@ -15,9 +15,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <BuildInfo.h>
#include <CrashAnnotations.h>
#include <LogHandler.h>
#include <SharedUtil.h>
#include <BuildInfo.h>
#include "DomainServer.h"
@ -32,6 +33,7 @@ int main(int argc, char* argv[]) {
// use a do-while to handle domain-server restart
do {
crash::annotations::setShutdownState(false);
DomainServer domainServer(argc, argv);
currentExitCode = domainServer.exec();
} while (currentExitCode == DomainServer::EXIT_CODE_REBOOT);
@ -39,4 +41,3 @@ int main(int argc, char* argv[]) {
qInfo() << "Quitting.";
return currentExitCode;
}

View file

@ -70,12 +70,16 @@ file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h")
GroupSources("src")
list(APPEND INTERFACE_SRCS ${RESOURCES_RCC})
# grab the Objective-C sources on OS X
if (APPLE)
file(GLOB_RECURSE INTERFACE_OBJCPP_SRCS "src/*.m" "src/*.mm")
list(APPEND INTERFACE_SRCS ${INTERFACE_OBJCPP_SRCS})
endif ()
# Add SpeechRecognizer if on Windows or OS X, otherwise remove
if (WIN32)
# Use .cpp and .h files as is.
elseif (APPLE)
file(GLOB INTERFACE_OBJCPP_SRCS "src/SpeechRecognizer.mm")
set(INTERFACE_SRCS ${INTERFACE_SRCS} ${INTERFACE_OBJCPP_SRCS})
get_filename_component(SPEECHRECOGNIZER_CPP "src/SpeechRecognizer.cpp" ABSOLUTE)
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
else ()

View file

@ -1544,103 +1544,292 @@
"type": "randomSwitchStateMachine"
},
{
"children": [
"children": [
{
"children": [
"children": [
{
"children": [
{
"children": [
],
"data": {
"endFrame": 21,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_all.fbx"
},
"id": "seatedReactionPointIntro",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 100,
"loopFlag": true,
"startFrame": 21,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_all.fbx"
},
"id": "seatedReactionPointLoop",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 134,
"loopFlag": false,
"mirrorFlag": false,
"startFrame": 100,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_all.fbx"
},
"id": "seatedReactionPointOutro",
"type": "clip"
}
],
"data": {
"currentState": "seatedReactionPointIntro",
"randomSwitchTimeMax": 10,
"randomSwitchTimeMin": 1,
"states": [
{
"easingType": "easeInOutQuad",
"id": "seatedReactionPointIntro",
"interpDuration": 18,
"interpTarget": 18,
"interpType": "evaluateBoth",
"priority": 1,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionPointLoop",
"var": "seatedReactionPointIntroOnDone"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "seatedReactionPointLoop",
"interpDuration": 18,
"interpTarget": 18,
"interpType": "evaluateBoth",
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionPointOutro",
"var": "reactionPointDisabled"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "seatedReactionPointOutro",
"interpDuration": 18,
"interpTarget": 18,
"interpType": "evaluateBoth",
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionPointLoop",
"var": "reactionPointEnabled"
}
]
}
],
"triggerRandomSwitch": ""
},
"id": "seatedReactionPoint",
"type": "randomSwitchStateMachine"
}
],
"data": {
"endFrame": 21,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_all.fbx"
"alpha": 0,
"alphaVar": "seatedPointBlendAlpha",
"blendType": "addAbsolute"
},
"id": "seatedReactionPointIntro",
"type": "clip"
"id": "seatedReactionPointBase",
"type": "blendLinear"
},
{
"children": [
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 11,
"loopFlag": true,
"startFrame": 11,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx"
},
"id": "seatedPointLeft",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 30,
"loopFlag": true,
"startFrame": 30,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx"
},
"id": "seatedPointRight",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 50,
"loopFlag": true,
"startFrame": 50,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx"
},
"id": "seatedPointUp",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 70,
"loopFlag": true,
"startFrame": 70,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx"
},
"id": "seatedPointDown",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 90,
"loopFlag": true,
"startFrame": 90,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx"
},
"id": "seatedPointUpLeft",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 110,
"loopFlag": true,
"startFrame": 110,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx"
},
"id": "seatedPointUpRight",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 130,
"loopFlag": true,
"startFrame": 130,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx"
},
"id": "seatedPointDownLeft",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 150,
"loopFlag": true,
"startFrame": 150,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx"
},
"id": "seatedPointDownRight",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 3,
"loopFlag": true,
"startFrame": 3,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_aimoffsets.fbx"
},
"id": "seatedPointCenter",
"type": "clip"
}
],
"data": {
"endFrame": 100,
"loopFlag": true,
"startFrame": 21,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_all.fbx"
"alpha": [
0,
0,
0
],
"alphaVar": "pointAroundAlpha",
"centerId": "seatedPointCenter",
"downId": "seatedPointDown",
"downLeftId": "seatedPointDownLeft",
"downRightId": "seatedPointDownRight",
"leftId": "seatedPointLeft",
"rightId": "seatedPointRight",
"upId": "seatedPointUp",
"upLeftId": "seatedPointUpLeft",
"upRightId": "seatedPointUpRight"
},
"id": "seatedReactionPointLoop",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 134,
"loopFlag": false,
"mirrorFlag": false,
"startFrame": 100,
"timeScale": 1,
"url": "qrc:///avatar/animations/sitting_emote_point_all.fbx"
},
"id": "seatedReactionPointOutro",
"type": "clip"
"id": "seatedPointAround",
"type": "blendDirectional"
}
],
"data": {
"currentState": "seatedReactionPointIntro",
"randomSwitchTimeMax": 10,
"randomSwitchTimeMin": 1,
"states": [
{
"easingType": "easeInOutQuad",
"id": "seatedReactionPointIntro",
"interpDuration": 18,
"interpTarget": 18,
"interpType": "evaluateBoth",
"priority": 1,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionPointLoop",
"var": "seatedReactionPointIntroOnDone"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "seatedReactionPointLoop",
"interpDuration": 18,
"interpTarget": 18,
"interpType": "evaluateBoth",
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionPointOutro",
"var": "reactionPointDisabled"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "seatedReactionPointOutro",
"interpDuration": 18,
"interpTarget": 18,
"interpType": "evaluateBoth",
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "seatedReactionPointLoop",
"var": "reactionPointEnabled"
}
]
}
],
"triggerRandomSwitch": ""
"alpha": 0,
"alphaVar": "pointBlendAlpha",
"blendType": "addAbsolute"
},
"id": "seatedReactionPoint",
"type": "randomSwitchStateMachine"
"type": "blendLinear"
}
],
"data": {
@ -2802,7 +2991,7 @@
"children": [
],
"data": {
"endFrame": 92,
"endFrame": 84,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
@ -2815,7 +3004,7 @@
"children": [
],
"data": {
"endFrame": 158,
"endFrame": 100,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
@ -3531,97 +3720,275 @@
"children": [
{
"children": [
{
"children": [
],
"data": {
"endFrame": 21,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_all.fbx"
},
"id": "reactionPointIntro",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 100,
"loopFlag": true,
"startFrame": 21,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_all.fbx"
},
"id": "reactionPointLoop",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 134,
"loopFlag": false,
"startFrame": 100,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_all.fbx"
},
"id": "reactionPointOutro",
"type": "clip"
}
],
"data": {
"endFrame": 21,
"loopFlag": false,
"startFrame": 1,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_all.fbx"
"currentState": "reactionPointIntro",
"randomSwitchTimeMax": 10,
"randomSwitchTimeMin": 1,
"states": [
{
"easingType": "easeInOutQuad",
"id": "reactionPointIntro",
"interpDuration": 1,
"interpTarget": 1,
"interpType": "evaluateBoth",
"priority": 1,
"resume": false,
"transitions": [
{
"randomSwitchState": "reactionPointLoop",
"var": "reactionPointIntroOnDone"
}
]
},
{
"id": "reactionPointLoop",
"interpDuration": 1,
"interpTarget": 1,
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "reactionPointOutro",
"var": "reactionPointDisabled"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "reactionPointOutro",
"interpDuration": 6,
"interpTarget": 6,
"interpType": "evaluateBoth",
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "reactionPointLoop",
"var": "reactionPointEnabled"
}
]
}
],
"triggerRandomSwitch": ""
},
"id": "reactionPointIntro",
"type": "clip"
"id": "reactionPoint",
"type": "randomSwitchStateMachine"
},
{
"children": [
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 11,
"loopFlag": true,
"startFrame": 11,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx"
},
"id": "idlePointLeft",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 30,
"loopFlag": true,
"startFrame": 30,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx"
},
"id": "idlePointRight",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 50,
"loopFlag": true,
"startFrame": 50,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx"
},
"id": "idlePointUp",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 70,
"loopFlag": true,
"startFrame": 70,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx"
},
"id": "idlePointDown",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 90,
"loopFlag": true,
"startFrame": 90,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx"
},
"id": "idlePointUpLeft",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 110,
"loopFlag": true,
"startFrame": 110,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx"
},
"id": "idlePointUpRight",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 130,
"loopFlag": true,
"startFrame": 130,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx"
},
"id": "idlePointDownLeft",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 150,
"loopFlag": true,
"startFrame": 150,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx"
},
"id": "idlePointDownRight",
"type": "clip"
},
{
"children": [
],
"data": {
"baseFrame": 1,
"baseURL": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx",
"blendType": "addAbsolute",
"endFrame": 3,
"loopFlag": true,
"startFrame": 3,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_aimoffsets.fbx"
},
"id": "idlePointCenter",
"type": "clip"
}
],
"data": {
"endFrame": 100,
"loopFlag": true,
"startFrame": 21,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_all.fbx"
"alpha": [
0,
0,
0
],
"alphaVar": "pointAroundAlpha",
"centerId": "idlePointCenter",
"downId": "idlePointDown",
"downLeftId": "idlePointDownLeft",
"downRightId": "idlePointDownRight",
"leftId": "idlePointLeft",
"rightId": "idlePointRight",
"upId": "idlePointUp",
"upLeftId": "idlePointUpLeft",
"upRightId": "idlePointUpRight"
},
"id": "reactionPointLoop",
"type": "clip"
},
{
"children": [
],
"data": {
"endFrame": 134,
"loopFlag": false,
"startFrame": 100,
"timeScale": 1,
"url": "qrc:///avatar/animations/emote_point01_all.fbx"
},
"id": "reactionPointOutro",
"type": "clip"
"id": "idlePointAround",
"type": "blendDirectional"
}
],
"data": {
"currentState": "reactionPointIntro",
"randomSwitchTimeMax": 10,
"randomSwitchTimeMin": 1,
"states": [
{
"easingType": "easeInOutQuad",
"id": "reactionPointIntro",
"interpDuration": 1,
"interpTarget": 1,
"interpType": "evaluateBoth",
"priority": 1,
"resume": false,
"transitions": [
{
"randomSwitchState": "reactionPointLoop",
"var": "reactionPointIntroOnDone"
}
]
},
{
"id": "reactionPointLoop",
"interpDuration": 1,
"interpTarget": 1,
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "reactionPointOutro",
"var": "reactionPointDisabled"
}
]
},
{
"easingType": "easeInOutQuad",
"id": "reactionPointOutro",
"interpDuration": 6,
"interpTarget": 6,
"interpType": "evaluateBoth",
"priority": 0,
"resume": false,
"transitions": [
{
"randomSwitchState": "reactionPointLoop",
"var": "reactionPointEnabled"
}
]
}
],
"triggerRandomSwitch": ""
"alpha": 0,
"alphaVar": "pointBlendAlpha",
"blendType": "addAbsolute"
},
"id": "reactionPoint",
"type": "randomSwitchStateMachine"
"type": "blendLinear"
}
],
"data": {
@ -4610,9 +4977,9 @@
{
"easingType": "easeInOutQuad",
"id": "idle",
"interpDuration": 10,
"interpTarget": 10,
"interpType": "snapshotPrev",
"interpDuration": 20,
"interpTarget": 20,
"interpType": "evaluateBoth",
"transitions": [
{
"state": "WALKFWD",
@ -5683,16 +6050,16 @@
"upLeftId": "lookUpLeft",
"upRightId": "lookUpRight"
},
"id": "lookAround",
"id": "lookAroundBlend",
"type": "blendDirectional"
}
],
"data": {
"alpha": 0,
"alphaVar": "additiveBlendAlpha",
"alphaVar": "lookBlendAlpha",
"blendType": "addAbsolute"
},
"id": "additiveBlend",
"id": "lookAround",
"type": "blendLinear"
}
],

View file

@ -3,8 +3,10 @@
"channels": [
{ "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.Q", "when": ["!Application.CameraSelfie"," !Application.CameraLookAt", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.Q", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Q", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.E", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" },
{ "comment" : "Mouse turn need to be small continuous increments",
@ -122,18 +124,18 @@
},
{ "from": { "makeAxis" : [
["Keyboard.Q"],
["Keyboard.E"]
]
["Keyboard.A"],
["Keyboard.D"]
]
},
"when": ["Application.CameraLookAt", "!Keyboard.Control"],
"to": "Actions.Yaw"
},
{ "from": { "makeAxis" : [
["Keyboard.E"],
["Keyboard.Q"]
]
["Keyboard.A"],
["Keyboard.D"]
]
},
"when": ["Application.CameraSelfie", "!Keyboard.Control"],
"to": "Actions.Yaw"
@ -198,7 +200,7 @@
"to": "Actions.DeltaPitch",
"filters":
[
{ "type": "scale", "scale": 0.3 }
{ "type": "scale", "scale": 0.2 }
]
},
@ -207,7 +209,7 @@
"to": "Actions.DeltaPitch",
"filters":
[
{ "type": "scale", "scale": 0.3 }
{ "type": "scale", "scale": 0.2 }
]
},
@ -215,10 +217,6 @@
{ "from": "Keyboard.S", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" },
{ "from": "Keyboard.S", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_FORWARD" },
{ "from": "Keyboard.W", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" },
{ "from": "Keyboard.A", "when": "Application.CameraLookAt", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.D", "when": "Application.CameraLookAt", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.A", "when": "Application.CameraSelfie", "to": "Actions.LATERAL_RIGHT" },
{ "from": "Keyboard.D", "when": "Application.CameraSelfie", "to": "Actions.LATERAL_LEFT" },
{ "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" },
{ "from": "Keyboard.C", "when": "!Keyboard.Control", "to": "Actions.VERTICAL_DOWN" },
{ "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },

View file

@ -1,6 +1,6 @@
import QtQuick 2.0
import QtWebEngine 1.5
import "../../controls" as Controls
import "../../../controls" as Controls
Controls.TabletWebView {
profile: WebEngineProfile { httpUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"}

View file

@ -0,0 +1,24 @@
//
// AppNapDisabler.h
// interface/src
//
// 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_AppNapDisabler_h
#define hifi_AppNapDisabler_h
#import <objc/objc-runtime.h>
class AppNapDisabler {
public:
AppNapDisabler();
~AppNapDisabler();
private:
id _activity;
};
#endif // hifi_AppNapDisabler_h

View file

@ -0,0 +1,28 @@
//
// AppNapDisabler.mm
// interface/src
//
// 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 <QtGlobal>
#ifdef Q_OS_MAC
#include "AppNapDisabler.h"
#import <AppKit/AppKit.h>
AppNapDisabler::AppNapDisabler() {
_activity = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityUserInitiated reason:@"Audio is in use"];
[_activity retain];
}
AppNapDisabler::~AppNapDisabler() {
[[NSProcessInfo processInfo] endActivity:_activity];
[_activity release];
}
#endif // Q_OS_MAC

View file

@ -106,8 +106,8 @@
#include <MessagesClient.h>
#include <hfm/ModelFormatRegistry.h>
#include <model-networking/ModelCacheScriptingInterface.h>
#include <material-networking/MaterialCacheScriptingInterface.h>
#include <material-networking/TextureCacheScriptingInterface.h>
#include <material-networking/MaterialCache.h>
#include <ModelEntityItem.h>
#include <NetworkAccessManager.h>
#include <NetworkingConstants.h>
@ -265,6 +265,12 @@ extern "C" {
#include "AndroidHelper.h"
#endif
#if defined(Q_OS_MAC)
// On Mac OS, disable App Nap to prevent audio glitches while running in the background
#include "AppNapDisabler.h"
static AppNapDisabler appNapDisabler; // disabled, while in scope
#endif
#include "graphics/RenderEventHandler.h"
Q_LOGGING_CATEGORY(trace_app_input_mouse, "trace.app.input.mouse")
@ -886,6 +892,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<TextureCache>();
DependencyManager::set<MaterialCache>();
DependencyManager::set<TextureCacheScriptingInterface>();
DependencyManager::set<MaterialCacheScriptingInterface>();
DependencyManager::set<FramebufferCache>();
DependencyManager::set<AnimationCache>();
DependencyManager::set<AnimationCacheScriptingInterface>();
@ -2906,6 +2913,7 @@ Application::~Application() {
DependencyManager::destroy<AnimationCacheScriptingInterface>();
DependencyManager::destroy<AnimationCache>();
DependencyManager::destroy<FramebufferCache>();
DependencyManager::destroy<MaterialCacheScriptingInterface>();
DependencyManager::destroy<MaterialCache>();
DependencyManager::destroy<TextureCacheScriptingInterface>();
DependencyManager::destroy<TextureCache>();
@ -3431,6 +3439,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
// Caches
surfaceContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCacheScriptingInterface>().data());
surfaceContext->setContextProperty("TextureCache", DependencyManager::get<TextureCacheScriptingInterface>().data());
surfaceContext->setContextProperty("MaterialCache", DependencyManager::get<MaterialCacheScriptingInterface>().data());
surfaceContext->setContextProperty("ModelCache", DependencyManager::get<ModelCacheScriptingInterface>().data());
surfaceContext->setContextProperty("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
@ -7458,6 +7467,7 @@ void Application::registerScriptEngineWithApplicationServices(const ScriptEngine
// Caches
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCacheScriptingInterface>().data());
scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCacheScriptingInterface>().data());
scriptEngine->registerGlobalObject("MaterialCache", DependencyManager::get<MaterialCacheScriptingInterface>().data());
scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCacheScriptingInterface>().data());
scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCacheScriptingInterface>().data());
@ -8491,6 +8501,8 @@ void Application::toggleLogDialog() {
Qt::WindowFlags flags = _logDialog->windowFlags() | Qt::Tool;
_logDialog->setWindowFlags(flags);
}
#else
Q_UNUSED(keepOnTop)
#endif
}

View file

@ -99,10 +99,16 @@ static const QString USER_RECENTER_MODEL_FORCE_STAND = QStringLiteral("ForceStan
static const QString USER_RECENTER_MODEL_AUTO = QStringLiteral("Auto");
static const QString USER_RECENTER_MODEL_DISABLE_HMD_LEAN = QStringLiteral("DisableHMDLean");
const QString HEAD_BLENDING_NAME = "lookAroundAlpha";
const QString HEAD_ALPHA_NAME = "additiveBlendAlpha";
const QString HEAD_BLEND_DIRECTIONAL_ALPHA_NAME = "lookAroundAlpha";
const QString HEAD_BLEND_LINEAR_ALPHA_NAME = "lookBlendAlpha";
const float HEAD_ALPHA_BLENDING = 1.0f;
const QString POINT_REACTION_NAME = "point";
const QString POINT_BLEND_DIRECTIONAL_ALPHA_NAME = "pointAroundAlpha";
const QString POINT_BLEND_LINEAR_ALPHA_NAME = "pointBlendAlpha";
const QString POINT_REF_JOINT_NAME = "RightShoulder";
const float POINT_ALPHA_BLENDING = 1.0f;
MyAvatar::SitStandModelType stringToUserRecenterModel(const QString& str) {
if (str == USER_RECENTER_MODEL_FORCE_SIT) {
return MyAvatar::ForceSit;
@ -948,13 +954,16 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
qCDebug(interfaceapp) << "MyAvatar::simulate headPosition is NaN";
headPosition = glm::vec3(0.0f);
}
head->setPosition(headPosition);
head->setScale(getModelScale());
head->simulate(deltaTime);
CameraMode mode = qApp->getCamera().getMode();
if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_FIRST_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
updateHeadLookAt(deltaTime);
if (!_pointAtActive || !_isPointTargetValid) {
updateHeadLookAt(deltaTime);
} else {
resetHeadLookAt();
}
} else if (_headLookAtActive){
resetHeadLookAt();
_headLookAtActive = false;
@ -2709,7 +2718,11 @@ void MyAvatar::updateMotors() {
if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) {
if (_characterController.getState() == CharacterController::State::Hover ||
_characterController.computeCollisionMask() == BULLET_COLLISION_MASK_COLLISIONLESS) {
motorRotation = getMyHead()->getHeadOrientation();
if (qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT || qApp->getCamera().getMode() == CAMERA_MODE_SELFIE) {
motorRotation = getLookAtRotation();
} else {
motorRotation = getMyHead()->getHeadOrientation();
}
} else {
// non-hovering = walking: follow camera twist about vertical but not lift
// we decompose camera's rotation and store the twist part in motorRotation
@ -3512,6 +3525,12 @@ void MyAvatar::updateOrientation(float deltaTime) {
float timeScale = deltaTime * FPS;
bool faceForward = false;
bool isMovingFwdBwd = getDriveKey(TRANSLATE_Z) != 0.0f;
bool isMovingSideways = getDriveKey(TRANSLATE_X) != 0.0f;
bool isCameraYawing = getDriveKey(DELTA_YAW) + getDriveKey(STEP_YAW) + getDriveKey(YAW) != 0.0f;
bool isRotatingWhileSeated = !isCameraYawing && isMovingSideways && _characterController.getSeated();
glm::quat previousOrientation = getWorldOrientation();
if (!computeLookAt) {
setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));
_lookAtCameraTarget = eyesPosition + getWorldOrientation() * Vectors::FRONT;
@ -3534,9 +3553,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
_lookAtPitch = _previousLookAtPitch;
}
}
bool isMovingFwdBwd = getDriveKey(TRANSLATE_Z) != 0.0f;
bool isMovingSideways = getDriveKey(TRANSLATE_X) != 0.0f;
bool isRotatingWhileSeated = isMovingSideways && _characterController.getSeated();
faceForward = isMovingFwdBwd || (isMovingSideways && !isRotatingWhileSeated);
// Blend the avatar orientation with the camera look at if moving forward.
if (faceForward || _shouldTurnToFaceCamera) {
@ -3636,9 +3653,13 @@ void MyAvatar::updateOrientation(float deltaTime) {
ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight);
cameraVector = (ajustedYawVector * _lookAtPitch) * Vectors::FRONT;
}
if (frontBackDot < triggerAngle) {
_shouldTurnToFaceCamera = true;
_firstPersonSteadyHeadTimer = 0.0f;
if (!isRotatingWhileSeated) {
if (frontBackDot < triggerAngle) {
_shouldTurnToFaceCamera = true;
_firstPersonSteadyHeadTimer = 0.0f;
}
} else {
setWorldOrientation(previousOrientation);
}
} else if (frontBackDot > glm::sin(glm::radians(reorientAngle))) {
_shouldTurnToFaceCamera = false;
@ -3650,8 +3671,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
glm::vec3 targetPoint = eyesPosition + glm::normalize(cameraVector);
const float LOOKAT_MIX_ALPHA = 0.25f;
if (getDriveKey(TRANSLATE_Y) == 0.0f) {
if (!isFlying() || !hasDriveInput()) {
// Approximate the head's look at vector to the camera look at vector with some delay.
float mixAlpha = LOOKAT_MIX_ALPHA * timeScale;
if (mixAlpha > 1.0f) {
@ -6137,6 +6157,10 @@ bool MyAvatar::beginReaction(QString reactionName) {
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) {
std::lock_guard<std::mutex> guard(_reactionLock);
_reactionEnabledRefCounts[reactionIndex]++;
if (reactionName == POINT_REACTION_NAME) {
_pointAtActive = true;
_isPointTargetValid = true;
}
return true;
}
return false;
@ -6146,13 +6170,18 @@ bool MyAvatar::endReaction(QString reactionName) {
int reactionIndex = beginEndReactionNameToIndex(reactionName);
if (reactionIndex >= 0 && reactionIndex < (int)NUM_AVATAR_BEGIN_END_REACTIONS) {
std::lock_guard<std::mutex> guard(_reactionLock);
bool wasReactionActive = true;
if (_reactionEnabledRefCounts[reactionIndex] > 0) {
_reactionEnabledRefCounts[reactionIndex]--;
return true;
wasReactionActive = true;
} else {
_reactionEnabledRefCounts[reactionIndex] = 0;
return false;
wasReactionActive = false;
}
if (reactionName == POINT_REACTION_NAME) {
_pointAtActive = _reactionEnabledRefCounts[reactionIndex] > 0;
}
return wasReactionActive;
}
return false;
}
@ -6163,10 +6192,13 @@ void MyAvatar::updateRigControllerParameters(Rig::ControllerParameters& params)
for (int i = 0; i < TRIGGER_REACTION_NAMES.size(); i++) {
params.reactionTriggers[i] = _reactionTriggers[i];
}
int pointReactionIndex = beginEndReactionNameToIndex("point");
for (int i = 0; i < BEGIN_END_REACTION_NAMES.size(); i++) {
// copy current state into params.
params.reactionEnabledFlags[i] = _reactionEnabledRefCounts[i] > 0;
if (params.reactionEnabledFlags[i] && i == pointReactionIndex) {
params.reactionEnabledFlags[i] = _isPointTargetValid;
}
}
for (int i = 0; i < TRIGGER_REACTION_NAMES.size(); i++) {
@ -6692,10 +6724,42 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera)
getHead()->setLookAtPosition(lookAtSpot);
}
glm::vec3 MyAvatar::aimToBlendValues(const glm::vec3& aimVector, const glm::quat& frameOrientation) {
// This method computes the values for the directional blending animation node
glm::vec3 uVector = glm::normalize(frameOrientation * Vectors::UNIT_X);
glm::vec3 vVector = glm::normalize(frameOrientation * Vectors::UNIT_Y);
glm::vec3 aimDirection;
if (glm::length(aimVector) > EPSILON) {
aimDirection = glm::normalize(aimVector);
} else {
// aim vector is zero
return glm::vec3();
}
float xDot = glm::dot(uVector, aimDirection);
float yDot = glm::dot(vVector, aimDirection);
// Make sure dot products are in range to avoid acosf returning NaN
xDot = glm::min(glm::max(xDot, -1.0f), 1.0f);
yDot = glm::min(glm::max(yDot, -1.0f), 1.0f);
float xAngle = acosf(xDot);
float yAngle = acosf(yDot);
// xBlend and yBlend are the values from -1.0 to 1.0 that set the directional blending.
// We compute them using the angles (0 to PI/2) => (1.0 to 0.0) and (PI/2 to PI) => (0.0 to -1.0)
float xBlend = -(xAngle - 0.5f * PI) / (0.5f * PI);
float yBlend = -(yAngle - 0.5f * PI) / (0.5f * PI);
glm::vec3 blendValues = glm::vec3(xBlend, yBlend, 0.0f);
return blendValues;
}
void MyAvatar::resetHeadLookAt() {
if (_skeletonModelLoaded) {
_skeletonModel->getRig().setDirectionalBlending(HEAD_BLENDING_NAME, glm::vec3(),
HEAD_ALPHA_NAME, HEAD_ALPHA_BLENDING);
_skeletonModel->getRig().setDirectionalBlending(HEAD_BLEND_DIRECTIONAL_ALPHA_NAME, glm::vec3(),
HEAD_BLEND_LINEAR_ALPHA_NAME, HEAD_ALPHA_BLENDING);
}
}
@ -6711,39 +6775,10 @@ void MyAvatar::resetLookAtRotation(const glm::vec3& avatarPosition, const glm::q
void MyAvatar::updateHeadLookAt(float deltaTime) {
if (_skeletonModelLoaded) {
glm::vec3 lookAtTarget = _scriptControlsHeadLookAt ? _lookAtScriptTarget : _lookAtCameraTarget;
glm::vec3 avatarXVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_X);
glm::vec3 avatarYVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_Y);
glm::vec3 avatarZVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_Z);
glm::vec3 headToTargetVector = lookAtTarget - getDefaultEyePosition();
if (glm::length(headToTargetVector) > EPSILON) {
headToTargetVector = glm::normalize(headToTargetVector);
} else {
// The target point is the avatar head
return;
}
float xDot = glm::dot(avatarXVector, headToTargetVector);
float yDot = glm::dot(avatarYVector, headToTargetVector);
float zDot = glm::dot(avatarZVector, headToTargetVector);
// Force the head to look at one of the sides when the look at point is behind the avatar
if (zDot > 0.0f && xDot != 0.0f) {
//xDot /= fabsf(xDot);
}
// Make sure dot products are in range to avoid acosf returning NaN
xDot = glm::min(glm::max(xDot, -1.0f), 1.0f);
yDot = glm::min(glm::max(yDot, -1.0f), 1.0f);
float xAngle = acosf(xDot);
float yAngle = acosf(yDot);
// xBlend and yBlend are the values from -1.0 to 1.0 that set the directional blending.
// We compute them using the angles (0 to PI/2) => (1.0 to 0.0) and (PI/2 to PI) => (0.0 to -1.0)
float xBlend = -(xAngle - 0.5f * PI) / (0.5f * PI);
float yBlend = -(yAngle - 0.5f * PI) / (0.5f * PI);
glm::vec3 lookAtBlend = glm::vec3(xBlend, yBlend, 0.0f);
_skeletonModel->getRig().setDirectionalBlending(HEAD_BLENDING_NAME, lookAtBlend,
HEAD_ALPHA_NAME, HEAD_ALPHA_BLENDING);
glm::vec3 aimVector = lookAtTarget - getDefaultEyePosition();
glm::vec3 lookAtBlend = MyAvatar::aimToBlendValues(aimVector, getWorldOrientation());
_skeletonModel->getRig().setDirectionalBlending(HEAD_BLEND_DIRECTIONAL_ALPHA_NAME, lookAtBlend,
HEAD_BLEND_LINEAR_ALPHA_NAME, HEAD_ALPHA_BLENDING);
if (_scriptControlsHeadLookAt) {
_scriptHeadControlTimer += deltaTime;
@ -6774,3 +6809,30 @@ glm::vec3 MyAvatar::getLookAtPivotPoint() {
return yAxisEyePosition;
}
bool MyAvatar::setPointAt(const glm::vec3& pointAtTarget) {
if (QThread::currentThread() != thread()) {
bool result = false;
BLOCKING_INVOKE_METHOD(this, "setPointAt", Q_RETURN_ARG(bool, result),
Q_ARG(const glm::vec3&, pointAtTarget));
return result;
}
if (_skeletonModelLoaded && _pointAtActive) {
glm::vec3 aimVector = pointAtTarget - getJointPosition(POINT_REF_JOINT_NAME);
_isPointTargetValid = glm::dot(aimVector, getWorldOrientation() * Vectors::FRONT) > 0.0f;
if (_isPointTargetValid) {
glm::vec3 pointAtBlend = MyAvatar::aimToBlendValues(aimVector, getWorldOrientation());
_skeletonModel->getRig().setDirectionalBlending(POINT_BLEND_DIRECTIONAL_ALPHA_NAME, pointAtBlend,
POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING);
}
return _isPointTargetValid;
}
return false;
}
void MyAvatar::resetPointAt() {
if (_skeletonModelLoaded) {
_skeletonModel->getRig().setDirectionalBlending(POINT_BLEND_DIRECTIONAL_ALPHA_NAME, glm::vec3(),
POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING);
}
}

View file

@ -1765,6 +1765,16 @@ public:
*/
Q_INVOKABLE glm::vec3 getHeadLookAt() { return _lookAtCameraTarget; }
/**jsdoc
* Aims the pointing directional blending towards the provided target point.
* The "point" reaction should be triggered before using this method.
* <code>MyAvatar.beginReaction("point")</code>
* Returns <code>true</code> if the target point lays in front of the avatar.
* @function MyAvatar.setPointAt
* @param {Vec3} pointAtTarget - The target point in world coordinates.
*/
Q_INVOKABLE bool setPointAt(const glm::vec3& pointAtTarget);
glm::quat getLookAtRotation() { return _lookAtYaw * _lookAtPitch; }
/**jsdoc
@ -2655,6 +2665,8 @@ private:
bool _scriptControlsHeadLookAt { false };
float _scriptHeadControlTimer { 0.0f };
float _firstPersonSteadyHeadTimer { 0.0f };
bool _pointAtActive { false };
bool _isPointTargetValid { true };
Setting::Handle<float> _realWorldFieldOfView;
Setting::Handle<bool> _useAdvancedMovementControls;
@ -2683,6 +2695,8 @@ private:
void updateHeadLookAt(float deltaTime);
void resetHeadLookAt();
void resetLookAtRotation(const glm::vec3& avatarPosition, const glm::quat& avatarOrientation);
void resetPointAt();
static glm::vec3 aimToBlendValues(const glm::vec3& aimVector, const glm::quat& frameOrientation);
// Avatar Preferences
QUrl _fullAvatarURLFromPreferences;

View file

@ -282,9 +282,9 @@ void AudioDeviceList::onDevicesChanged(const QList<HifiAudioDeviceInfo>& devices
if (deviceInfo.isDefault()) {
if (deviceInfo.getMode() == QAudio::AudioInput) {
device.display = "Default microphone (recommended)";
device.display = "Computer's default microphone (recommended)";
} else {
device.display = "Default audio (recommended)";
device.display = "Computer's default audio (recommended)";
}
} else {
device.display = device.info.deviceName()

View file

@ -359,7 +359,7 @@ protected:
A,
B
};
NetworkAnimState() : clipNodeEnum(NetworkAnimState::None) {}
NetworkAnimState() : clipNodeEnum(NetworkAnimState::None), fps(30.0f), loop(false), firstFrame(0.0f), lastFrame(0.0f), blendTime(FLT_MAX) {}
NetworkAnimState(ClipNodeEnum clipNodeEnumIn, const QString& urlIn, float fpsIn, bool loopIn, float firstFrameIn, float lastFrameIn) :
clipNodeEnum(clipNodeEnumIn), url(urlIn), fps(fpsIn), loop(loopIn), firstFrame(firstFrameIn), lastFrame(lastFrameIn) {}

View file

@ -60,25 +60,13 @@ const int AudioClient::MIN_BUFFER_FRAMES = 1;
const int AudioClient::MAX_BUFFER_FRAMES = 20;
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100;
#if defined(Q_OS_ANDROID)
static const int CHECK_INPUT_READS_MSECS = 2000;
static const int MIN_READS_TO_CONSIDER_INPUT_ALIVE = 10;
#endif
static const auto DEFAULT_POSITION_GETTER = []{ return Vectors::ZERO; };
static const auto DEFAULT_ORIENTATION_GETTER = [] { return Quaternions::IDENTITY; };
static const int DEFAULT_BUFFER_FRAMES = 1;
// OUTPUT_CHANNEL_COUNT is audio pipeline output format, which is always 2 channel.
// _outputFormat.channelCount() is device output format, which may be 1 or multichannel.
static const int OUTPUT_CHANNEL_COUNT = 2;
static const bool DEFAULT_STARVE_DETECTION_ENABLED = true;
static const int STARVE_DETECTION_THRESHOLD = 3;
static const int STARVE_DETECTION_PERIOD = 10 * 1000; // 10 Seconds
const AudioClient::AudioPositionGetter AudioClient::DEFAULT_POSITION_GETTER = []{ return Vectors::ZERO; };
const AudioClient::AudioOrientationGetter AudioClient::DEFAULT_ORIENTATION_GETTER = [] { return Quaternions::IDENTITY; };
Setting::Handle<bool> dynamicJitterBufferEnabled("dynamicJitterBuffersEnabled",
InboundAudioStream::DEFAULT_DYNAMIC_JITTER_BUFFER_ENABLED);
@ -272,55 +260,7 @@ static inline float convertToFloat(int16_t sample) {
return (float)sample * (1 / 32768.0f);
}
AudioClient::AudioClient() :
AbstractAudioInterface(),
_gate(this),
_audioInput(NULL),
_dummyAudioInput(NULL),
_desiredInputFormat(),
_inputFormat(),
_numInputCallbackBytes(0),
_audioOutput(NULL),
_desiredOutputFormat(),
_outputFormat(),
_outputFrameSize(0),
_numOutputCallbackBytes(0),
_loopbackAudioOutput(NULL),
_loopbackOutputDevice(NULL),
_inputRingBuffer(0),
_localInjectorsStream(0, 1),
_receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES),
_isStereoInput(false),
_outputStarveDetectionStartTimeMsec(0),
_outputStarveDetectionCount(0),
_outputBufferSizeFrames("audioOutputBufferFrames", DEFAULT_BUFFER_FRAMES),
_sessionOutputBufferSizeFrames(_outputBufferSizeFrames.get()),
_outputStarveDetectionEnabled("audioOutputStarveDetectionEnabled", DEFAULT_STARVE_DETECTION_ENABLED),
_lastRawInputLoudness(0.0f),
_lastSmoothedRawInputLoudness(0.0f),
_lastInputLoudness(0.0f),
_timeSinceLastClip(-1.0f),
_muted(false),
_shouldEchoLocally(false),
_shouldEchoToServer(false),
_isNoiseGateEnabled(true),
_isAECEnabled(true),
_reverb(false),
_reverbOptions(&_scriptReverbOptions),
_inputToNetworkResampler(NULL),
_networkToOutputResampler(NULL),
_localToOutputResampler(NULL),
_loopbackResampler(NULL),
_audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT),
_outgoingAvatarAudioSequenceNumber(0),
_audioOutputIODevice(_localInjectorsStream, _receivedAudioStream, this),
_stats(&_receivedAudioStream),
_positionGetter(DEFAULT_POSITION_GETTER),
#if defined(Q_OS_ANDROID)
_checkInputTimer(this),
_isHeadsetPluggedIn(false),
#endif
_orientationGetter(DEFAULT_ORIENTATION_GETTER) {
AudioClient::AudioClient() {
// avoid putting a lock in the device callback
assert(_localSamplesAvailable.is_lock_free());
@ -554,7 +494,9 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic));
//Use the received manufacturer id to get the device's real name
waveInGetDevCaps(wic.wMid, &wic, sizeof(wic));
#if !defined(NDEBUG)
qCDebug(audioclient) << "input device:" << wic.szPname;
#endif
deviceName = wic.szPname;
} else {
WAVEOUTCAPS woc;
@ -562,7 +504,9 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(woc));
//Use the received manufacturer id to get the device's real name
waveOutGetDevCaps(woc.wMid, &woc, sizeof(woc));
#if !defined(NDEBUG)
qCDebug(audioclient) << "output device:" << woc.szPname;
#endif
deviceName = woc.szPname;
}
} else {
@ -592,10 +536,10 @@ HifiAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) {
break;
}
}
#if !defined(NDEBUG)
qCDebug(audioclient) << "defaultAudioDeviceForMode mode: " << (mode == QAudio::AudioOutput ? "Output" : "Input")
<< " [" << deviceName << "] [" << foundDevice.deviceName() << "]";
#endif
return foundDevice;
#endif

View file

@ -80,6 +80,9 @@ class QIODevice;
class Transform;
class NLPacket;
#define DEFAULT_STARVE_DETECTION_ENABLED true
#define DEFAULT_BUFFER_FRAMES 1
class AudioClient : public AbstractAudioInterface, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
@ -292,6 +295,16 @@ protected:
virtual void customDeleter() override;
private:
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES{ 100 };
// OUTPUT_CHANNEL_COUNT is audio pipeline output format, which is always 2 channel.
// _outputFormat.channelCount() is device output format, which may be 1 or multichannel.
static const int OUTPUT_CHANNEL_COUNT{ 2 };
static const int STARVE_DETECTION_THRESHOLD{ 3 };
static const int STARVE_DETECTION_PERIOD{ 10 * 1000 }; // 10 Seconds
static const AudioPositionGetter DEFAULT_POSITION_GETTER;
static const AudioOrientationGetter DEFAULT_ORIENTATION_GETTER;
friend class CheckDevicesThread;
friend class LocalInjectorsThread;
@ -307,9 +320,9 @@ private:
float gainForSource(float distance, float volume);
#ifdef Q_OS_ANDROID
QTimer _checkInputTimer;
QTimer _checkInputTimer{ this };
long _inputReadsSinceLastCheck = 0l;
bool _isHeadsetPluggedIn;
bool _isHeadsetPluggedIn { false };
#endif
class Gate {
@ -336,68 +349,68 @@ private:
bool _isSimulatingJitter{ false };
};
Gate _gate;
Gate _gate{ this };
Mutex _injectorsMutex;
QAudioInput* _audioInput;
QTimer* _dummyAudioInput;
QAudioInput* _audioInput{ nullptr };
QTimer* _dummyAudioInput{ nullptr };
QAudioFormat _desiredInputFormat;
QAudioFormat _inputFormat;
QIODevice* _inputDevice;
int _numInputCallbackBytes;
QAudioOutput* _audioOutput;
QIODevice* _inputDevice{ nullptr };
int _numInputCallbackBytes{ 0 };
QAudioOutput* _audioOutput{ nullptr };
std::atomic<bool> _audioOutputInitialized { false };
QAudioFormat _desiredOutputFormat;
QAudioFormat _outputFormat;
int _outputFrameSize;
int _numOutputCallbackBytes;
QAudioOutput* _loopbackAudioOutput;
QIODevice* _loopbackOutputDevice;
AudioRingBuffer _inputRingBuffer;
LocalInjectorsStream _localInjectorsStream;
int _outputFrameSize{ 0 };
int _numOutputCallbackBytes{ 0 };
QAudioOutput* _loopbackAudioOutput{ nullptr };
QIODevice* _loopbackOutputDevice{ nullptr };
AudioRingBuffer _inputRingBuffer{ 0 };
LocalInjectorsStream _localInjectorsStream{ 0 , 1 };
// In order to use _localInjectorsStream as a lock-free pipe,
// use it with a single producer/consumer, and track available samples and injectors
std::atomic<int> _localSamplesAvailable { 0 };
std::atomic<bool> _localInjectorsAvailable { false };
MixedProcessedAudioStream _receivedAudioStream;
bool _isStereoInput;
MixedProcessedAudioStream _receivedAudioStream{ RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES };
bool _isStereoInput{ false };
std::atomic<bool> _enablePeakValues { false };
quint64 _outputStarveDetectionStartTimeMsec;
int _outputStarveDetectionCount;
quint64 _outputStarveDetectionStartTimeMsec{ 0 };
int _outputStarveDetectionCount { 0 };
Setting::Handle<int> _outputBufferSizeFrames;
int _sessionOutputBufferSizeFrames;
Setting::Handle<bool> _outputStarveDetectionEnabled;
Setting::Handle<int> _outputBufferSizeFrames{"audioOutputBufferFrames", DEFAULT_BUFFER_FRAMES};
int _sessionOutputBufferSizeFrames{ _outputBufferSizeFrames.get() };
Setting::Handle<bool> _outputStarveDetectionEnabled{ "audioOutputStarveDetectionEnabled", DEFAULT_STARVE_DETECTION_ENABLED};
StDev _stdev;
QElapsedTimer _timeSinceLastReceived;
float _lastRawInputLoudness; // before mute/gate
float _lastSmoothedRawInputLoudness;
float _lastInputLoudness; // after mute/gate
float _timeSinceLastClip;
float _lastRawInputLoudness{ 0.0f }; // before mute/gate
float _lastSmoothedRawInputLoudness{ 0.0f };
float _lastInputLoudness{ 0.0f }; // after mute/gate
float _timeSinceLastClip{ -1.0f };
int _totalInputAudioSamples;
bool _muted;
bool _shouldEchoLocally;
bool _shouldEchoToServer;
bool _isNoiseGateEnabled;
bool _muted{ false };
bool _shouldEchoLocally{ false };
bool _shouldEchoToServer{ false };
bool _isNoiseGateEnabled{ true };
bool _warnWhenMuted;
bool _isAECEnabled;
bool _isAECEnabled{ true };
bool _reverb;
bool _reverb{ false };
AudioEffectOptions _scriptReverbOptions;
AudioEffectOptions _zoneReverbOptions;
AudioEffectOptions* _reverbOptions;
AudioEffectOptions* _reverbOptions{ &_scriptReverbOptions };
AudioReverb _sourceReverb { AudioConstants::SAMPLE_RATE };
AudioReverb _listenerReverb { AudioConstants::SAMPLE_RATE };
AudioReverb _localReverb { AudioConstants::SAMPLE_RATE };
// possible streams needed for resample
AudioSRC* _inputToNetworkResampler;
AudioSRC* _networkToOutputResampler;
AudioSRC* _localToOutputResampler;
AudioSRC* _loopbackResampler;
AudioSRC* _inputToNetworkResampler{ nullptr };
AudioSRC* _networkToOutputResampler{ nullptr };
AudioSRC* _localToOutputResampler{ nullptr };
AudioSRC* _loopbackResampler{ nullptr };
// for network audio (used by network audio thread)
int16_t _networkScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
@ -416,7 +429,7 @@ private:
int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC];
float* _localOutputMixBuffer { NULL };
Mutex _localAudioMutex;
AudioLimiter _audioLimiter;
AudioLimiter _audioLimiter{ AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT };
// Adds Reverb
void configureReverb();
@ -445,17 +458,17 @@ private:
int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const;
int calculateNumberOfFrameSamples(int numBytes) const;
quint16 _outgoingAvatarAudioSequenceNumber;
quint16 _outgoingAvatarAudioSequenceNumber{ 0 };
AudioOutputIODevice _audioOutputIODevice;
AudioOutputIODevice _audioOutputIODevice{ _localInjectorsStream, _receivedAudioStream, this };
AudioIOStats _stats;
AudioIOStats _stats{ &_receivedAudioStream };
AudioGate* _audioGate { nullptr };
bool _audioGateOpen { true };
AudioPositionGetter _positionGetter;
AudioOrientationGetter _orientationGetter;
AudioPositionGetter _positionGetter{ DEFAULT_POSITION_GETTER };
AudioOrientationGetter _orientationGetter{ DEFAULT_ORIENTATION_GETTER };
glm::vec3 avatarBoundingBoxCorner;
glm::vec3 avatarBoundingBoxScale;

View file

@ -133,7 +133,21 @@ void Avatar::setShowNamesAboveHeads(bool show) {
showNamesAboveHeads = show;
}
static const char* avatarTransitStatusToStringMap[] = {
"IDLE",
"STARTED",
"PRE_TRANSIT",
"START_TRANSIT",
"TRANSITING",
"END_TRANSIT",
"POST_TRANSIT",
"ENDED",
"ABORT_TRANSIT"
};
AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) {
AvatarTransit::Status previousStatus = _status;
float oneFrameDistance = _isActive ? glm::length(avatarPosition - _endPosition) : glm::length(avatarPosition - _lastPosition);
if (oneFrameDistance > (config._minTriggerDistance * _scale)) {
if (oneFrameDistance < (config._maxTriggerDistance * _scale)) {
@ -150,6 +164,10 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av
reset();
_status = Status::ENDED;
}
if (previousStatus != _status) {
qDebug(avatars_renderer) << "AvatarTransit " << avatarTransitStatusToStringMap[(int)previousStatus] << "->" << avatarTransitStatusToStringMap[_status];
}
return _status;
}

View file

@ -108,15 +108,6 @@ protected:
virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; }
virtual void setRenderLayer(RenderLayer value) { _renderLayer = value; }
virtual void setPrimitiveMode(PrimitiveMode value) { _primitiveMode = value; }
template <typename F, typename T>
T withReadLockResult(const std::function<T()>& f) {
T result;
withReadLock([&] {
result = f();
});
return result;
}
signals:
void requestRenderUpdate();

View file

@ -21,8 +21,10 @@ const uint32_t MAX_RANGE_QUERY_DEPTH = 1;
static bool timeElapsed = true;
#else
const uint32_t MAX_RANGE_QUERY_DEPTH = 10000;
#if !defined(USE_GLES)
static bool timeElapsed = false;
#endif
#endif
#if defined(USE_GLES)
static bool hasTimerExtension() {

View file

@ -17,34 +17,6 @@
namespace gpu { namespace gles {
// returns the FOV from the projection matrix
static inline vec4 extractFov( const glm::mat4& m) {
static const std::array<vec4, 4> CLIPS{ {
{ 1, 0, 0, 1 },
{ -1, 0, 0, 1 },
{ 0, 1, 0, 1 },
{ 0, -1, 0, 1 }
} };
glm::mat4 mt = glm::transpose(m);
vec4 v, result;
// Left
v = mt * CLIPS[0];
result.x = -atanf(v.z / v.x);
// Right
v = mt * CLIPS[1];
result.y = atanf(v.z / v.x);
// Down
v = mt * CLIPS[2];
result.z = -atanf(v.z / v.y);
// Up
v = mt * CLIPS[3];
result.w = atanf(v.z / v.y);
return result;
}
class GLESFramebuffer : public gl::GLFramebuffer {
using Parent = gl::GLFramebuffer;
static GLuint allocate() {

View file

@ -50,6 +50,8 @@ namespace scriptable {
* @property {string} emissiveMap
* @property {string} albedoMap
* @property {string} opacityMap
* @property {string} opacityMapMode
* @property {number|string} opacityCutoff
* @property {string} metallicMap
* @property {string} specularMap
* @property {string} roughnessMap
@ -84,6 +86,8 @@ namespace scriptable {
QString emissiveMap;
QString albedoMap;
QString opacityMap;
QString opacityMapMode;
float opacityCutoff;
QString metallicMap;
QString specularMap;
QString roughnessMap;
@ -94,7 +98,6 @@ namespace scriptable {
QString lightMap;
QString scatteringMap;
std::array<glm::mat4, graphics::Material::NUM_TEXCOORD_TRANSFORMS> texCoordTransforms;
bool defaultFallthrough;
std::unordered_map<uint, bool> propertyFallthroughs; // not actually exposed to script

View file

@ -420,6 +420,18 @@ namespace scriptable {
obj.setProperty("opacityMap", material.opacityMap);
}
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT | graphics::MaterialKey::OPACITY_MASK_MAP_BIT)) {
obj.setProperty("opacityMapMode", FALLTHROUGH);
} else if (material.key.getOpacityMapMode() != graphics::Material::DEFAULT_OPACITY_MAP_MODE) {
obj.setProperty("opacityMapMode", material.opacityMapMode);
}
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT)) {
obj.setProperty("opacityCutoff", FALLTHROUGH);
} else if (material.key.isOpacityCutoff()) {
obj.setProperty("opacityCutoff", material.opacityCutoff);
}
if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT)) {
obj.setProperty("occlusionMap", FALLTHROUGH);
} else if (!material.occlusionMap.isEmpty()) {

View file

@ -26,6 +26,7 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const
roughness = material.roughness;
metallic = material.metallic;
scattering = material.scattering;
opacityCutoff = material.opacityCutoff;
unlit = material.unlit;
emissive = material.emissive;
albedo = material.albedo;
@ -41,6 +42,8 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const
occlusionMap = material.occlusionMap;
lightMap = material.lightMap;
scatteringMap = material.scatteringMap;
opacityMapMode = material.opacityMapMode;
defaultFallthrough = material.defaultFallthrough;
propertyFallthroughs = material.propertyFallthroughs;
@ -55,9 +58,12 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint
name = material->getName().c_str();
model = material->getModel().c_str();
opacity = material->getOpacity();
opacityMapMode = QString(graphics::MaterialKey::getOpacityMapModeName(material->getOpacityMapMode()).c_str());
roughness = material->getRoughness();
metallic = material->getMetallic();
scattering = material->getScattering();
opacityCutoff = material->getOpacityCutoff();
unlit = material->isUnlit();
emissive = material->getEmissive();
albedo = material->getAlbedo();

View file

@ -14,6 +14,8 @@
#include <Transform.h>
#include "GraphicsLogging.h"
using namespace graphics;
using namespace gpu;
@ -22,7 +24,26 @@ const float Material::DEFAULT_OPACITY { 1.0f };
const float Material::DEFAULT_ALBEDO { 0.5f };
const float Material::DEFAULT_METALLIC { 0.0f };
const float Material::DEFAULT_ROUGHNESS { 1.0f };
const float Material::DEFAULT_SCATTERING { 0.0f };
const float Material::DEFAULT_SCATTERING{ 0.0f };
const MaterialKey::OpacityMapMode Material::DEFAULT_OPACITY_MAP_MODE{ MaterialKey::OPACITY_MAP_OPAQUE };
const float Material::DEFAULT_OPACITY_CUTOFF { 0.5f };
std::string MaterialKey::getOpacityMapModeName(OpacityMapMode mode) {
const std::string names[3] = { "OPACITY_MAP_OPAQUE", "OPACITY_MAP_MASK", "OPACITY_MAP_BLEND" };
return names[mode];
}
bool MaterialKey::getOpacityMapModeFromName(const std::string& modeName, MaterialKey::OpacityMapMode& mode) {
for (int i = OPACITY_MAP_OPAQUE; i <= OPACITY_MAP_BLEND; i++) {
mode = (MaterialKey::OpacityMapMode) i;
if (modeName == getOpacityMapModeName(mode)) {
return true;
}
}
return false;
}
Material::Material() {
for (int i = 0; i < NUM_TOTAL_FLAGS; i++) {
@ -40,6 +61,7 @@ Material::Material(const Material& material) :
_roughness(material._roughness),
_metallic(material._metallic),
_scattering(material._scattering),
_opacityCutoff(material._opacityCutoff),
_texcoordTransforms(material._texcoordTransforms),
_lightmapParams(material._lightmapParams),
_materialParams(material._materialParams),
@ -50,7 +72,7 @@ Material::Material(const Material& material) :
}
Material& Material::operator=(const Material& material) {
QMutexLocker locker(&_textureMapsMutex);
std::lock_guard<std::recursive_mutex> locker(_textureMapsMutex);
_name = material._name;
_model = material._model;
@ -61,6 +83,7 @@ Material& Material::operator=(const Material& material) {
_roughness = material._roughness;
_metallic = material._metallic;
_scattering = material._scattering;
_opacityCutoff = material._opacityCutoff;
_texcoordTransforms = material._texcoordTransforms;
_lightmapParams = material._lightmapParams;
_materialParams = material._materialParams;
@ -109,8 +132,22 @@ void Material::setScattering(float scattering) {
_scattering = scattering;
}
void Material::setOpacityCutoff(float opacityCutoff) {
opacityCutoff = glm::clamp(opacityCutoff, 0.0f, 1.0f);
_key.setOpacityCutoff(opacityCutoff != DEFAULT_OPACITY_CUTOFF);
_opacityCutoff = opacityCutoff;
}
void Material::setOpacityMapMode(MaterialKey::OpacityMapMode opacityMapMode) {
_key.setOpacityMapMode(opacityMapMode);
}
MaterialKey::OpacityMapMode Material::getOpacityMapMode() const {
return _key.getOpacityMapMode();
}
void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) {
QMutexLocker locker(&_textureMapsMutex);
std::lock_guard<std::recursive_mutex> locker(_textureMapsMutex);
if (textureMap) {
_key.setMapChannel(channel, true);
@ -139,7 +176,14 @@ void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textur
}
void Material::resetOpacityMap() const {
bool Material::resetOpacityMap() const {
// If OpacityMapMode explicit then nothing need to change here.
if (_key.isOpacityMapMode()) {
return false;
}
// Else, the legacy behavior is to interpret the albedo texture assigned to tune the opacity map mode value
auto previous = _key.getOpacityMapMode();
// Clear the previous flags
_key.setOpacityMaskMap(false);
_key.setTranslucentMap(false);
@ -163,10 +207,16 @@ void Material::resetOpacityMap() const {
}
}
}
auto newious = _key.getOpacityMapMode();
if (previous != newious) {
//opacity change detected for this material
return true;
}
return false;
}
const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
QMutexLocker locker(&_textureMapsMutex);
std::lock_guard<std::recursive_mutex> locker(_textureMapsMutex);
auto result = _textureMaps.find(channel);
if (result != _textureMaps.end()) {

View file

@ -11,8 +11,7 @@
#ifndef hifi_model_Material_h
#define hifi_model_Material_h
#include <QMutex>
#include <mutex>
#include <bitset>
#include <map>
#include <unordered_map>
@ -44,6 +43,8 @@ public:
OPACITY_VAL_BIT,
OPACITY_MASK_MAP_BIT, // Opacity Map and Opacity MASK map are mutually exclusive
OPACITY_TRANSLUCENT_MAP_BIT,
OPACITY_MAP_MODE_BIT, // Opacity map mode bit is set if the value has set explicitely and not deduced from the textures assigned
OPACITY_CUTOFF_VAL_BIT,
SCATTERING_VAL_BIT,
// THe map bits must be in the same sequence as the enum names for the map channels
@ -73,6 +74,15 @@ public:
NUM_MAP_CHANNELS,
};
enum OpacityMapMode {
OPACITY_MAP_OPAQUE = 0,
OPACITY_MAP_MASK,
OPACITY_MAP_BLEND,
};
static std::string getOpacityMapModeName(OpacityMapMode mode);
// find the enum value from a string, return true if match found
static bool getOpacityMapModeFromName(const std::string& modeName, OpacityMapMode& mode);
// The signature is the Flags
Flags _flags;
@ -94,6 +104,27 @@ public:
Builder& withGlossy() { _flags.set(GLOSSY_VAL_BIT); return (*this); }
Builder& withTranslucentFactor() { _flags.set(OPACITY_VAL_BIT); return (*this); }
Builder& withTranslucentMap() { _flags.set(OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
Builder& withMaskMap() { _flags.set(OPACITY_MASK_MAP_BIT); return (*this); }
Builder& withOpacityMapMode(OpacityMapMode mode) {
switch (mode) {
case OPACITY_MAP_OPAQUE:
_flags.reset(OPACITY_TRANSLUCENT_MAP_BIT);
_flags.reset(OPACITY_MASK_MAP_BIT);
break;
case OPACITY_MAP_MASK:
_flags.reset(OPACITY_TRANSLUCENT_MAP_BIT);
_flags.set(OPACITY_MASK_MAP_BIT);
break;
case OPACITY_MAP_BLEND:
_flags.set(OPACITY_TRANSLUCENT_MAP_BIT);
_flags.reset(OPACITY_MASK_MAP_BIT);
break;
};
_flags.set(OPACITY_MAP_MODE_BIT); // Intentionally set the mode!
return (*this);
}
Builder& withOpacityCutoff() { _flags.set(OPACITY_CUTOFF_VAL_BIT); return (*this); }
Builder& withScattering() { _flags.set(SCATTERING_VAL_BIT); return (*this); }
@ -102,9 +133,6 @@ public:
Builder& withMetallicMap() { _flags.set(METALLIC_MAP_BIT); return (*this); }
Builder& withRoughnessMap() { _flags.set(ROUGHNESS_MAP_BIT); return (*this); }
Builder& withTranslucentMap() { _flags.set(OPACITY_TRANSLUCENT_MAP_BIT); return (*this); }
Builder& withMaskMap() { _flags.set(OPACITY_MASK_MAP_BIT); return (*this); }
Builder& withNormalMap() { _flags.set(NORMAL_MAP_BIT); return (*this); }
Builder& withOcclusionMap() { _flags.set(OCCLUSION_MAP_BIT); return (*this); }
Builder& withLightMap() { _flags.set(LIGHT_MAP_BIT); return (*this); }
@ -151,6 +179,9 @@ public:
void setOpacityMaskMap(bool value) { _flags.set(OPACITY_MASK_MAP_BIT, value); }
bool isOpacityMaskMap() const { return _flags[OPACITY_MASK_MAP_BIT]; }
void setOpacityCutoff(bool value) { _flags.set(OPACITY_CUTOFF_VAL_BIT, value); }
bool isOpacityCutoff() const { return _flags[OPACITY_CUTOFF_VAL_BIT]; }
void setNormalMap(bool value) { _flags.set(NORMAL_MAP_BIT, value); }
bool isNormalMap() const { return _flags[NORMAL_MAP_BIT]; }
@ -171,6 +202,26 @@ public:
// Translucency and Opacity Heuristics are combining several flags:
void setOpacityMapMode(OpacityMapMode mode) {
switch (mode) {
case OPACITY_MAP_OPAQUE:
_flags.reset(OPACITY_TRANSLUCENT_MAP_BIT);
_flags.reset(OPACITY_MASK_MAP_BIT);
break;
case OPACITY_MAP_MASK:
_flags.reset(OPACITY_TRANSLUCENT_MAP_BIT);
_flags.set(OPACITY_MASK_MAP_BIT);
break;
case OPACITY_MAP_BLEND:
_flags.set(OPACITY_TRANSLUCENT_MAP_BIT);
_flags.reset(OPACITY_MASK_MAP_BIT);
break;
};
_flags.set(OPACITY_MAP_MODE_BIT); // Intentionally set the mode!
}
bool isOpacityMapMode() const { return _flags[OPACITY_MAP_MODE_BIT]; }
OpacityMapMode getOpacityMapMode() const { return (isOpacityMaskMap() ? OPACITY_MAP_MASK : (isTranslucentMap() ? OPACITY_MAP_BLEND : OPACITY_MAP_OPAQUE)); }
bool isTranslucent() const { return isTranslucentFactor() || isTranslucentMap(); }
bool isOpaque() const { return !isTranslucent(); }
bool isSurfaceOpaque() const { return isOpaque() && !isOpacityMaskMap(); }
@ -229,6 +280,12 @@ public:
Builder& withoutMaskMap() { _value.reset(MaterialKey::OPACITY_MASK_MAP_BIT); _mask.set(MaterialKey::OPACITY_MASK_MAP_BIT); return (*this); }
Builder& withMaskMap() { _value.set(MaterialKey::OPACITY_MASK_MAP_BIT); _mask.set(MaterialKey::OPACITY_MASK_MAP_BIT); return (*this); }
Builder& withoutOpacityMapMode() { _value.reset(MaterialKey::OPACITY_MAP_MODE_BIT); _mask.set(MaterialKey::OPACITY_MAP_MODE_BIT); return (*this); }
Builder& withOpacityMapMode() { _value.set(MaterialKey::OPACITY_MAP_MODE_BIT); _mask.set(MaterialKey::OPACITY_MAP_MODE_BIT); return (*this); }
Builder& withoutOpacityCutoff() { _value.reset(MaterialKey::OPACITY_CUTOFF_VAL_BIT); _mask.set(MaterialKey::OPACITY_CUTOFF_VAL_BIT); return (*this); }
Builder& withOpacityCutoff() { _value.set(MaterialKey::OPACITY_CUTOFF_VAL_BIT); _mask.set(MaterialKey::OPACITY_CUTOFF_VAL_BIT); return (*this); }
Builder& withoutNormalMap() { _value.reset(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); }
Builder& withNormalMap() { _value.set(MaterialKey::NORMAL_MAP_BIT); _mask.set(MaterialKey::NORMAL_MAP_BIT); return (*this); }
@ -283,6 +340,14 @@ public:
void setOpacity(float opacity);
float getOpacity() const { return _opacity; }
static const MaterialKey::OpacityMapMode DEFAULT_OPACITY_MAP_MODE;
void setOpacityMapMode(MaterialKey::OpacityMapMode opacityMapMode);
MaterialKey::OpacityMapMode getOpacityMapMode() const;
static const float DEFAULT_OPACITY_CUTOFF;
void setOpacityCutoff(float opacityCutoff);
float getOpacityCutoff() const { return _opacityCutoff; }
void setUnlit(bool value);
bool isUnlit() const { return _key.isUnlit(); }
@ -310,7 +375,8 @@ public:
// Albedo maps cannot have opacity detected until they are loaded
// This method allows const changing of the key/schemaBuffer without touching the map
void resetOpacityMap() const;
// return true if the opacity changed, flase otherwise
bool resetOpacityMap() const;
// conversion from legacy material properties to PBR equivalent
static float shininessToRoughness(float shininess) { return 1.0f - shininess / 100.0f; }
@ -357,6 +423,7 @@ private:
float _roughness { DEFAULT_ROUGHNESS };
float _metallic { DEFAULT_METALLIC };
float _scattering { DEFAULT_SCATTERING };
float _opacityCutoff { DEFAULT_OPACITY_CUTOFF };
std::array<glm::mat4, NUM_TEXCOORD_TRANSFORMS> _texcoordTransforms;
glm::vec2 _lightmapParams { 0.0, 1.0 };
glm::vec2 _materialParams { 0.0, 1.0 };
@ -365,7 +432,7 @@ private:
bool _defaultFallthrough { false };
std::unordered_map<uint, bool> _propertyFallthroughs { NUM_TOTAL_FLAGS };
mutable QMutex _textureMapsMutex { QMutex::Recursive };
mutable std::recursive_mutex _textureMapsMutex;
};
typedef std::shared_ptr<Material> MaterialPointer;
@ -425,18 +492,8 @@ public:
float _metallic { Material::DEFAULT_METALLIC }; // Not Metallic
float _scattering { Material::DEFAULT_SCATTERING }; // Scattering info
#if defined(__clang__)
__attribute__((unused))
#endif
glm::vec2 _spare { 0.0f }; // Padding
float _opacityCutoff { Material::DEFAULT_OPACITY_CUTOFF }; // Opacity cutoff applyed when using opacityMap as Mask
uint32_t _key { 0 }; // a copy of the materialKey
#if defined(__clang__)
__attribute__((unused))
#endif
glm::vec3 _spare2 { 0.0f };
// for alignment beauty, Material size == Mat4x4
// Texture Coord Transform Array
glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS];

View file

@ -49,8 +49,7 @@ struct TexMapArray {
struct Material {
vec4 _emissiveOpacity;
vec4 _albedoRoughness;
vec4 _metallicScatteringSpare2;
vec4 _keySpare3;
vec4 _metallicScatteringOpacityCutoffKey;
};
LAYOUT_STD140(binding=GRAPHICS_BUFFER_MATERIAL) uniform materialBuffer {
@ -72,10 +71,11 @@ vec3 getMaterialAlbedo(Material m) { return m._albedoRoughness.rgb; }
float getMaterialRoughness(Material m) { return m._albedoRoughness.a; }
float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); }
float getMaterialMetallic(Material m) { return m._metallicScatteringSpare2.x; }
float getMaterialScattering(Material m) { return m._metallicScatteringSpare2.y; }
float getMaterialMetallic(Material m) { return m._metallicScatteringOpacityCutoffKey.x; }
float getMaterialScattering(Material m) { return m._metallicScatteringOpacityCutoffKey.y; }
float getMaterialOpacityCutoff(Material m) { return m._metallicScatteringOpacityCutoffKey.z; }
BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._keySpare3.x); }
BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._metallicScatteringOpacityCutoffKey.w); }
const BITFIELD EMISSIVE_VAL_BIT = 0x00000001;
const BITFIELD UNLIT_VAL_BIT = 0x00000002;
@ -85,16 +85,18 @@ const BITFIELD GLOSSY_VAL_BIT = 0x00000010;
const BITFIELD OPACITY_VAL_BIT = 0x00000020;
const BITFIELD OPACITY_MASK_MAP_BIT = 0x00000040;
const BITFIELD OPACITY_TRANSLUCENT_MAP_BIT = 0x00000080;
const BITFIELD SCATTERING_VAL_BIT = 0x00000100;
const BITFIELD OPACITY_MAP_MODE_BIT = 0x00000100;
const BITFIELD OPACITY_CUTOFF_VAL_BIT = 0x00000200;
const BITFIELD SCATTERING_VAL_BIT = 0x00000400;
const BITFIELD EMISSIVE_MAP_BIT = 0x00000200;
const BITFIELD ALBEDO_MAP_BIT = 0x00000400;
const BITFIELD METALLIC_MAP_BIT = 0x00000800;
const BITFIELD ROUGHNESS_MAP_BIT = 0x00001000;
const BITFIELD NORMAL_MAP_BIT = 0x00002000;
const BITFIELD OCCLUSION_MAP_BIT = 0x00004000;
const BITFIELD LIGHTMAP_MAP_BIT = 0x00008000;
const BITFIELD SCATTERING_MAP_BIT = 0x00010000;
const BITFIELD EMISSIVE_MAP_BIT = 0x00000800;
const BITFIELD ALBEDO_MAP_BIT = 0x00001000;
const BITFIELD METALLIC_MAP_BIT = 0x00002000;
const BITFIELD ROUGHNESS_MAP_BIT = 0x00004000;
const BITFIELD NORMAL_MAP_BIT = 0x00008000;
const BITFIELD OCCLUSION_MAP_BIT = 0x00010000;
const BITFIELD LIGHTMAP_MAP_BIT = 0x00020000;
const BITFIELD SCATTERING_MAP_BIT = 0x00040000;
<@endif@>

View file

@ -214,14 +214,22 @@ vec3 fetchLightMap(vec2 uv) {
}
<@endfunc@>
<@func evalMaterialOpacity(fetchedOpacity, materialOpacity, matKey, opacity)@>
<@func evalMaterialOpacityMask(fetchedOpacity, materialOpacityCutoff, opacity)@>
{
const float OPACITY_MASK_THRESHOLD = 0.5;
<$opacity$> = mix(1.0,
mix(<$fetchedOpacity$>,
step(OPACITY_MASK_THRESHOLD, <$fetchedOpacity$>),
float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0)),
float((<$matKey$> & (OPACITY_TRANSLUCENT_MAP_BIT | OPACITY_MASK_MAP_BIT)) != 0)) * <$materialOpacity$>;
// This path only valid for opaque or texel opaque material
<$opacity$> = step(<$materialOpacityCutoff$>, <$fetchedOpacity$>);
}
<@endfunc@>
<@func evalMaterialOpacity(fetchedOpacity, materialOpacityCutoff, materialOpacity, matKey, opacity)@>
{
// This path only valid for transparent material
// Assert that float((<$matKey$> & (OPACITY_TRANSLUCENT_MAP_BIT | OPACITY_MASK_MAP_BIT)) != 0)) == 1.0
<$opacity$> = mix(<$fetchedOpacity$>,
step(<$materialOpacityCutoff$>, <$fetchedOpacity$>),
float((<$matKey$> & OPACITY_MASK_MAP_BIT) != 0))
* <$materialOpacity$>;
}
<@endfunc@>

View file

@ -139,6 +139,14 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
* @property {string} opacityMap - The URL of the opacity texture image. Set the value the same as the <code>albedoMap</code>
* value for transparency.
* <code>"hifi_pbr"</code> model only.
* @property {number|string} opacityMapMode - The mode defining the interpretation of the opacity map. Values can be:
* <code>"OPACITY_MAP_OPAQUE"</code> for ignoring the opacity map information.
* <code>"OPACITY_MAP_MASK"</code> for using the opacity map as a mask, where only the texel greater than opacityCutoff are visible and rendered opaque.
* <code>"OPACITY_MAP_BLEND"</code> for using the opacity map for alpha blending the material surface with the background.
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
* @property {number|string} opacityCutoff - The opacity cutoff threshold used to determine the opaque texels of the Opacity map
* when opacityMapMode is "OPACITY_MAP_MASK", range <code>0.0</code> &ndash; <code>1.0</code>.
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
* @property {string} roughnessMap - The URL of the roughness texture image. You can use this or <code>glossMap</code>, but not
* both.
* Set to <code>"fallthrough"</code> to fall through to the material below. <code>"hifi_pbr"</code> model only.
@ -258,6 +266,24 @@ std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource
} else if (value.isDouble()) {
material->setMetallic(value.toDouble());
}
} else if (key == "opacityMapMode") {
auto value = materialJSON.value(key);
auto valueString = (value.isString() ? value.toString() : "");
if (valueString == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OPACITY_MAP_MODE_BIT);
} else {
graphics::MaterialKey::OpacityMapMode mode;
if (graphics::MaterialKey::getOpacityMapModeFromName(valueString.toStdString(), mode)) {
material->setOpacityMapMode(mode);
}
}
} else if (key == "opacityCutoff") {
auto value = materialJSON.value(key);
if (value.isString() && value.toString() == FALLTHROUGH) {
material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OPACITY_CUTOFF_VAL_BIT);
} else if (value.isDouble()) {
material->setOpacityCutoff(value.toDouble());
}
} else if (key == "scattering") {
auto value = materialJSON.value(key);
if (value.isString() && value.toString() == FALLTHROUGH) {
@ -748,13 +774,14 @@ bool NetworkMaterial::isMissingTexture() {
return false;
}
void NetworkMaterial::checkResetOpacityMap() {
bool NetworkMaterial::checkResetOpacityMap() {
// If material textures are loaded, check the material translucency
// FIXME: This should not be done here. The opacity map should already be reset in Material::setTextureMap.
// However, currently that code can be called before the albedo map is defined, so resetOpacityMap will fail.
// Geometry::areTexturesLoaded() is called repeatedly until it returns true, so we do the check here for now
const auto& albedoTexture = _textures[NetworkMaterial::MapChannel::ALBEDO_MAP];
if (albedoTexture.texture) {
resetOpacityMap();
return resetOpacityMap();
}
return false;
}

View file

@ -34,7 +34,7 @@ public:
void setLightMap(const QUrl& url);
bool isMissingTexture();
void checkResetOpacityMap();
bool checkResetOpacityMap();
class Texture {
public:

View file

@ -0,0 +1,17 @@
//
// MaterialCacheScriptingInterface.cpp
// libraries/mmodel-networking/src/model-networking
//
// Created by Sam Gateau on 17 September 2019.
// 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 "MaterialCacheScriptingInterface.h"
MaterialCacheScriptingInterface::MaterialCacheScriptingInterface() :
ScriptableResourceCache::ScriptableResourceCache(DependencyManager::get<MaterialCache>())
{ }

View file

@ -0,0 +1,51 @@
//
// MaterialCacheScriptingInterface.h
// libraries/material-networking/src/material-networking
//
// Created by Sam Gateau on 17 September 2019.
// 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
//
#pragma once
#ifndef hifi_MaterialCacheScriptingInterface_h
#define hifi_MaterialCacheScriptingInterface_h
#include <QObject>
#include <ResourceCache.h>
#include "MaterialCache.h"
class MaterialCacheScriptingInterface : public ScriptableResourceCache, public Dependency {
Q_OBJECT
// Properties are copied over from ResourceCache (see ResourceCache.h for reason).
/**jsdoc
* The <code>TextureCache</code> API manages texture cache resources.
*
* @namespace TextureCache
*
* @hifi-interface
* @hifi-client-entity
* @hifi-avatar
*
* @property {number} numTotal - Total number of total resources. <em>Read-only.</em>
* @property {number} numCached - Total number of cached resource. <em>Read-only.</em>
* @property {number} sizeTotal - Size in bytes of all resources. <em>Read-only.</em>
* @property {number} sizeCached - Size in bytes of all cached resources. <em>Read-only.</em>
*
* @borrows ResourceCache.getResourceList as getResourceList
* @borrows ResourceCache.updateTotalSize as updateTotalSize
* @borrows ResourceCache.prefetch as prefetch
* @borrows ResourceCache.dirty as dirty
*/
public:
MaterialCacheScriptingInterface();
};
#endif // hifi_MaterialCacheScriptingInterface_h

View file

@ -472,7 +472,10 @@ bool Geometry::areTexturesLoaded() const {
return false;
}
material->checkResetOpacityMap();
bool changed = material->checkResetOpacityMap();
if (changed) {
qCWarning(modelnetworking) << "Material list: opacity change detected for material " << material->getName().c_str();
}
}
for (auto& materialMapping : _materialMapping) {
@ -483,7 +486,10 @@ bool Geometry::areTexturesLoaded() const {
return false;
}
materialPair.second->checkResetOpacityMap();
bool changed = materialPair.second->checkResetOpacityMap();
if (changed) {
qCWarning(modelnetworking) << "Mapping list: opacity change detected for material " << materialPair.first.c_str();
}
}
}
}

View file

@ -148,6 +148,8 @@ void ResourceCacheSharedItems::clear() {
ScriptableResourceCache::ScriptableResourceCache(QSharedPointer<ResourceCache> resourceCache) {
_resourceCache = resourceCache;
connect(&(*_resourceCache), &ResourceCache::dirty,
this, &ScriptableResourceCache::dirty, Qt::DirectConnection);
}
QVariantList ScriptableResourceCache::getResourceList() {
@ -323,7 +325,11 @@ QVariantList ResourceCache::getResourceList() {
BLOCKING_INVOKE_METHOD(this, "getResourceList",
Q_RETURN_ARG(QVariantList, list));
} else {
auto resources = _resources.uniqueKeys();
QList<QUrl> resources;
{
QReadLocker locker(&_resourcesLock);
resources = _resources.uniqueKeys();
}
list.reserve(resources.size());
for (auto& resource : resources) {
list << resource;
@ -510,7 +516,7 @@ void ResourceCache::updateTotalSize(const qint64& deltaSize) {
emit dirty();
}
QList<QSharedPointer<Resource>> ResourceCache::getLoadingRequests() {
return DependencyManager::get<ResourceCacheSharedItems>()->getLoadingRequests();
}

View file

@ -317,6 +317,13 @@ class ScriptableResourceCache : public QObject {
Q_PROPERTY(size_t sizeTotal READ getSizeTotalResources NOTIFY dirty)
Q_PROPERTY(size_t sizeCached READ getSizeCachedResources NOTIFY dirty)
/**jsdoc
* @property {number} numGlobalQueriesPending - Total number of global queries pending (across all resource managers). <em>Read-only.</em>
* @property {number} numGlobalQueriesLoading - Total number of global queries loading (across all resource managers). <em>Read-only.</em>
*/
Q_PROPERTY(size_t numGlobalQueriesPending READ getNumGlobalQueriesPending NOTIFY dirty)
Q_PROPERTY(size_t numGlobalQueriesLoading READ getNumGlobalQueriesLoading NOTIFY dirty)
public:
ScriptableResourceCache(QSharedPointer<ResourceCache> resourceCache);
@ -390,6 +397,9 @@ private:
size_t getSizeTotalResources() const { return _resourceCache->getSizeTotalResources(); }
size_t getNumCachedResources() const { return _resourceCache->getNumCachedResources(); }
size_t getSizeCachedResources() const { return _resourceCache->getSizeCachedResources(); }
size_t getNumGlobalQueriesPending() const { return ResourceCache::getPendingRequestCount(); }
size_t getNumGlobalQueriesLoading() const { return ResourceCache::getLoadingRequestCount(); }
};
/// Base class for resources.

View file

@ -269,7 +269,7 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in
bool wasDuplicate = false;
if (sequenceNumber > _lastReceivedSequenceNumber) {
// Update largest recieved sequence number
// Update largest received sequence number
_lastReceivedSequenceNumber = sequenceNumber;
} else {
// Otherwise, it could be a resend, try and remove it from the loss list
@ -312,9 +312,7 @@ void Connection::processControl(ControlPacketPointer controlPacket) {
// We're already in a state where we've received a handshake ack, so we are likely in a state
// where the other end expired our connection. Let's reset.
#ifdef UDT_CONNECTION_DEBUG
qCDebug(networking) << "Got HandshakeRequest from" << _destination << ", stopping SendQueue";
#endif
qCDebug(networking) << "Got HandshakeRequest from" << _destination << "while active, stopping SendQueue";
_hasReceivedHandshakeACK = false;
stopSendQueue();
}
@ -333,7 +331,7 @@ void Connection::processACK(ControlPacketPointer controlPacket) {
// validate that this isn't a BS ACK
if (ack > getSendQueue().getCurrentSequenceNumber()) {
// in UDT they specifically break the connection here - do we want to do anything?
Q_ASSERT_X(false, "Connection::processACK", "ACK recieved higher than largest sent sequence number");
Q_ASSERT_X(false, "Connection::processACK", "ACK received higher than largest sent sequence number");
return;
}

View file

@ -73,6 +73,7 @@ public:
void setMaxBandwidth(int maxBandwidth);
void sendHandshakeRequest();
bool hasReceivedHandshake() const { return _hasReceivedHandshake; }
void recordSentUnreliablePackets(int wireSize, int payloadSize);
void recordReceivedUnreliablePackets(int wireSize, int payloadSize);

View file

@ -239,15 +239,24 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc
int pending = _udpSocket.bytesToWrite();
if (bytesWritten < 0 || pending) {
int wsaError = 0;
static std::atomic<int> previousWsaError (0);
#ifdef WIN32
wsaError = WSAGetLastError();
#endif
qCDebug(networking) << "udt::writeDatagram (" << _udpSocket.state() << sockAddr << ") error - " << wsaError << _udpSocket.error() << "(" << _udpSocket.errorString() << ")"
QString errorString;
QDebug(&errorString) << "udt::writeDatagram (" << _udpSocket.state() << sockAddr << ") error - "
<< wsaError << _udpSocket.error() << "(" << _udpSocket.errorString() << ")"
<< (pending ? "pending bytes:" : "pending:") << pending;
if (previousWsaError.exchange(wsaError) != wsaError) {
qCDebug(networking).noquote() << errorString;
#ifdef DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize << "writing datagram to" << sockAddr;
#endif // DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize << "writing datagram to" << sockAddr;
#endif // DEBUG_EVENT_QUEUE
} else {
HIFI_FCDEBUG(networking(), errorString.toLatin1().constData());
}
}
return bytesWritten;
@ -525,16 +534,25 @@ std::vector<HifiSockAddr> Socket::getConnectionSockAddrs() {
void Socket::handleSocketError(QAbstractSocket::SocketError socketError) {
int wsaError = 0;
static std::atomic<int> previousWsaError(0);
#ifdef WIN32
wsaError = WSAGetLastError();
#endif
int pending = _udpSocket.bytesToWrite();
qCDebug(networking) << "udt::Socket (" << _udpSocket.state() << ") error - " << wsaError << socketError << "(" << _udpSocket.errorString() << ")"
<< (pending ? "pending bytes:" : "pending:") << pending;
QString errorString;
QDebug(&errorString) << "udt::Socket (" << _udpSocket.state() << ") error - " << wsaError << socketError <<
"(" << _udpSocket.errorString() << ")" << (pending ? "pending bytes:" : "pending:")
<< pending;
if (previousWsaError.exchange(wsaError) != wsaError) {
qCDebug(networking).noquote() << errorString;
#ifdef DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize;
#endif // DEBUG_EVENT_QUEUE
int nodeListQueueSize = ::hifi::qt::getEventQueueSize(thread());
qCDebug(networking) << "Networking queue size - " << nodeListQueueSize;
#endif // DEBUG_EVENT_QUEUE
} else {
HIFI_FCDEBUG(networking(), errorString.toLatin1().constData());
}
}
void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) {
@ -548,12 +566,14 @@ void Socket::handleRemoteAddressChange(HifiSockAddr previousAddress, HifiSockAdd
Lock connectionsLock(_connectionsHashMutex);
const auto connectionIter = _connectionsHash.find(previousAddress);
if (connectionIter != _connectionsHash.end()) {
// Don't move classes that are unused so far.
if (connectionIter != _connectionsHash.end() && connectionIter->second->hasReceivedHandshake()) {
auto connection = move(connectionIter->second);
_connectionsHash.erase(connectionIter);
connection->setDestinationAddress(currentAddress);
_connectionsHash[currentAddress] = move(connection);
connectionsLock.unlock();
qCDebug(networking) << "Moved Connection class from" << previousAddress << "to" << currentAddress;
Lock sequenceNumbersLock(_unreliableSequenceNumbersMutex);
const auto sequenceNumbersIter = _unreliableSequenceNumbers.find(previousAddress);

View file

@ -75,7 +75,8 @@ void PhysicalEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
if (motionState) {
removeOwnershipData(motionState);
_entitiesToRemoveFromPhysics.insert(entity);
} else if (entity->isDead() && entity->getElement()) {
}
if (entity->isDead() && entity->getElement()) {
_deadEntities.insert(entity);
}
}

View file

@ -214,6 +214,8 @@ bool OffscreenSurface::eventFilter(QObject* originalDestination, QEvent* event)
fakeMouseEventType = QEvent::MouseButtonRelease;
fakeMouseButtons = Qt::NoButton;
break;
default:
Q_UNREACHABLE();
}
// Same case as OffscreenUi.cpp::eventFilter: touch events are always being accepted so we now use mouse events and consider one touch, touchPoints()[0].
QMouseEvent fakeMouseEvent(fakeMouseEventType, originalEvent->touchPoints()[0].pos(), fakeMouseButton, fakeMouseButtons, Qt::NoModifier);

View file

@ -461,6 +461,13 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial
wasSet = true;
}
break;
case graphics::MaterialKey::OPACITY_CUTOFF_VAL_BIT:
if (materialKey.isOpacityCutoff()) {
schema._opacityCutoff = material->getOpacityCutoff();
schemaKey.setOpacityCutoff(true);
wasSet = true;
}
break;
case graphics::MaterialKey::SCATTERING_VAL_BIT:
if (materialKey.isScattering()) {
schema._scattering = material->getScattering();
@ -752,7 +759,7 @@ bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu:
// For shadows, we only need opacity mask information
auto key = multiMaterial.getMaterialKey();
if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE || key.isOpacityMaskMap()) {
if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE || (key.isOpacityMaskMap() || key.isTranslucentMap())) {
auto& schemaBuffer = multiMaterial.getSchemaBuffer();
batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer);
if (enableTextures) {

View file

@ -103,14 +103,14 @@ void main(void) {
<$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$>
<@if HIFI_USE_TRANSLUCENT@>
float cutoff = getMaterialOpacityCutoff(mat);
float opacity = getMaterialOpacity(mat) * _color.a;
<@else@>
float opacity = 1.0;
<@endif@>
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
<@if HIFI_USE_TRANSLUCENT@>
<$evalMaterialOpacity(albedoTex.a, cutoff, opacity, matKey, opacity)$>;
<$discardInvisible(opacity)$>;
<@else@>
float cutoff = getMaterialOpacityCutoff(mat);
float opacity = 1.0;
<$evalMaterialOpacityMask(albedoTex.a, cutoff, opacity)$>;
<$discardTransparent(opacity)$>;
<@endif@>
@ -155,15 +155,15 @@ void main(void) {
<@endif@>
<@if HIFI_USE_TRANSLUCENT@>
float cutoff = getMaterialOpacityCutoff(mat);
float opacity = getMaterialOpacity(mat) * _color.a;
<$evalMaterialOpacity(albedoTex.a, cutoff, opacity, matKey, opacity)$>;
<$discardInvisible(opacity)$>;
<@else@>
float cutoff = getMaterialOpacityCutoff(mat);
float opacity = 1.0;
<@endif@>
<$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>;
<@if HIFI_USE_TRANSLUCENT@>
<$discardInvisible(opacity)$>;
<@else@>
<$discardTransparent(opacity)$>;
<$evalMaterialOpacityMask(albedoTex.a, cutoff, opacity)$>;
<$discardTransparent(opacity)$>;
<@endif@>
vec3 albedo = getMaterialAlbedo(mat);
@ -217,13 +217,13 @@ void main(void) {
_fragColor0 = color;
<@else@>
_fragColor0 = vec4(evalLightmappedColor(
cam._viewInverse,
1.0,
DEFAULT_OCCLUSION,
fragNormalWS,
albedo,
lightmap),
opacity);
cam._viewInverse,
1.0,
DEFAULT_OCCLUSION,
fragNormalWS,
albedo,
lightmap),
opacity);
<@endif@>
<@else@>
<@if not HIFI_USE_LIGHTMAP@>
@ -241,13 +241,13 @@ void main(void) {
opacity);
<@else@>
_fragColor0 = vec4(evalLightmappedColor(
cam._viewInverse,
1.0,
DEFAULT_OCCLUSION,
fragNormalWS,
albedo,
lightmap),
opacity);
cam._viewInverse,
1.0,
DEFAULT_OCCLUSION,
fragNormalWS,
albedo,
lightmap),
opacity);
<@endif@>
<@endif@>
<@else@>
@ -315,13 +315,13 @@ void main(void) {
opacity);
<@else@>
_fragColor0 = vec4(evalLightmappedColor(
cam._viewInverse,
1.0,
DEFAULT_OCCLUSION,
fragNormalWS,
albedo,
lightmap),
opacity);
cam._viewInverse,
1.0,
DEFAULT_OCCLUSION,
fragNormalWS,
albedo,
lightmap),
opacity);
<@endif@>
<@endif@>
<@endif@>

View file

@ -63,4 +63,6 @@ void EngineStats::run(const RenderContextPointer& renderContext) {
config->frameSetPipelineCount = _gpuStats._PSNumSetPipelines;
config->frameSetInputFormatCount = _gpuStats._ISNumFormatChanges;
// These new stat values are notified with the "newStats" signal triggered by the timer
}

View file

@ -24,42 +24,42 @@ namespace render {
class EngineStatsConfig : public Job::Config{
Q_OBJECT
Q_PROPERTY(quint32 bufferCPUCount MEMBER bufferCPUCount NOTIFY dirty)
Q_PROPERTY(quint32 bufferGPUCount MEMBER bufferGPUCount NOTIFY dirty)
Q_PROPERTY(qint64 bufferCPUMemSize MEMBER bufferCPUMemSize NOTIFY dirty)
Q_PROPERTY(qint64 bufferGPUMemSize MEMBER bufferGPUMemSize NOTIFY dirty)
Q_PROPERTY(quint32 bufferCPUCount MEMBER bufferCPUCount NOTIFY newStats)
Q_PROPERTY(quint32 bufferGPUCount MEMBER bufferGPUCount NOTIFY newStats)
Q_PROPERTY(qint64 bufferCPUMemSize MEMBER bufferCPUMemSize NOTIFY newStats)
Q_PROPERTY(qint64 bufferGPUMemSize MEMBER bufferGPUMemSize NOTIFY newStats)
Q_PROPERTY(quint32 textureCPUCount MEMBER textureCPUCount NOTIFY dirty)
Q_PROPERTY(quint32 textureGPUCount MEMBER textureGPUCount NOTIFY dirty)
Q_PROPERTY(quint32 textureResidentGPUCount MEMBER textureResidentGPUCount NOTIFY dirty)
Q_PROPERTY(quint32 textureFramebufferGPUCount MEMBER textureFramebufferGPUCount NOTIFY dirty)
Q_PROPERTY(quint32 textureResourceGPUCount MEMBER textureResourceGPUCount NOTIFY dirty)
Q_PROPERTY(quint32 textureExternalGPUCount MEMBER textureExternalGPUCount NOTIFY dirty)
Q_PROPERTY(quint32 textureCPUCount MEMBER textureCPUCount NOTIFY newStats)
Q_PROPERTY(quint32 textureGPUCount MEMBER textureGPUCount NOTIFY newStats)
Q_PROPERTY(quint32 textureResidentGPUCount MEMBER textureResidentGPUCount NOTIFY newStats)
Q_PROPERTY(quint32 textureFramebufferGPUCount MEMBER textureFramebufferGPUCount NOTIFY newStats)
Q_PROPERTY(quint32 textureResourceGPUCount MEMBER textureResourceGPUCount NOTIFY newStats)
Q_PROPERTY(quint32 textureExternalGPUCount MEMBER textureExternalGPUCount NOTIFY newStats)
Q_PROPERTY(qint64 textureCPUMemSize MEMBER textureCPUMemSize NOTIFY dirty)
Q_PROPERTY(qint64 textureGPUMemSize MEMBER textureGPUMemSize NOTIFY dirty)
Q_PROPERTY(qint64 textureResidentGPUMemSize MEMBER textureResidentGPUMemSize NOTIFY dirty)
Q_PROPERTY(qint64 textureFramebufferGPUMemSize MEMBER textureFramebufferGPUMemSize NOTIFY dirty)
Q_PROPERTY(qint64 textureResourceGPUMemSize MEMBER textureResourceGPUMemSize NOTIFY dirty)
Q_PROPERTY(qint64 textureExternalGPUMemSize MEMBER textureExternalGPUMemSize NOTIFY dirty)
Q_PROPERTY(qint64 textureCPUMemSize MEMBER textureCPUMemSize NOTIFY newStats)
Q_PROPERTY(qint64 textureGPUMemSize MEMBER textureGPUMemSize NOTIFY newStats)
Q_PROPERTY(qint64 textureResidentGPUMemSize MEMBER textureResidentGPUMemSize NOTIFY newStats)
Q_PROPERTY(qint64 textureFramebufferGPUMemSize MEMBER textureFramebufferGPUMemSize NOTIFY newStats)
Q_PROPERTY(qint64 textureResourceGPUMemSize MEMBER textureResourceGPUMemSize NOTIFY newStats)
Q_PROPERTY(qint64 textureExternalGPUMemSize MEMBER textureExternalGPUMemSize NOTIFY newStats)
Q_PROPERTY(quint32 texturePendingGPUTransferCount MEMBER texturePendingGPUTransferCount NOTIFY dirty)
Q_PROPERTY(qint64 texturePendingGPUTransferSize MEMBER texturePendingGPUTransferSize NOTIFY dirty)
Q_PROPERTY(qint64 textureResourcePopulatedGPUMemSize MEMBER textureResourcePopulatedGPUMemSize NOTIFY dirty)
Q_PROPERTY(quint32 texturePendingGPUTransferCount MEMBER texturePendingGPUTransferCount NOTIFY newStats)
Q_PROPERTY(qint64 texturePendingGPUTransferSize MEMBER texturePendingGPUTransferSize NOTIFY newStats)
Q_PROPERTY(qint64 textureResourcePopulatedGPUMemSize MEMBER textureResourcePopulatedGPUMemSize NOTIFY newStats)
Q_PROPERTY(quint32 frameAPIDrawcallCount MEMBER frameAPIDrawcallCount NOTIFY dirty)
Q_PROPERTY(quint32 frameDrawcallCount MEMBER frameDrawcallCount NOTIFY dirty)
Q_PROPERTY(quint32 frameDrawcallRate MEMBER frameDrawcallRate NOTIFY dirty)
Q_PROPERTY(quint32 frameAPIDrawcallCount MEMBER frameAPIDrawcallCount NOTIFY newStats)
Q_PROPERTY(quint32 frameDrawcallCount MEMBER frameDrawcallCount NOTIFY newStats)
Q_PROPERTY(quint32 frameDrawcallRate MEMBER frameDrawcallRate NOTIFY newStats)
Q_PROPERTY(quint32 frameTriangleCount MEMBER frameTriangleCount NOTIFY dirty)
Q_PROPERTY(quint32 frameTriangleRate MEMBER frameTriangleRate NOTIFY dirty)
Q_PROPERTY(quint32 frameTriangleCount MEMBER frameTriangleCount NOTIFY newStats)
Q_PROPERTY(quint32 frameTriangleRate MEMBER frameTriangleRate NOTIFY newStats)
Q_PROPERTY(quint32 frameTextureCount MEMBER frameTextureCount NOTIFY dirty)
Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY dirty)
Q_PROPERTY(quint64 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY dirty)
Q_PROPERTY(quint32 frameTextureCount MEMBER frameTextureCount NOTIFY newStats)
Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY newStats)
Q_PROPERTY(quint64 frameTextureMemoryUsage MEMBER frameTextureMemoryUsage NOTIFY newStats)
Q_PROPERTY(quint32 frameSetPipelineCount MEMBER frameSetPipelineCount NOTIFY dirty)
Q_PROPERTY(quint32 frameSetInputFormatCount MEMBER frameSetInputFormatCount NOTIFY dirty)
Q_PROPERTY(quint32 frameSetPipelineCount MEMBER frameSetPipelineCount NOTIFY newStats)
Q_PROPERTY(quint32 frameSetInputFormatCount MEMBER frameSetInputFormatCount NOTIFY newStats)
public:
@ -101,13 +101,6 @@ namespace render {
quint32 frameSetPipelineCount{ 0 };
quint32 frameSetInputFormatCount{ 0 };
void emitDirty() { emit dirty(); }
signals:
void dirty();
};
class EngineStats {

View file

@ -35,7 +35,7 @@ namespace shader {
const Dialect DEFAULT_DIALECT = Dialect::glsl310es;
const std::vector<Dialect>& allDialects() {
static const std::vector<Dialect> ALL_DIALECTS{ { Dialect::glsl310es } };
static const std::vector<Dialect> ALL_DIALECTS{ Dialect::glsl310es };
return ALL_DIALECTS;
}

View file

@ -0,0 +1,27 @@
//
// CrashAnnotations.cpp
// libraries/shared/src
//
// Created by Clement Brisset on 9/26/19.
// 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 "SharedUtil.h"
// Global variables specifically tuned to work with ptrace module for crash collection
// Do not move/rename unless you update the ptrace module with it.
bool crash_annotation_isShuttingDown = false;
namespace crash {
namespace annotations {
void setShutdownState(bool isShuttingDown) {
crash_annotation_isShuttingDown = isShuttingDown;
}
};
};

View file

@ -0,0 +1,23 @@
//
// CrashAnnotations.h
// libraries/shared/src
//
// Created by Clement Brisset on 9/26/19.
// 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_CrashAnnotations_h
#define hifi_CrashAnnotations_h
namespace crash {
namespace annotations {
void setShutdownState(bool isShuttingDown);
};
};
#endif // hifi_CrashAnnotations_h

View file

@ -14,6 +14,8 @@
#include <stdint.h>
#include <array>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
@ -364,4 +366,30 @@ inline int fastLrintf(float x) {
#endif
}
// returns the FOV from the projection matrix
inline glm::vec4 extractFov( const glm::mat4& m) {
static const std::array<glm::vec4, 4> CLIPS{ {
{ 1, 0, 0, 1 },
{ -1, 0, 0, 1 },
{ 0, 1, 0, 1 },
{ 0, -1, 0, 1 }
} };
glm::mat4 mt = glm::transpose(m);
glm::vec4 v, result;
// Left
v = mt * CLIPS[0];
result.x = -atanf(v.z / v.x);
// Right
v = mt * CLIPS[1];
result.y = atanf(v.z / v.x);
// Down
v = mt * CLIPS[2];
result.z = -atanf(v.z / v.y);
// Up
v = mt * CLIPS[3];
result.w = atanf(v.z / v.y);
return result;
}
#endif // hifi_GLMHelpers_h

View file

@ -279,6 +279,7 @@ public:
* @param {string} [injectedJavaScriptUrl=""] - The URL of JavaScript to inject into the web page.
* @param {boolean} [loadOtherBase=false] - If <code>true</code>, the web page or app is displayed in a frame with "back"
* and "close" buttons.
* <p class="important">Deprecated: This parameter is deprecated and will be removed.</p>
*/
Q_INVOKABLE void gotoWebScreen(const QString& url);
Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl, bool loadOtherBase = false);

View file

@ -0,0 +1,59 @@
"use strict";
var Page = Script.require('../lib/skit/Page.js');
function openView() {
//window.closed.connect(function() { Script.stop(); });
var pages = new Pages(Script.resolvePath("."));
function fromQml(message) {
console.log(JSON.stringify(message))
if (message.method == "inspectResource") {
pages.open("openResourceInspector")
pages.sendTo("openResourceInspector", message)
return;
}
if (pages.open(message.method)) {
return;
}
}
function openCashWindow(window) {
var onMousePressEvent = function (e) {
};
Controller.mousePressEvent.connect(onMousePressEvent);
var onMouseReleaseEvent = function () {
};
Controller.mouseReleaseEvent.connect(onMouseReleaseEvent);
var onMouseMoveEvent = function (e) {
};
Controller.mouseMoveEvent.connect(onMouseMoveEvent);
}
function closeCashWindow() {
Controller.mousePressEvent.disconnect(onMousePressEvent);
Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent);
Controller.mouseMoveEvent.disconnect(onMouseMoveEvent);
pages.clear();
}
pages.addPage('Cash', 'Cash', "cash.qml", 300, 500, fromQml, openCashWindow, closeCashWindow);
pages.addPage('openModelCacheInspector', 'Model Cache Inspector', "cash/ModelCacheInspector.qml", 300, 500, fromQml);
pages.addPage('openMaterialCacheInspector', 'Material Cache Inspector', "cash/MaterialCacheInspector.qml", 300, 500, fromQml);
pages.addPage('openTextureCacheInspector', 'Texture Cache Inspector', "cash/TextureCacheInspector.qml", 300, 500, fromQml);
pages.addPage('openAnimationCacheInspector', 'Animation Cache Inspector', "cash/AnimationCacheInspector.qml", 300, 500);
pages.addPage('openSoundCacheInspector', 'Sound Cache Inspector', "cash/SoundCacheInspector.qml", 300, 500);
pages.addPage('openResourceInspector', 'Resource Inspector', "cash/ResourceInspector.qml", 300, 500);
pages.open('Cash');
return pages;
}
openView();

View file

@ -0,0 +1,167 @@
//
// cash.qml
//
// Created by Sam Gateau on 17/9/2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import controlsUit 1.0 as HifiControls
import "../lib/prop" as Prop
import "cash"
import "../lib/plotperf"
Rectangle {
anchors.fill: parent
id: root;
Prop.Global { id: global;}
color: global.color
ScrollView {
id: scrollView
anchors.fill: parent
contentWidth: parent.width
clip: true
Column {
id: column
width: parent.width
Prop.PropFolderPanel {
label: "Resource Queries Inspector"
isUnfold: true
panelFrameData: Component {
Column {
PlotPerf {
title: "Global Queries"
height: 80
valueScale: 1
valueUnit: ""
plots: [
{
object: ModelCache,
prop: "numGlobalQueriesPending",
label: "Pending",
color: "#1AC567"
},
{
object: ModelCache,
prop: "numGlobalQueriesLoading",
label: "Loading",
color: "#FEC567"
},
{
object: ModelCache,
prop: "numLoading",
label: "Model Loading",
color: "#C5FE67"
}
]
}
}
}
}
Prop.PropFolderPanel {
label: "Cache Inspectors"
isUnfold: true
panelFrameData: Component {
Column {
Prop.PropButton {
text: "Model"
onClicked: {
sendToScript({method: "openModelCacheInspector"});
}
width:column.width
}
Prop.PropButton {
text: "Material"
onClicked: {
sendToScript({method: "openMaterialCacheInspector"});
}
width:column.width
}
Prop.PropButton {
text: "Texture"
onClicked: {
sendToScript({method: "openTextureCacheInspector"});
}
width:column.width
}
Prop.PropButton {
text: "Animation"
onClicked: {
sendToScript({method: "openAnimationCacheInspector"});
}
width:column.width
}
Prop.PropButton {
text: "Sound"
onClicked: {
sendToScript({method: "openSoundCacheInspector"});
}
width:column.width
}
}
}
}
Prop.PropFolderPanel {
label: "Stats"
isUnfold: true
panelFrameData: Component { Column {
PlotPerf {
title: "Resources"
height: 200
valueScale: 1
valueUnit: ""
plots: [
{
object: TextureCache,
prop: "numTotal",
label: "Textures",
color: "#1AC567"
},
{
object: TextureCache,
prop: "numCached",
label: "Textures Cached",
color: "#FEC567"
},
{
object: ModelCache,
prop: "numTotal",
label: "Models",
color: "#FED959"
},
{
object: ModelCache,
prop: "numCached",
label: "Models Cached",
color: "#FEFE59"
},
{
object: MaterialCache,
prop: "numTotal",
label: "Materials",
color: "#00B4EF"
},
{
object: MaterialCache,
prop: "numCached",
label: "Materials Cached",
color: "#FFB4EF"
}
]
}}
}
}
}
}
}

View file

@ -0,0 +1,21 @@
//
// AnimationCacheInspector.qml
//
// Created by Sam Gateau on 2019-09-17
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import "../../lib/prop" as Prop
ResourceCacheInspector {
id: root;
anchors.fill: parent.fill
cache: AnimationCache
cacheResourceName: "Animation"
}

View file

@ -0,0 +1,21 @@
//
// MaterialCacheInspector.qml
//
// Created by Sam Gateau on 2019-09-17
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import "../../lib/prop" as Prop
ResourceCacheInspector {
id: root;
anchors.fill: parent.fill
cache: MaterialCache
cacheResourceName: "Material"
}

View file

@ -0,0 +1,21 @@
//
// ModelCacheInspector.qml
//
// Created by Sam Gateau on 2019-09-17
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import "../../lib/prop" as Prop
ResourceCacheInspector {
id: root;
anchors.fill: parent.fill
cache: ModelCache
cacheResourceName: "Model"
}

View file

@ -0,0 +1,367 @@
//
// ResourceCacheInspector.qml
//
// Created by Sam Gateau on 2019-09-17
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import QtQml.Models 2.12
import "../../lib/skit/qml" as Skit
import "../../lib/prop" as Prop
Item {
id: root;
Prop.Global { id: global }
anchors.fill: parent.fill
property var cache: {}
property string cacheResourceName: ""
function fromScript(message) {
switch (message.method) {
case "resetItemList":
resetItemListFromCache()
break;
}
}
function requestResourceDetails(resourceURL) {
sendToScript({method: "inspectResource", params: {url: resourceURL, semantic: cacheResourceName}});
}
Component.onCompleted: {
resetItemListFromCache();
}
function fetchItemsList() {
var theList;
if (cache !== undefined) {
theList = cache.getResourceList();
} else {
theList = ["ResourceCacheInspector.cache is undefined"];
}
var theListString = new Array(theList.length)
for (var i in theList) {
theListString[i] = (theList[i].toString())
}
return theListString;
}
function resetItemListFromCache() {
resetItemList(fetchItemsList())
}
property var needFreshList : false
function updateItemListFromCache() {
needFreshList = true
}
Timer {
interval: 2000; running: true; repeat: true
onTriggered: pullFreshValues()
}
function pullFreshValues() {
if (needFreshList) {
updateItemList(fetchItemsList())
needFreshList = false
}
}
property alias resourceItemsModel: visualModel.model
property var currentItemsList: new Array();
function packItemEntry(item, identifier) {
var entry = { "identifier": identifier, "name": "", "scheme": "", "host": "", "pathDir": "", "url": item}
if (item.length > 0) {
// Detect scheme:
var schemePos = item.search(":")
entry.scheme = item.substring(0, schemePos)
if (schemePos < 0) schemePos = 0
else schemePos += 1
// path pos is probably after schemePos
var pathPos = schemePos
// try to detect //userinfo@host:port
var token = item.substr(schemePos, 2);
if (token.search("//") == 0) {
pathPos += 2
}
item = item.substring(pathPos, item.length)
// item is now everything after scheme:[//]
var splitted = item.split('/')
// odd ball, the rest of the url has no other'/' ?
// in theory this means it s just the host info ?
// we are assuming that path ALWAYS starts with a slash
entry.host = splitted[0]
if (splitted.length > 1) {
entry.name = splitted[splitted.length - 1]
// if splitted is longer than 2 then there should be a path dir
if (splitted.length > 2) {
for (var i = 1; i < splitted.length - 1; i++) {
entry.pathDir += '/' + splitted[i]
}
}
}
}
return entry
}
function resetItemList(itemList) {
currentItemsList = []
resourceItemsModel.clear()
for (var i in itemList) {
var item = itemList[i]
currentItemsList.push(item)
resourceItemsModel.append(packItemEntry(item, currentItemsList.length -1))
}
// At the end of it, force an update
visualModel.forceUpdate()
}
function updateItemList(newItemList) {
resetItemList(newItemList)
}
property var itemFields: ['identifier', 'name', 'scheme', 'host', 'pathDir', 'url']
Column {
id: header
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Item {
anchors.left: parent.left
anchors.right: parent.right
height: totalCount.height
id: headerTop
Prop.PropButton {
id: refreshButton
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: "Refresh"
color: needFreshList ? global.colorOrangeAccent : global.fontColor
onPressed: { pullFreshValues() }
}
GridLayout {
anchors.left: refreshButton.right
anchors.right: parent.right
id: headerCountLane
columns: 3
Item {
Layout.fillWidth: true
Prop.PropScalar {
id: itemCount
label: "Count"
object: root.cache
property: "numTotal"
integral: true
readOnly: true
}
}
Item {
Layout.fillWidth: true
Prop.PropScalar {
id: totalCount
label: "Count"
object: root.cache
property: "numTotal"
integral: true
readOnly: true
onSourceValueVarChanged: { updateItemListFromCache() }
}
}
Item {
Layout.fillWidth: true
Prop.PropScalar {
id: cachedCount
label: "Cached"
object: root.cache
property: "numCached"
integral: true
readOnly: true
onSourceValueVarChanged: { updateItemListFromCache() }
}
}
}
}
Item {
anchors.left: parent.left
anchors.right: parent.right
height: orderSelector.height
Prop.PropText {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 50
id: orderSelectorLabel
text: "Sort by"
horizontalAlignment: Text.AlignHCenter
}
Prop.PropComboBox {
anchors.left: orderSelectorLabel.right
width: 80
id: orderSelector
model: itemFields
currentIndex: 1
property var isSchemeVisible: (currentIndex == 2 || filterFieldSelector.currentIndex == 2)
property var isHostVisible: (currentIndex == 3 || filterFieldSelector.currentIndex == 3)
property var isPathDirVisible: (currentIndex == 4 || filterFieldSelector.currentIndex == 4)
property var isURLVisible: (currentIndex == 5 || filterFieldSelector.currentIndex == 5)
}
Prop.PropTextField {
anchors.left: orderSelector.right
anchors.right: filterFieldSelector.left
id: nameFilter
placeholderText: qsTr("Filter by " + itemFields[filterFieldSelector.currentIndex] + "...")
Layout.fillWidth: true
}
Prop.PropComboBox {
anchors.right: parent.right
id: filterFieldSelector
model: itemFields
currentIndex: 1
width: 80
opacity: (nameFilter.text.length > 0) ? 1.0 : 0.5
}
}
}
Component {
id: resouceItemDelegate
MouseArea {
id: dragArea
property bool held: false
anchors { left: parent.left; right: parent.right }
height: item.height
onPressed: {held = true}
onReleased: {held = false}
onDoubleClicked: { requestResourceDetails(model.url) }
Rectangle {
id: item
width: parent.width
height: global.slimHeight
color: dragArea.held ? global.colorBackHighlight : (index % 2 ? global.colorBackShadow : global.colorBack)
Row {
id: itemRow
anchors.verticalCenter : parent.verticalCenter
Prop.PropText {
id: itemIdentifier
text: model.identifier
width: 30
}
Prop.PropSplitter {
visible: orderSelector.isSchemeVisible
size:8
}
Prop.PropLabel {
visible: orderSelector.isSchemeVisible
text: model.scheme
width: 30
}
Prop.PropSplitter {
visible: orderSelector.isHostVisible
size:8
}
Prop.PropLabel {
visible: orderSelector.isHostVisible
text: model.host
width: 150
}
Prop.PropSplitter {
visible: orderSelector.isPathDirVisible
size:8
}
Prop.PropLabel {
visible: orderSelector.isPathDirVisible
text: model.pathDir
}
Prop.PropSplitter {
size:8
}
Prop.PropLabel {
visible: !orderSelector.isURLVisible
text: model.name
}
Prop.PropLabel {
visible: orderSelector.isURLVisible
text: model.url
}
}
}
}
}
Skit.SortFilterModel {
id: visualModel
model: ListModel {}
property int sortOrder: orderSelector.currentIndex
property var lessThanArray: [
function(left, right) { return left.index < right.index },
function(left, right) { return left.name < right.name },
function(left, right) { return left.scheme < right.scheme },
function(left, right) { return left.host < right.host },
function(left, right) { return left.pathDir < right.pathDir },
function(left, right) { return left.url < right.url }
];
lessThan: lessThanArray[sortOrder]
property int filterField: filterFieldSelector.currentIndex
onFilterFieldChanged: { refreshFilter() }
property var textFilter: nameFilter.text
onTextFilterChanged: { refreshFilter() }
function filterToken(itemWord, token) { return (itemWord.search(token) > -1) }
property var acceptItemArray: [
function(item) { return true },
function(item) { return filterToken(item.identifier.toString(), textFilter) },
function(item) { return filterToken(item.name, textFilter) },
function(item) { return filterToken(item.scheme, textFilter) },
function(item) { return filterToken(item.host, textFilter) },
function(item) { return filterToken(item.pathDir, textFilter) },
function(item) { return filterToken(item.url, textFilter) }
]
function refreshFilter() {
//console.log("refreshFilter! token = " + textFilter + " field = " + filterField)
acceptItem = acceptItemArray[(textFilter.length != 0) * + (1 + filterField)]
}
delegate: resouceItemDelegate
}
ListView {
anchors.top: header.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
clip: true
id: listView
model: visualModel
}
}

View file

@ -0,0 +1,58 @@
//
// ResourceInspector.qml
//
// Created by Sam Gateau on 2019-09-24
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import QtQml.Models 2.12
import "../../lib/prop" as Prop
Item {
id: root;
Prop.Global { id: global }
anchors.fill: parent.fill
property var cache: {}
property string cacheResourceName: ""
function fromScript(message) {
switch (message.method) {
case "inspectResource":
inspectResource(message.params.url, message.params.semantic)
break;
}
}
function inspectResource(url, semantic) {
console.log("inspectResource :" + url + " as " + semantic)
info.text = "url: " + url + "\nsemantic: " + semantic + "\n";
if (semantic == "Texture") {
var res = TextureCache.prefetch(url, 0)
info.text += JSON.stringify(res);
} else if (semantic == "Model") {
var res = ModelCache.prefetch(url)
info.text += JSON.stringify(res);
}
}
TextEdit {
id: info
anchors.fill: parent
text: "Click an object to get material JSON"
width: root.width
font.pointSize: 10
color: "#FFFFFF"
readOnly: true
selectByMouse: true
wrapMode: Text.WordWrap
}
}

View file

@ -0,0 +1,21 @@
//
// SoundCacheInspector.qml
//
// Created by Sam Gateau on 2019-09-17
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import "../../lib/prop" as Prop
ResourceCacheInspector {
id: root;
anchors.fill: parent.fill
cache: SoundCache
cacheResourceName: "Sound"
}

View file

@ -0,0 +1,21 @@
//
// TextureCacheInspector.qml
//
// Created by Sam Gateau on 2019-09-17
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.7
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import "../../lib/prop" as Prop
ResourceCacheInspector {
id: root;
anchors.fill: parent.fill
cache: TextureCache
cacheResourceName: "Texture"
}

View file

@ -0,0 +1,6 @@
ResourceCacheInspector 1.0 ResourceCacheInspector.qml
TextureCacheInspector 1.0 TextureCacheInspector.qml
MaterialCacheInspector 1.0 MaterialCacheInspector.qml
ModelCacheInspector 1.0 ModelCacheInspector.qml
AnimationCacheInspector 1.0 AnimationCacheInspector.qml
SoundCacheInspector 1.0 SoundCacheInspector.qml

View file

@ -91,12 +91,12 @@ Rectangle {
}
}
Original.ScrollView {
ListView {
anchors.fill: parent
ListView {
id: theView
model: jobsModel
delegate: objRecursiveDelegate
}
id: theView
model: jobsModel
delegate: objRecursiveDelegate
}
}

View file

@ -16,6 +16,7 @@ PropItem {
id: root
property alias enums : valueCombo.model
property alias currentIndex : valueCombo.currentIndex
PropComboBox {
id: valueCombo

View file

@ -25,19 +25,16 @@ Item {
//
function defaultGet() { var v = root.object[root.property]; return v; }
function defaultSet(value) { root.object[root.property] = value; }
// function defaultSetReadOnly(value) { log ( "read only " + property + ", NOT setting to " + value); }
// function defaultSetReadOnly(value) {}
// property var valueVarSetter: (root.readOnly ? defaultSetReadOnly : defaultSet)
property var valueVarSetter: defaultSet
function defaultSetReadOnly(value) {}
property var valueVarSetter: (readOnly ? defaultSetReadOnly : defaultSet)
property var valueVarGetter: defaultGet
// PropItem is stretching horizontally accross its parent
// Fixed height
height: global.lineHeight
anchors.left: parent.left
anchors.right: parent.right
height: global.lineHeight
anchors.leftMargin: global.horizontalMargin
anchors.rightMargin: global.horizontalMargin
// LabelControl And SplitterControl are on the left side of the PropItem
property bool showLabel: true

View file

@ -32,9 +32,7 @@ PropItem {
property var sourceValueVar: root.valueVarGetter()
function applyValueVarFromWidgets(value) {
if (!root.readOnly) {
root.valueVarSetter(value)
}
root.valueVarSetter(value)
}
PropLabel {
@ -58,6 +56,7 @@ PropItem {
MouseArea{
id: mousearea
enabled: !root.readOnly
anchors.fill: parent
onDoubleClicked: { sliderControl.visible = !sliderControl.visible }
}

View file

@ -1,8 +1,10 @@
Module Prop
Global 1.0 style/Global.qml
PropText 1.0 style/PiText.qml
PropTextField 1.0 style/PiTextField.qml
PropLabel 1.0 style/PiLabel.qml
PropSplitter 1.0 style/PiSplitter.qml
PropButton 1.0 style/PiButton.qml
PropComboBox 1.0 style/PiComboBox.qml
PropCanvasIcon 1.0 style/PiCanvasIcon.qml
PropCheckBox 1.0 style/PiCheckBox.qml

View file

@ -0,0 +1,35 @@
//
// Prop/style/PiButton.qml
//
// Created by Sam Gateau on 17/09/2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.6
import QtQuick.Controls 2.1
Button {
Global { id: global }
id: control
text: ""
spacing: 0
property alias color: theContentItem.color
contentItem: PiText {
id: theContentItem
text: control.text
horizontalAlignment: Text.AlignHCenter
color: global.fontColor
}
background: Rectangle {
color: control.down ? global.colorBackHighlight : global.colorBackShadow
opacity: enabled ? 1 : 0.3
border.color: control.down ? global.colorBorderHighight : (control.hovered ? global.colorBorderHighight : global.colorBorderLight)
border.width: global.valueBorderWidth
radius: global.valueBorderRadius
}
}

View file

@ -16,7 +16,9 @@ ComboBox {
id: valueCombo
height: global.slimHeight
width: 120
implicitHeight: global.slimHeight
// look
flat: true
@ -51,8 +53,6 @@ ComboBox {
}
background: Rectangle {
implicitWidth: 120
implicitHeight: 40
color: global.colorBack
border.color: valueCombo.popup.visible ? global.colorBorderLighter : global.colorBorderLight
border.width: global.valueBorderWidth

View file

@ -0,0 +1,31 @@
//
// Prop/style/PiTextField.qml
//
// Created by Sam Gateau on 9/24/2019
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.12
import QtQuick.Controls 2.12
TextField {
id: control
Global { id: global }
implicitHeight: global.slimHeight
implicitWidth: 200
placeholderText: qsTr("Enter description")
color: global.fontColor
font.pixelSize: global.fontSize
font.family: global.fontFamily
font.weight: global.fontWeight
background: Rectangle {
color: (control.text.length > 0) ? global.colorBackHighlight : global.colorBackShadow
border.color: (control.text.length > 0) ? global.colorBorderHighight : "transparent"
}
}

View file

@ -10,11 +10,12 @@
"use strict";
(function() {
function Page(title, qmlurl, width, height, onViewCreated, onViewClosed) {
function Page(title, qmlurl, width, height, onFromQml, onViewCreated, onViewClosed) {
this.title = title;
this.qml = qmlurl;
this.qmlurl = qmlurl;
this.width = width;
this.height = height;
this.onFromQml = onFromQml;
this.onViewCreated = onViewCreated;
this.onViewClosed = onViewClosed;
@ -30,6 +31,9 @@ Page.prototype.killView = function () {
//this.window.closed.disconnect(function () {
// this.killView();
//});
if (this.onFromQml) {
this.window.fromQml.disconnect(this.onFromQml)
}
this.window.close();
this.window = false;
}
@ -39,12 +43,15 @@ Page.prototype.createView = function () {
var that = this;
if (!this.window) {
print("Page: New window for page:" + this.title);
this.window = Desktop.createWindow(Script.resolvePath(this.qml), {
this.window = Desktop.createWindow(this.qmlurl, {
title: this.title,
presentationMode: Desktop.PresentationMode.NATIVE,
size: {x: this.width, y: this.height}
});
this.onViewCreated(this.window);
if (this.onFromQml) {
this.window.fromQml.connect(this.onFromQml)
}
this.window.closed.connect(function () {
that.killView();
that.onViewClosed();
@ -53,11 +60,13 @@ Page.prototype.createView = function () {
};
Pages = function () {
Pages = function (relativePath) {
print(relativePath)
this._relativePath = relativePath
this._pages = {};
};
Pages.prototype.addPage = function (command, title, qmlurl, width, height, onViewCreated, onViewClosed) {
Pages.prototype.addPage = function (command, title, qmlurl, width, height, onFromQml, onViewCreated, onViewClosed) {
if (onViewCreated === undefined) {
// Workaround for bad linter
onViewCreated = function(window) {};
@ -66,7 +75,7 @@ Pages.prototype.addPage = function (command, title, qmlurl, width, height, onVie
// Workaround for bad linter
onViewClosed = function() {};
}
this._pages[command] = new Page(title, qmlurl, width, height, onViewCreated, onViewClosed);
this._pages[command] = new Page(title, Script.resolvePath(this._relativePath + qmlurl), width, height, onFromQml, onViewCreated, onViewClosed);
};
Pages.prototype.open = function (command) {
@ -87,4 +96,12 @@ Pages.prototype.clear = function () {
this._pages = {};
};
Pages.prototype.sendTo = function (command, message) {
if (!this._pages[command]) {
print("Pages: unknown command = " + command);
return;
}
this._pages[command].window.sendToQml(message);
};
}());

View file

@ -0,0 +1,93 @@
import QtQuick 2.9
import QtQml.Models 2.3
DelegateModel {
id: delegateModel
property var lessThan: function(left, right) { return true; }
property var acceptItem: function(item) { return true; }
function insertPosition(lessThanFunctor, item) {
var lower = 0
var upper = visibleItems.count
while (lower < upper) {
var middle = Math.floor(lower + (upper - lower) / 2)
var result = lessThanFunctor(item.model, visibleItems.get(middle).model);
if (result) {
upper = middle
} else {
lower = middle + 1
}
}
return lower
}
function sortAndFilter(lessThanFunctor, acceptItemFunctor) {
while (unsortedItems.count > 0) {
var item = unsortedItems.get(0)
if (acceptItemFunctor(item.model)) {
var index = insertPosition(lessThanFunctor, item)
item.groups = ["items","visible"]
visibleItems.move(item.visibleIndex, index)
} else {
item.groups = ["items"]
}
}
}
// Private bool to track when items changed and view is dirty
property bool itemsDirty: true
function update() {
console.log("SortFilterMode: update and sort and filter items !!" + items.count);
if (items.count > 0) {
items.setGroups(0, items.count, ["items","unsorted"]);
}
sortAndFilter(lessThan, acceptItem)
itemsDirty = false;
itemsUpdated()
}
signal itemsUpdated()
function updateOnItemsChanged() {
itemsDirty = true;
if (isAutoUpdateOnChanged) {
update()
}
}
property bool isAutoUpdateOnChanged: false
function setAutoUpdateOnChanged(enabled) {
isAutoUpdateOnChanged = enabled
if (enabled) {
update()
}
}
function forceUpdate() {
update();
}
items.onChanged: updateOnItemsChanged()
onLessThanChanged: update()
onAcceptItemChanged: update()
groups: [
DelegateModelGroup {
id: visibleItems
name: "visible"
includeByDefault: false
},
DelegateModelGroup {
id: unsortedItems
name: "unsorted"
includeByDefault: false
}
]
filterOnGroup: "visible"
}

View file

@ -0,0 +1 @@
SortFilterModel 1.0 SortFilterModel.qml

View file

@ -1,14 +1,13 @@
var MaterialInspector = Script.require('./materialInspector.js');
var Page = Script.require('./luci/Page.js');
var Page = Script.require('../lib/skit/Page.js');
function openView() {
//window.closed.connect(function() { Script.stop(); });
var pages = new Pages(Script.resolvePath("."));
var pages = new Pages();
function fromQml(message) {
if (pages.open(message.method)) {
return;
@ -17,12 +16,6 @@ function openView() {
var luciWindow
function openLuciWindow(window) {
if (luciWindow !== undefined) {
activeWindow.fromQml.disconnect(fromQml);
}
if (window !== undefined) {
window.fromQml.connect(fromQml);
}
luciWindow = window;
@ -57,9 +50,6 @@ function openView() {
}
function closeLuciWindow() {
if (luciWindow !== undefined) {
activeWindow.fromQml.disconnect(fromQml);
}
luciWindow = {};
Controller.mousePressEvent.disconnect(onMousePressEvent);
@ -68,10 +58,10 @@ function openView() {
pages.clear();
}
pages.addPage('Luci', 'Luci', '../luci.qml', 300, 420, openLuciWindow, closeLuciWindow);
pages.addPage('openEngineInspectorView', 'Render Engine Inspector', '../engineInspector.qml', 300, 400);
pages.addPage('openEngineLODView', 'Render LOD', '../lod.qml', 300, 400);
pages.addPage('openMaterialInspectorView', 'Material Inspector', '../materialInspector.qml', 300, 400, MaterialInspector.setWindow, MaterialInspector.setWindow);
pages.addPage('Luci', 'Luci', 'luci.qml', 300, 420, fromQml, openLuciWindow, closeLuciWindow);
pages.addPage('openEngineInspectorView', 'Render Engine Inspector', 'engineInspector.qml', 300, 400);
pages.addPage('openEngineLODView', 'Render LOD', 'lod.qml', 300, 400);
pages.addPage('openMaterialInspectorView', 'Material Inspector', 'materialInspector.qml', 300, 400, null, MaterialInspector.setWindow, MaterialInspector.setWindow);
pages.open('Luci');

View file

@ -128,7 +128,7 @@ function fromQml(message) {
var SELECT_LIST = "luci_materialInspector_SelectionList";
Selection.enableListHighlight(SELECT_LIST, {
outlineUnoccludedColor: { red: 255, green: 255, blue: 255 }
outlineUnoccludedColor: { red: 125, green: 255, blue: 225 }
});
function setSelectedObject(id, type) {
Selection.clearSelectedItemsList(SELECT_LIST);

View file

@ -203,10 +203,20 @@ function maybeDeleteRemoteIndicatorTimeout() {
}
}
var reactionsBegun = [];
var pointReticle = null;
var mouseMoveEventsConnected = false;
var targetPointInterpolateConnected = false;
var pointAtTarget = Vec3.ZERO;
var isReticleVisible = true;
function targetPointInterpolate() {
if (reticlePosition) {
pointAtTarget = Vec3.mix(pointAtTarget, reticlePosition, POINT_AT_MIX_ALPHA);
isReticleVisible = MyAvatar.setPointAt(pointAtTarget);
}
}
function beginReactionWrapper(reaction) {
maybeDeleteRemoteIndicatorTimeout();
@ -227,14 +237,18 @@ function beginReactionWrapper(reaction) {
break;
case ("point"):
deleteOldReticles();
pointAtTarget = MyAvatar.getHeadLookAt();
if (!mouseMoveEventsConnected) {
Controller.mouseMoveEvent.connect(mouseMoveEvent);
mouseMoveEventsConnected = true;
}
if (!targetPointInterpolateConnected) {
Script.update.connect(targetPointInterpolate);
targetPointInterpolateConnected = true;
}
}
}
// Checks to see if there are any reticle entities already to delete
function deleteOldReticles() {
MyAvatar.getAvatarEntitiesVariant()
@ -250,6 +264,8 @@ function deleteOldReticles() {
var MAX_INTERSECTION_DISTANCE_M = 50;
var reticleUpdateRateLimiterTimer = false;
var RETICLE_UPDATE_RATE_LIMITER_TIMER_MS = 75;
var POINT_AT_MIX_ALPHA = 0.15;
var reticlePosition = Vec3.ZERO;
function mouseMoveEvent(event) {
if (!reticleUpdateRateLimiterTimer) {
reticleUpdateRateLimiterTimer = Script.setTimeout(function() {
@ -261,11 +277,10 @@ function mouseMoveEvent(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
var avatarIntersectionData = AvatarManager.findRayIntersection(pickRay);
var avatarIntersectionData = AvatarManager.findRayIntersection(pickRay, [], [MyAvatar.sessionUUID], false);
var entityIntersectionData = Entities.findRayIntersection(pickRay, true);
var avatarIntersectionDistanceM = avatarIntersectionData.intersects && avatarIntersectionData.distance < MAX_INTERSECTION_DISTANCE_M ? avatarIntersectionData.distance : null;
var entityIntersectionDistanceM = entityIntersectionData.intersects && entityIntersectionData.distance < MAX_INTERSECTION_DISTANCE_M ? entityIntersectionData.distance : null;
var reticlePosition;
if (avatarIntersectionDistanceM && entityIntersectionDistanceM) {
if (avatarIntersectionDistanceM < entityIntersectionDistanceM) {
@ -283,7 +298,7 @@ function mouseMoveEvent(event) {
}
if (pointReticle && reticlePosition) {
Entities.editEntity(pointReticle, { position: reticlePosition });
Entities.editEntity(pointReticle, { position: reticlePosition, visible: isReticleVisible });
} else if (reticlePosition) {
pointReticle = Entities.addEntity({
type: "Box",
@ -349,6 +364,10 @@ function endReactionWrapper(reaction) {
Controller.mouseMoveEvent.disconnect(mouseMoveEvent);
mouseMoveEventsConnected = false;
}
if (targetPointInterpolateConnected) {
Script.update.disconnect(targetPointInterpolate);
targetPointInterpolateConnected = false;
}
maybeClearReticleUpdateLimiterTimeout();
deleteOldReticles();
break;
@ -757,7 +776,6 @@ function toggleEmojiApp() {
emojiAPI.registerAvimojiQMLWindow(emojiAppWindow);
}
// #endregion
// *************************************
// END EMOJI_MAIN

222
tools/ci-scripts/notarize Executable file
View file

@ -0,0 +1,222 @@
#!/bin/bash
set -e
# Sign, Notarize, and Staple a notarization ticket to the app.
function usage {
echo "usage: $0 datt bundle_path [entitlements]"
exit -1
}
apple_id=""
developer_id_key_label="Mac Developer ID Application: High Fidelity Inc"
k_password_label="com.highfidelity.mac-signing-appleid"
k_password_service="AC_PASSWORD"
# Check for supported Mac OS Version (10.14 or greater)
if [ ! -x /usr/bin/sw_vers ]; then
echo "Can't find sw_vers, is this Mac OS?"
exit -1
fi
set +e
/usr/bin/sw_vers -productVersion | \
awk -F'.' '{ if ($1 == 10 && $2 >= 14) { exit 0 } else { exit 1 } }'
is_supported=$?
set -e
if [[ $is_supported -eq 1 ]]; then
echo "Must use Mac OS 10.14 or greater"
exit -1
fi
# Check for Xcode
if [ ! -x /usr/bin/xcode-select ]; then
echo "Xcode is not installed"
exit -1
fi
function set_apple_id {
if [[ "$apple_id" != "" ]]; then return; fi # already set
apple_id=$(security find-generic-password \
-l $k_password_label -s $k_password_service | \
grep acct | awk -F'=' '{ print $2 }' | tr -d '"')
if [ "$apple_id" == "" ]; then
echo "couldn't find Apple ID in keychain"
exit -1
fi
}
function ensure_developer_id_key_is_present {
set +e
security find-key -l "$developer_id_key_label" > /dev/null 2>&1
success=$?
set -e
if [[ $success == 1 ]]; then
echo "The Developer ID Key is not in your Keychain: $developer_id_key_label"
exit -1
fi
}
function sign {
bundle_path="$1"
entitlements="$2"
ensure_developer_id_key_is_present
if [ "$entitlements" != "" ]; then
codesign --force \
--deep \
--sign "Developer ID Application: High Fidelity Inc" \
--timestamp \
--options runtime \
--entitlements "$entitlements" \
"$bundle_path"
else
codesign --force \
--deep \
--sign "Developer ID Application: High Fidelity Inc" \
--timestamp \
--options runtime \
"$bundle_path"
fi
}
# The `notarize` function sets `result_uuid` on return.
result_uuid=""
function notarize {
local bundle_path=$1
local bundle_id=$(/usr/libexec/PlistBuddy -c Print:CFBundleIdentifier \
"$bundle_path"/Contents/Info.plist)
if [ "$bundle_id" == "" ]; then
echo "couldn't find bundle id in bundle's Info.plist: $bundle_path"
exit -1
fi
set_apple_id
if [ -e "$bundle_path.zip" ]; then
echo "zip already exists, moving it out of the way"
mv "$bundle_path.zip"{,.$(uuidgen)}
fi
local local_bundle_path=$(basename "$bundle_path")
(cd "$bundle_path/.."; \
zip -r --symlinks "$local_bundle_path.zip" "$local_bundle_path" \
>/dev/null 2>&1)
# `result_uuid` is intentionally global
result_uuid=$(xcrun altool --notarize-app \
--primary-bundle-id $bundle_id \
--username $apple_id --password "@keychain:$k_password_service" \
--file "$bundle_path.zip" 2>&1 | tee /dev/tty | \
awk -F'=' '{ if (/^RequestUUID/) print $2 }' | sed 's/^ //')
}
function is_notarization_complete {
set_apple_id
set +e
local status_code=$(xcrun altool --notarization-info $result_uuid \
--username $apple_id --password "@keychain:$k_password_service" 2>&1 | \
awk '{ if (/Status Code/) print $3 }')
set -e
# Status Code is only set when a job is complete.
if [[ "$status_code" -ne "" ]]; then
return 1
fi
return $status_code
}
command=$1
case $command in
# Utility to add your Apple ID to the keychain
add_password)
echo -n "Apple ID: "
read appleid_name
if [ "$appleid_name" == "" ]; then
echo "Apple ID was empty!"
exit -1
fi
security add-generic-password \
-a "$appleid_name" \
-s "$k_password_service" \
-l "$k_password_label" \
-j "Apple ID used for High Fidelity's code signing" \
-w
;;
# Sign a bundle with the Developer ID Application cert
sign)
bundle_path=$2
if [ "$bundle_path" == "" ]; then
echo "usage: $0 $command bundle_path"
exit -1
fi
if [ ! -d "$bundle_path" ]; then
echo "bundle_path doesn't exist"
exit -1
fi
sign "$bundle_path" "$3"
;;
# Submit a bundle to Apple for notarization
notarize)
bundle_path=$2
if [ "$bundle_path" == "" ]; then
echo "usage: $0 $command bundle_path"
exit -1
fi
if [ ! -d "$bundle_path" ]; then
echo "bundle_path doesn't exist"
exit -1
fi
notarize "$bundle_path"
;;
# Check the notarization logs
logs)
result_uuid=$2
if [ "$result_uuid" == "" ]; then
echo "usage: $0 $command result_uuid"
exit -1
fi
set_apple_id
xcrun altool --notarization-info $result_uuid \
--username $apple_id --password "@keychain:$k_password_service"
;;
# Do all the things.
datt)
bundle_path=$2
if [ "$bundle_path" == "" ]; then
echo "usage: $0 bundle_path [entitlements]"
exit -1
fi
if [ ! -d "$bundle_path" ]; then
echo "bundle_path doesn't exist"
exit -1
fi
sign "$bundle_path" "$3"
notarize "$bundle_path"
# TODO: is_notarization_complete needs to recognize notarization
# while :; do
# echo "sleeping..."
# sleep 27
# is_notarization_complete
# [[ $? == 1 ]] && break
# done
# xcrun stapler staple "$bundle_path"
;;
*)
usage
;;
esac

View file

@ -51,6 +51,12 @@ void RenderThread::initialize(QWindow* window) {
_backend = _gpuContext->getBackend();
_context.doneCurrent();
_context.moveToThread(_thread);
if (!_presentPipeline) {
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
_presentPipeline = gpu::Pipeline::create(program, state);
}
#else
auto size = window->size();
_extent = vk::Extent2D{ (uint32_t)size.width(), (uint32_t)size.height() };
@ -169,15 +175,28 @@ void RenderThread::renderFrame(gpu::FramePointer& frame) {
}
#ifdef USE_GL
static gpu::BatchPointer batch = nullptr;
if (!batch) {
batch = std::make_shared<gpu::Batch>();
batch->setPipeline(_presentPipeline);
batch->setFramebuffer(nullptr);
batch->setResourceTexture(0, frame->framebuffer->getRenderBuffer(0));
batch->setViewportTransform(ivec4(uvec2(0), ivec2(windowSize.width(), windowSize.height())));
batch->draw(gpu::TRIANGLE_STRIP, 4);
}
glDisable(GL_FRAMEBUFFER_SRGB);
_gpuContext->executeBatch(*batch);
// Keep this raw gl code here for reference
//glDisable(GL_FRAMEBUFFER_SRGB);
//glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
/* glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(
0, 0, fboSize.x, fboSize.y,
0, 0, windowSize.width(), windowSize.height(),
GL_COLOR_BUFFER_BIT, GL_NEAREST);
*/
(void)CHECK_GL_ERROR();
_context.swapBuffers();
_context.doneCurrent();

View file

@ -57,7 +57,7 @@ public:
uint32_t _externalTexture{ 0 };
void move(const glm::vec3& v);
glm::mat4 _correction;
gpu::PipelinePointer _presentPipeline;
void resize(const QSize& newSize);
void setup() override;

View file

@ -35,7 +35,7 @@
function onClicked() {
if (!shown) {
tablet.gotoWebScreen(APP_URL, "", true);
tablet.gotoWebScreen(APP_URL, "");
} else {
tablet.gotoHomeScreen();
}