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

# Conflicts:
#	libraries/entities-renderer/src/RenderableZoneEntityItem.cpp
This commit is contained in:
Nissim Hadar 2018-02-12 17:50:39 -08:00
commit 34aab4a9c8
205 changed files with 5970 additions and 5271 deletions

5
.gitignore vendored
View file

@ -85,4 +85,7 @@ npm-debug.log
android/app/src/main/assets
# Resource binary file
interface/compiledResources
interface/compiledResources
# GPUCache
interface/resources/GPUCache/*

View file

@ -13,6 +13,11 @@ include("cmake/init.cmake")
include("cmake/compiler.cmake")
if (BUILD_SCRIBE_ONLY)
add_subdirectory(tools/scribe)
return()
endif()
if (NOT DEFINED SERVER_ONLY)
set(SERVER_ONLY 0)
endif()

View file

@ -1,6 +1,6 @@
set(TARGET_NAME native-lib)
setup_hifi_library()
link_hifi_libraries(shared networking gl gpu image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND})
link_hifi_libraries(shared networking gl gpu qml image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND})
target_opengl()
target_bullet()

View file

@ -18,7 +18,7 @@ android {
'-DANDROID_TOOLCHAIN=clang',
'-DANDROID_STL=c++_shared',
'-DQT_CMAKE_PREFIX_PATH=' + HIFI_ANDROID_PRECOMPILED + '/qt/lib/cmake',
'-DNATIVE_SCRIBE=' + HIFI_ANDROID_PRECOMPILED + '/scribe',
'-DNATIVE_SCRIBE=' + HIFI_ANDROID_PRECOMPILED + '/scribe' + EXEC_SUFFIX,
'-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED,
'-DRELEASE_NUMBER=' + RELEASE_NUMBER,
'-DRELEASE_TYPE=' + RELEASE_TYPE,

View file

@ -137,13 +137,16 @@ def packages = [
def scribeLocalFile='scribe' + EXEC_SUFFIX
def scribeFile='scribe_linux_x86_64'
def scribeChecksum='c98678d9726bd8bbf1bab792acf3ff6c'
def scribeChecksum='ca4b904f52f4f993c29175ba96798fa6'
def scribeVersion='wgpf4dB2Ltzg4Lb2jJ4nPFsHoDkmK_OO'
if (Os.isFamily(Os.FAMILY_MAC)) {
scribeFile = 'scribe_osx_x86_64'
scribeChecksum='a137ad62c1bf7cca739da219544a9a16'
scribeChecksum='72db9d32d4e1e50add755570ac5eb749'
scribeVersion='o_NbPrktzEYtBkQf3Tn7zc1nZWzM52w6'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
scribeFile = 'scribe_win32_x86_64.exe'
scribeChecksum='75c2ce9ed45d17de375e3988bfaba816'
scribeChecksum='678e43d290c90fda670c6fefe038a06d'
scribeVersion='GCCJxlmd2irvNOFWfZR0U1UCLHndHQrC'
}
def options = [
@ -398,7 +401,7 @@ task copyDependencies(dependsOn: [ extractDependencies ]) {
}
task downloadScribe(type: Download) {
src baseUrl + scribeFile
src baseUrl + scribeFile + '?versionId=' + scribeVersion
dest new File(baseFolder, scribeLocalFile)
onlyIfNewer true
}

View file

@ -1,6 +1,6 @@
set(TARGET_NAME assignment-client)
setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets)
setup_hifi_project(Core Gui Network Script Quick WebSockets)
# Fix up the rpath so macdeployqt works
if (APPLE)

View file

@ -129,6 +129,12 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
AvatarAudioStream* listenerAudioStream = static_cast<AudioMixerClientData*>(listener->getLinkedData())->getAvatarAudioStream();
AudioMixerClientData* listenerData = static_cast<AudioMixerClientData*>(listener->getLinkedData());
// if we received an invalid position from this listener, then refuse to make them a mix
// because we don't know how to do it properly
if (!listenerAudioStream->hasValidPosition()) {
return false;
}
// zero out the mix for this listener
memset(_mixSamples, 0, sizeof(_mixSamples));
@ -170,7 +176,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
auto nodeID = node->getUUID();
// compute the node's max relative volume
float nodeVolume;
float nodeVolume = 0.0f;
for (auto& streamPair : nodeData->getAudioStreams()) {
auto nodeStream = streamPair.second;
@ -187,10 +193,8 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
}
// max-heapify the nodes by relative volume
throttledNodes.push_back(std::make_pair(nodeVolume, node));
if (!throttledNodes.empty()) {
std::push_heap(throttledNodes.begin(), throttledNodes.end());
}
throttledNodes.push_back({ nodeVolume, node });
std::push_heap(throttledNodes.begin(), throttledNodes.end());
}
}
});
@ -244,12 +248,18 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& listener) {
void AudioMixerSlave::throttleStream(AudioMixerClientData& listenerNodeData, const QUuid& sourceNodeID,
const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd) {
addStream(listenerNodeData, sourceNodeID, listeningNodeStream, streamToAdd, true);
// only throttle this stream to the mix if it has a valid position, we won't know how to mix it otherwise
if (streamToAdd.hasValidPosition()) {
addStream(listenerNodeData, sourceNodeID, listeningNodeStream, streamToAdd, true);
}
}
void AudioMixerSlave::mixStream(AudioMixerClientData& listenerNodeData, const QUuid& sourceNodeID,
const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd) {
addStream(listenerNodeData, sourceNodeID, listeningNodeStream, streamToAdd, false);
// only add the stream to the mix if it has a valid position, we won't know how to mix it otherwise
if (streamToAdd.hasValidPosition()) {
addStream(listenerNodeData, sourceNodeID, listeningNodeStream, streamToAdd, false);
}
}
void AudioMixerSlave::addStream(AudioMixerClientData& listenerNodeData, const QUuid& sourceNodeID,

View file

@ -39,56 +39,41 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
get_filename_component(SHADER_TARGET ${SHADER_FILE} NAME_WE)
get_filename_component(SHADER_EXT ${SHADER_FILE} EXT)
if(SHADER_EXT STREQUAL .slv)
set(SHADER_TARGET ${SHADER_TARGET}_vert.h)
set(SHADER_TYPE vert)
elseif(${SHADER_EXT} STREQUAL .slf)
set(SHADER_TARGET ${SHADER_TARGET}_frag.h)
set(SHADER_TYPE frag)
elseif(${SHADER_EXT} STREQUAL .slg)
set(SHADER_TARGET ${SHADER_TARGET}_geom.h)
set(SHADER_TYPE geom)
endif()
set(SHADER_TARGET ${SHADER_TARGET}_${SHADER_TYPE})
set(SHADER_TARGET "${SHADERS_DIR}/${SHADER_TARGET}")
set(SHADER_TARGET_HEADER ${SHADER_TARGET}.h)
set(SHADER_TARGET_SOURCE ${SHADER_TARGET}.cpp)
set(SCRIBE_COMMAND scribe)
# Target dependant Custom rule on the SHADER_FILE
if (APPLE)
set(GLPROFILE MAC_GL)
set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE})
elseif (ANDROID)
set(GLPROFILE LINUX_GL)
set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
# for an android build, we can't use the scribe that cmake would normally produce as a target,
# since it's unrunnable by the cross-compiling build machine
# so, we require the compiling user to point us at a compiled executable version for their native toolchain
if (NOT NATIVE_SCRIBE)
find_program(NATIVE_SCRIBE scribe PATHS ${SCRIBE_PATH} ENV SCRIBE_PATH)
endif()
if (NOT NATIVE_SCRIBE)
message(FATAL_ERROR "The High Fidelity scribe tool is required for shader pre-processing. \
Please compile scribe using your native toolchain and set SCRIBE_PATH to the path containing the scribe executable in your ENV.\
")
endif ()
add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND ${NATIVE_SCRIBE} ${SCRIBE_ARGS} DEPENDS ${SHADER_INCLUDE_FILES} ${SHADER_FILE})
set(SCRIBE_COMMAND ${NATIVE_SCRIBE})
elseif (UNIX)
set(GLPROFILE LINUX_GL)
set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE})
else ()
set(GLPROFILE PC_GL)
set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE})
endif()
set(SCRIBE_ARGS -c++ -T ${SHADER_TYPE} -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
add_custom_command(
OUTPUT ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE}
COMMAND ${SCRIBE_COMMAND} ${SCRIBE_ARGS}
DEPENDS ${SCRIBE_COMMAND} ${SHADER_INCLUDE_FILES} ${SHADER_FILE}
)
#output the generated file name
set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET} PARENT_SCOPE)
set(AUTOSCRIBE_SHADER_RETURN ${SHADER_TARGET_HEADER} ${SHADER_TARGET_SOURCE} PARENT_SCOPE)
file(GLOB INCLUDE_FILES ${SHADER_TARGET})
file(GLOB INCLUDE_FILES ${SHADER_TARGET_HEADER})
endfunction()
@ -126,7 +111,7 @@ macro(AUTOSCRIBE_SHADER_LIB)
if (WIN32)
source_group("Shaders" FILES ${SHADER_INCLUDE_FILES})
source_group("Shaders" FILES ${SHADER_SOURCE_FILES})
source_group("Shaders" FILES ${AUTOSCRIBE_SHADER_SRC})
source_group("Shaders\\generated" FILES ${AUTOSCRIBE_SHADER_SRC})
endif()
list(APPEND AUTOSCRIBE_SHADER_LIB_SRC ${SHADER_INCLUDE_FILES})
@ -136,4 +121,7 @@ macro(AUTOSCRIBE_SHADER_LIB)
# Link library shaders, if they exist
include_directories("${SHADERS_DIR}")
# Add search directory to find gpu/Shader.h
include_directories("${HIFI_LIBRARY_DIR}/gpu/src")
endmacro()

View file

@ -161,7 +161,7 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
} else if (_server->_settingsManager.hasPermissionsForMachineFingerprint(machineFingerprint)) {
userPerms = _server->_settingsManager.getPermissionsForMachineFingerprint(machineFingerprint);
#ifdef WANT_DEBUG
qDebug(() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms;
qDebug() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms;
#endif
} else if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) {
// this user comes from an IP we have in our permissions table, apply those permissions
@ -187,7 +187,7 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
} else if (_server->_settingsManager.hasPermissionsForMachineFingerprint(machineFingerprint)) {
userPerms = _server->_settingsManager.getPermissionsForMachineFingerprint(machineFingerprint);
#ifdef WANT_DEBUG
qDebug(() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms;
qDebug() << "| user-permissions: specific Machine Fingerprint matches, so: " << userPerms;
#endif
} else if (_server->_settingsManager.hasPermissionsForIP(senderAddress)) {
// this user comes from an IP we have in our permissions table, apply those permissions
@ -393,9 +393,12 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
QString verifiedUsername; // if this remains empty, consider this an anonymous connection attempt
if (!username.isEmpty()) {
if (usernameSignature.isEmpty()) {
const QUuid& connectionToken = _connectionTokenHash.value(username.toLower());
if (usernameSignature.isEmpty() || connectionToken.isNull()) {
// user is attempting to prove their identity to us, but we don't have enough information
sendConnectionTokenPacket(username, nodeConnection.senderSockAddr);
// ask for their public key right now to make sure we have it
requestUserPublicKey(username, true);
getGroupMemberships(username); // optimistically get started on group memberships

View file

@ -17,9 +17,9 @@ if (ANDROID)
set(BUILD_SHARED_LIBS ON)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}")
setup_hifi_library(Gui Widgets AndroidExtras)
setup_hifi_library(Gui AndroidExtras)
else ()
setup_hifi_project(Gui Widgets)
setup_hifi_project(Gui)
endif ()
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})

View file

@ -2,7 +2,7 @@ set(TARGET_NAME interface)
project(${TARGET_NAME})
file(GLOB_RECURSE QML_SRC resources/qml/*.qml resources/qml/*.js)
add_custom_target(qml SOURCES ${QML_SRC})
add_custom_target(qmls SOURCES ${QML_SRC})
GroupSources("resources/qml")
function(JOIN VALUES GLUE OUTPUT)
@ -78,7 +78,7 @@ endif ()
find_package(
Qt5 COMPONENTS
Gui Multimedia Network OpenGL Qml Quick Script Svg
Gui Widgets Multimedia Network Qml Quick Script Svg
${PLATFORM_QT_COMPONENTS}
WebChannel WebSockets
)
@ -208,7 +208,7 @@ link_hifi_libraries(
pointers
recording fbx networking model-networking entities avatars trackers
audio audio-client animation script-engine physics
render-utils entities-renderer avatars-renderer ui auto-updater midi
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
controllers plugins image trackers
ui-plugins display-plugins input-plugins
${PLATFORM_GL_BACKEND}
@ -269,7 +269,7 @@ endif ()
target_link_libraries(
${TARGET_NAME}
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::Widgets
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
Qt5::WebChannel
${PLATFORM_QT_LIBRARIES}

View file

@ -1,96 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 50 50"
style="enable-background:new 0 0 50 50;"
xml:space="preserve"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="bubble-a.svg"><metadata
id="metadata36"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs34" /><sodipodi:namedview
pagecolor="#ff4900"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1149"
inkscape:window-height="801"
id="namedview32"
showgrid="false"
inkscape:zoom="4.72"
inkscape:cx="25"
inkscape:cy="25"
inkscape:window-x="485"
inkscape:window-y="514"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" /><style
type="text/css"
id="style4">
.st0{fill:#FFFFFF;}
</style><g
id="Layer_2" /><g
id="Layer_1"
style="fill:#000000;fill-opacity:1"><g
id="g8"
style="fill:#000000;fill-opacity:1"><path
class="st0"
d="M23.2,24.1c-0.8,0.9-1.5,1.8-2.2,2.6c-0.1,0.2-0.1,0.5-0.1,0.7c0.1,1.7,0.2,3.4,0.2,5.1 c0,0.8-0.4,1.2-1.1,1.3c-0.7,0.1-1.3-0.4-1.4-1.1c-0.2-2.2-0.3-4.3-0.5-6.5c0-0.3,0.1-0.7,0.4-1c1.1-1.5,2.3-3,3.4-4.5 c0.6-0.7,1.6-1.6,2.6-1.6c0.3,0,1.1,0,1.4,0c0.8-0.1,1.3,0.1,1.9,0.9c1,1.2,1.5,2.3,2.4,3.6c0.7,1.1,1.4,1.6,2.9,1.9 c1.1,0.2,2.2,0.5,3.3,0.8c0.3,0.1,0.6,0.2,0.8,0.3c0.5,0.3,0.7,0.8,0.6,1.3c-0.1,0.5-0.5,0.7-1,0.8c-0.4,0-0.9,0-1.3-0.1 c-1.4-0.3-2.7-0.6-4.1-0.9c-0.8-0.2-1.5-0.6-2.1-1.1c-0.3-0.3-0.6-0.5-0.9-0.8c0,0.3,0,0.5,0,0.7c0,1.2,0,2.4,0,3.6 c0,0.4-0.3,12.6-0.1,16.8c0,0.5-0.1,1-0.2,1.5c-0.2,0.7-0.6,1-1.4,1.1c-0.8,0-1.4-0.3-1.7-1c-0.2-0.5-0.3-1.1-0.4-1.6 c-0.4-4.6-0.9-12.9-1.1-13.8c-0.1-0.8-0.2-1.1-0.3-2.1c-0.1-0.5-0.1-0.9-0.1-1.3C23.3,27.9,23.2,26.1,23.2,24.1z"
id="path10"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M28.2,14.6c0,1.4-1.1,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.1-2.6-2.6v-1.6c0-1.4,1.1-2.6,2.6-2.6l0,0 c1.4,0,2.6,1.1,2.6,2.6V14.6z"
id="path12"
style="fill:#000000;fill-opacity:1" /></g><path
class="st0"
d="M8.4,38.9c2.8,3.2,6.4,5.5,10.5,6.7c0.6,0.2,1.3,0.1,1.7-0.3c0.4-0.3,0.6-0.6,0.7-1c0.2-0.5,0.1-1.1-0.2-1.5 c-0.3-0.5-0.7-0.8-1.2-1c-1.6-0.5-3.2-1.2-4.6-2.1c-1.5-0.9-2.8-2.1-4-3.4c-0.4-0.4-0.9-0.7-1.5-0.7c-0.5,0-1,0.2-1.3,0.5 c-0.4,0.4-0.6,0.8-0.7,1.4C7.8,38,8,38.5,8.4,38.9z"
id="path14"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M43.7,36.8c0.3-0.4,0.4-1,0.3-1.5c-0.1-0.5-0.4-1-0.8-1.3c-0.3-0.2-0.7-0.3-1.1-0.3c-0.7,0-1.3,0.3-1.7,0.9 c-1.1,1.6-2.4,3-4,4.2c-1.2,0.9-2.5,1.7-3.9,2.3c-0.5,0.2-0.9,0.6-1.1,1.1c-0.2,0.5-0.2,1,0,1.5c0.4,1,1.6,1.5,2.6,1 c1.7-0.7,3.3-1.7,4.8-2.8C40.7,40.4,42.4,38.7,43.7,36.8z"
id="path16"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M5.1,33.2c0.5,0.4,1.2,0.4,1.8,0.2c0.5-0.2,0.9-0.6,1.1-1.1c0.2-0.5,0.2-1,0-1.5c-0.1-0.4-0.4-0.7-0.7-0.9 c-0.3-0.2-0.7-0.3-1.1-0.3c-0.2,0-0.5,0-0.7,0.1c-1,0.4-1.5,1.6-1.1,2.6C4.5,32.7,4.7,33,5.1,33.2z"
id="path18"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M45.4,27.3c-0.2,0-0.3-0.1-0.5-0.1c-0.9,0-1.7,0.6-1.9,1.5c-0.1,0.5-0.1,1.1,0.2,1.5c0.3,0.5,0.7,0.8,1.2,0.9 c0.2,0,0.3,0.1,0.5,0.1c0.9,0,1.7-0.6,1.9-1.5c0.1-0.5,0.1-1.1-0.2-1.5C46.4,27.8,45.9,27.5,45.4,27.3z"
id="path20"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M8.6,12c-0.3-0.2-0.7-0.3-1-0.3c-0.3,0-0.7,0.1-1,0.3c-0.3,0.2-0.6,0.4-0.7,0.7c-2,3.5-3.1,7.4-3.1,11.4 c0,0.2,0,0.4,0,0.6c0,0.5,0.2,1,0.6,1.4c0.4,0.4,0.9,0.6,1.4,0.6v0.4l0.1-0.4c0.5,0,1-0.2,1.4-0.6c0.4-0.4,0.6-0.9,0.5-1.4 c0-0.2,0-0.4,0-0.5c0-3.3,0.9-6.6,2.6-9.4C9.9,13.8,9.6,12.6,8.6,12z"
id="path22"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M39.3,11.4c-0.1,0.5,0.1,1.1,0.4,1.5c1.1,1.4,2,3,2.6,4.6c0.6,1.6,1,3.2,1.2,4.9c0,0.5,0.3,1,0.6,1.3 c0.4,0.4,1,0.6,1.5,0.5c0.5,0,1-0.3,1.4-0.7c0.3-0.4,0.5-0.9,0.5-1.5c-0.4-4.2-2-8.2-4.6-11.6c-0.4-0.5-1-0.8-1.6-0.8 c-0.4,0-0.9,0.1-1.2,0.4C39.7,10.4,39.4,10.8,39.3,11.4z"
id="path24"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M12.2,6.3c-0.5,0-0.9,0.2-1.3,0.5c-0.4,0.3-0.7,0.8-0.7,1.4c-0.1,0.5,0.1,1.1,0.4,1.5c0.7,0.8,2,1,2.8,0.3 c0.4-0.3,0.7-0.8,0.7-1.3c0.1-0.5-0.1-1.1-0.4-1.5C13.4,6.6,12.8,6.3,12.2,6.3z"
id="path26"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M37.2,5.2c-0.3-0.2-0.7-0.3-1.1-0.3c-0.7,0-1.3,0.3-1.7,0.9c-0.3,0.4-0.4,1-0.3,1.5c0.1,0.5,0.4,1,0.9,1.3 C36,9.2,37.3,8.9,37.9,8C38.4,7.1,38.2,5.8,37.2,5.2z"
id="path28"
style="fill:#000000;fill-opacity:1" /><path
class="st0"
d="M16.5,4c-0.2,0.5-0.3,1-0.1,1.5c0.4,1,1.5,1.6,2.6,1.2c3.3-1.2,6.8-1.4,10.2-0.6c0.6,0.1,1.2,0,1.7-0.4 c0.4-0.3,0.6-0.7,0.7-1.1c0.1-0.5,0-1.1-0.3-1.5c-0.3-0.5-0.7-0.8-1.3-0.9c-1.6-0.4-3.3-0.5-4.9-0.5c-2.6,0-5.1,0.4-7.5,1.3 C17.1,3.2,16.7,3.6,16.5,4z"
id="path30"
style="fill:#000000;fill-opacity:1" /></g></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<g>
<ellipse cx="26.1" cy="18.8" rx="4.6" ry="4.5"/>
<path d="M29.6,25.8h-6.7c-2.8,0-5,2.3-5,5v2c2,2.9,7.4,6.8,8.5,6.8c1.3,0,6.3-4,8.1-6.9v-1.9C34.5,28,32.4,25.8,29.6,25.8z"/>
<path d="M25.8,5.4C32.5,8,35.9,8.6,38.9,9.3C40,9.5,41,9.7,42.1,9.9c-0.3,4.9-0.3,9.8-0.3,12.5c0,8.7-3.6,15.3-11.3,20.4
c-1.2,0.8-2.7,1.5-4.2,2.3c-0.1,0.1-0.2,0.1-0.3,0.2c-3.7-1.7-6.8-3.8-9.2-6.3c-2.7-2.8-4.6-6.1-5.7-10.1c-0.5-1.7-0.7-3.6-0.8-6.3
c-0.1-3.6-0.3-7.8-0.7-12.9C11,9.5,12.2,9.3,13.5,9C16.6,8.3,20.1,7.6,25.8,5.4 M25.8,1.5c-0.2,0-0.4,0-0.6,0.1
C15.7,5.4,12.6,4.9,6.6,6.8C6,7,5.8,7.3,5.8,7.9c0.4,5,0.7,10.2,0.8,14.9c0.1,2.4,0.3,4.9,0.9,7.2c2.6,9.4,9,15.4,17.9,19.2
c0.2,0.1,0.4,0.1,0.6,0.1c0.2,0,0.5,0,0.7-0.1c2-1,4-1.9,5.8-3.1c8.4-5.7,13-13.4,12.9-23.6c0-3.8,0.1-9.8,0.5-15.2
c-0.2-0.1-0.3-0.2-0.5-0.2c-6.2-2-8.1-1.1-19.3-5.5C26.1,1.5,26,1.5,25.8,1.5L25.8,1.5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -1,46 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<g>
<path class="st0" d="M23.2,24.1c-0.8,0.9-1.5,1.8-2.2,2.6c-0.1,0.2-0.1,0.5-0.1,0.7c0.1,1.7,0.2,3.4,0.2,5.1
c0,0.8-0.4,1.2-1.1,1.3c-0.7,0.1-1.3-0.4-1.4-1.1c-0.2-2.2-0.3-4.3-0.5-6.5c0-0.3,0.1-0.7,0.4-1c1.1-1.5,2.3-3,3.4-4.5
c0.6-0.7,1.6-1.6,2.6-1.6c0.3,0,1.1,0,1.4,0c0.8-0.1,1.3,0.1,1.9,0.9c1,1.2,1.5,2.3,2.4,3.6c0.7,1.1,1.4,1.6,2.9,1.9
c1.1,0.2,2.2,0.5,3.3,0.8c0.3,0.1,0.6,0.2,0.8,0.3c0.5,0.3,0.7,0.8,0.6,1.3c-0.1,0.5-0.5,0.7-1,0.8c-0.4,0-0.9,0-1.3-0.1
c-1.4-0.3-2.7-0.6-4.1-0.9c-0.8-0.2-1.5-0.6-2.1-1.1c-0.3-0.3-0.6-0.5-0.9-0.8c0,0.3,0,0.5,0,0.7c0,1.2,0,2.4,0,3.6
c0,0.4-0.3,12.6-0.1,16.8c0,0.5-0.1,1-0.2,1.5c-0.2,0.7-0.6,1-1.4,1.1c-0.8,0-1.4-0.3-1.7-1c-0.2-0.5-0.3-1.1-0.4-1.6
c-0.4-4.6-0.9-12.9-1.1-13.8c-0.1-0.8-0.2-1.1-0.3-2.1c-0.1-0.5-0.1-0.9-0.1-1.3C23.3,27.9,23.2,26.1,23.2,24.1z"/>
<path class="st0" d="M28.2,14.6c0,1.4-1.1,2.6-2.6,2.6l0,0c-1.4,0-2.6-1.1-2.6-2.6v-1.6c0-1.4,1.1-2.6,2.6-2.6l0,0
c1.4,0,2.6,1.1,2.6,2.6V14.6z"/>
</g>
<path class="st0" d="M8.4,38.9c2.8,3.2,6.4,5.5,10.5,6.7c0.6,0.2,1.3,0.1,1.7-0.3c0.4-0.3,0.6-0.6,0.7-1c0.2-0.5,0.1-1.1-0.2-1.5
c-0.3-0.5-0.7-0.8-1.2-1c-1.6-0.5-3.2-1.2-4.6-2.1c-1.5-0.9-2.8-2.1-4-3.4c-0.4-0.4-0.9-0.7-1.5-0.7c-0.5,0-1,0.2-1.3,0.5
c-0.4,0.4-0.6,0.8-0.7,1.4C7.8,38,8,38.5,8.4,38.9z"/>
<path class="st0" d="M43.7,36.8c0.3-0.4,0.4-1,0.3-1.5c-0.1-0.5-0.4-1-0.8-1.3c-0.3-0.2-0.7-0.3-1.1-0.3c-0.7,0-1.3,0.3-1.7,0.9
c-1.1,1.6-2.4,3-4,4.2c-1.2,0.9-2.5,1.7-3.9,2.3c-0.5,0.2-0.9,0.6-1.1,1.1c-0.2,0.5-0.2,1,0,1.5c0.4,1,1.6,1.5,2.6,1
c1.7-0.7,3.3-1.7,4.8-2.8C40.7,40.4,42.4,38.7,43.7,36.8z"/>
<path class="st0" d="M5.1,33.2c0.5,0.4,1.2,0.4,1.8,0.2c0.5-0.2,0.9-0.6,1.1-1.1c0.2-0.5,0.2-1,0-1.5c-0.1-0.4-0.4-0.7-0.7-0.9
c-0.3-0.2-0.7-0.3-1.1-0.3c-0.2,0-0.5,0-0.7,0.1c-1,0.4-1.5,1.6-1.1,2.6C4.5,32.7,4.7,33,5.1,33.2z"/>
<path class="st0" d="M45.4,27.3c-0.2,0-0.3-0.1-0.5-0.1c-0.9,0-1.7,0.6-1.9,1.5c-0.1,0.5-0.1,1.1,0.2,1.5c0.3,0.5,0.7,0.8,1.2,0.9
c0.2,0,0.3,0.1,0.5,0.1c0.9,0,1.7-0.6,1.9-1.5c0.1-0.5,0.1-1.1-0.2-1.5C46.4,27.8,45.9,27.5,45.4,27.3z"/>
<path class="st0" d="M8.6,12c-0.3-0.2-0.7-0.3-1-0.3c-0.3,0-0.7,0.1-1,0.3c-0.3,0.2-0.6,0.4-0.7,0.7c-2,3.5-3.1,7.4-3.1,11.4
c0,0.2,0,0.4,0,0.6c0,0.5,0.2,1,0.6,1.4c0.4,0.4,0.9,0.6,1.4,0.6v0.4l0.1-0.4c0.5,0,1-0.2,1.4-0.6c0.4-0.4,0.6-0.9,0.5-1.4
c0-0.2,0-0.4,0-0.5c0-3.3,0.9-6.6,2.6-9.4C9.9,13.8,9.6,12.6,8.6,12z"/>
<path class="st0" d="M39.3,11.4c-0.1,0.5,0.1,1.1,0.4,1.5c1.1,1.4,2,3,2.6,4.6c0.6,1.6,1,3.2,1.2,4.9c0,0.5,0.3,1,0.6,1.3
c0.4,0.4,1,0.6,1.5,0.5c0.5,0,1-0.3,1.4-0.7c0.3-0.4,0.5-0.9,0.5-1.5c-0.4-4.2-2-8.2-4.6-11.6c-0.4-0.5-1-0.8-1.6-0.8
c-0.4,0-0.9,0.1-1.2,0.4C39.7,10.4,39.4,10.8,39.3,11.4z"/>
<path class="st0" d="M12.2,6.3c-0.5,0-0.9,0.2-1.3,0.5c-0.4,0.3-0.7,0.8-0.7,1.4c-0.1,0.5,0.1,1.1,0.4,1.5c0.7,0.8,2,1,2.8,0.3
c0.4-0.3,0.7-0.8,0.7-1.3c0.1-0.5-0.1-1.1-0.4-1.5C13.4,6.6,12.8,6.3,12.2,6.3z"/>
<path class="st0" d="M37.2,5.2c-0.3-0.2-0.7-0.3-1.1-0.3c-0.7,0-1.3,0.3-1.7,0.9c-0.3,0.4-0.4,1-0.3,1.5c0.1,0.5,0.4,1,0.9,1.3
C36,9.2,37.3,8.9,37.9,8C38.4,7.1,38.2,5.8,37.2,5.2z"/>
<path class="st0" d="M16.5,4c-0.2,0.5-0.3,1-0.1,1.5c0.4,1,1.5,1.6,2.6,1.2c3.3-1.2,6.8-1.4,10.2-0.6c0.6,0.1,1.2,0,1.7-0.4
c0.4-0.3,0.6-0.7,0.7-1.1c0.1-0.5,0-1.1-0.3-1.5c-0.3-0.5-0.7-0.8-1.3-0.9c-1.6-0.4-3.3-0.5-4.9-0.5c-2.6,0-5.1,0.4-7.5,1.3
C17.1,3.2,16.7,3.6,16.5,4z"/>
<g>
<ellipse class="st0" cx="26.1" cy="18.8" rx="4.6" ry="4.5"/>
<path class="st0" d="M29.6,25.8h-6.7c-2.8,0-5,2.3-5,5v2c2,2.9,7.4,6.8,8.5,6.8c1.3,0,6.3-4,8.1-6.9v-1.9
C34.5,28,32.4,25.8,29.6,25.8z"/>
<path class="st0" d="M25.8,5.4C32.5,8,35.9,8.6,38.9,9.3C40,9.5,41,9.7,42.1,9.9c-0.3,4.9-0.3,9.8-0.3,12.5
c0,8.7-3.6,15.3-11.3,20.4c-1.2,0.8-2.7,1.5-4.2,2.3c-0.1,0.1-0.2,0.1-0.3,0.2c-3.7-1.7-6.8-3.8-9.2-6.3c-2.7-2.8-4.6-6.1-5.7-10.1
c-0.5-1.7-0.7-3.6-0.8-6.3c-0.1-3.6-0.3-7.8-0.7-12.9C11,9.5,12.2,9.3,13.5,9C16.6,8.3,20.1,7.6,25.8,5.4 M25.8,1.5
c-0.2,0-0.4,0-0.6,0.1C15.7,5.4,12.6,4.9,6.6,6.8C6,7,5.8,7.3,5.8,7.9c0.4,5,0.7,10.2,0.8,14.9c0.1,2.4,0.3,4.9,0.9,7.2
c2.6,9.4,9,15.4,17.9,19.2c0.2,0.1,0.4,0.1,0.6,0.1c0.2,0,0.5,0,0.7-0.1c2-1,4-1.9,5.8-3.1c8.4-5.7,13-13.4,12.9-23.6
c0-3.8,0.1-9.8,0.5-15.2c-0.2-0.1-0.3-0.2-0.5-0.2c-6.2-2-8.1-1.1-19.3-5.5C26.1,1.5,26,1.5,25.8,1.5L25.8,1.5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -19,21 +19,31 @@ import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../wallet" as HifiWallet
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
property string marketplaceUrl;
property string certificateId;
property string marketplaceUrl: "";
property string entityId: "";
property string certificateId: "";
property string itemName: "--";
property string itemOwner: "--";
property string itemEdition: "--";
property string dateOfPurchase: "--";
property string itemCost: "--";
property string certTitleTextColor: hifi.colors.darkGray;
property string certTextColor: hifi.colors.white;
property string infoTextColor: hifi.colors.blueAccent;
// 0 means replace none
// 4 means replace all but "Item Edition"
// 5 means replace all 5 replaceable fields
property int certInfoReplaceMode: 5;
property bool isLightbox: false;
property bool isMyCert: false;
property bool isCertificateInvalid: false;
property bool useGoldCert: true;
property bool certificateInfoPending: true;
property int certificateStatus: 0;
property bool certificateStatusPending: true;
// Style
color: hifi.colors.faintGray;
Connections {
@ -45,72 +55,135 @@ Rectangle {
} else {
root.marketplaceUrl = result.data.marketplace_item_url;
root.isMyCert = result.isMyCert ? result.isMyCert : false;
root.itemOwner = root.isCertificateInvalid ? "--" : (root.isMyCert ? Account.username :
"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022");
root.itemEdition = root.isCertificateInvalid ? "Uncertified Copy" :
(result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run));
root.dateOfPurchase = root.isCertificateInvalid ? "" : getFormattedDate(result.data.transfer_created_at * 1000);
root.itemName = result.data.marketplace_item_name;
if (root.certInfoReplaceMode > 3) {
root.itemName = result.data.marketplace_item_name;
// "\u2022" is the Unicode character 'BULLET' - it's what's used in password fields on the web, etc
root.itemOwner = root.isMyCert ? Account.username :
"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
root.dateOfPurchase = root.isMyCert ? getFormattedDate(result.data.transfer_created_at * 1000) : "Undisclosed";
root.itemCost = (root.isMyCert && result.data.cost !== undefined) ? result.data.cost : "Undisclosed";
}
if (root.certInfoReplaceMode > 4) {
root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run);
}
if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
if (root.isMyCert) {
errorText.text = "This item is an uncertified copy of an item you purchased.";
} else {
errorText.text = "The person who placed this item doesn't own it.";
}
}
if (result.data.invalid_reason || result.data.transfer_status[0] === "failed") {
titleBarText.text = "Invalid Certificate";
titleBarText.color = hifi.colors.redHighlight;
root.useGoldCert = false;
root.certTitleTextColor = hifi.colors.redHighlight;
root.certTextColor = hifi.colors.redHighlight;
root.infoTextColor = hifi.colors.redHighlight;
titleBarText.text = "Certificate\nNo Longer Valid";
popText.text = "";
showInMarketplaceButton.visible = false;
// "Edition" text previously set above in this function
// "Owner" text previously set above in this function
// "Purchase Date" text previously set above in this function
// "Purchase Price" text previously set above in this function
if (result.data.invalid_reason) {
errorText.text = result.data.invalid_reason;
}
} else if (result.data.transfer_status[0] === "pending") {
root.useGoldCert = false;
root.certTitleTextColor = hifi.colors.redHighlight;
root.certTextColor = hifi.colors.redHighlight;
root.infoTextColor = hifi.colors.redHighlight;
titleBarText.text = "Certificate Pending";
popText.text = "";
showInMarketplaceButton.visible = true;
// "Edition" text previously set above in this function
// "Owner" text previously set above in this function
// "Purchase Date" text previously set above in this function
// "Purchase Price" text previously set above in this function
errorText.text = "The status of this item is still pending confirmation. If the purchase is not confirmed, " +
"this entity will be cleaned up by the domain.";
errorText.color = hifi.colors.baseGray;
}
}
root.certificateInfoPending = false;
}
onUpdateCertificateStatus: {
if (certStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS
// NOP
} else if (certStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT
root.isCertificateInvalid = true;
errorText.text = "Verification of this certificate timed out.";
errorText.color = hifi.colors.redHighlight;
} else if (certStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED
root.isCertificateInvalid = true;
titleBarText.text = "Invalid Certificate";
titleBarText.color = hifi.colors.redHighlight;
popText.text = "";
root.itemOwner = "";
dateOfPurchaseHeader.text = "";
root.dateOfPurchase = "";
root.itemEdition = "Uncertified Copy";
errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item.";
errorText.color = hifi.colors.baseGray;
} else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
root.isCertificateInvalid = true;
titleBarText.text = "Invalid Certificate";
titleBarText.color = hifi.colors.redHighlight;
popText.text = "";
root.itemOwner = "";
dateOfPurchaseHeader.text = "";
root.dateOfPurchase = "";
root.itemEdition = "Uncertified Copy";
errorText.text = "The avatar who rezzed this item doesn't own it.";
errorText.color = hifi.colors.baseGray;
} else {
console.log("Unknown certificate status received from ledger signal!");
}
updateCertificateStatus(certStatus);
}
}
onCertificateIdChanged: {
if (certificateId !== "") {
Commerce.certificateInfo(certificateId);
function updateCertificateStatus(status) {
root.certificateStatus = status;
if (root.certificateStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS
root.useGoldCert = true;
root.certTitleTextColor = hifi.colors.darkGray;
root.certTextColor = hifi.colors.white;
root.infoTextColor = hifi.colors.blueAccent;
titleBarText.text = "Certificate";
popText.text = "PROOF OF PROVENANCE";
showInMarketplaceButton.visible = true;
root.certInfoReplaceMode = 5;
// "Item Name" text will be set in "onCertificateInfoResult()"
// "Edition" text will be set in "onCertificateInfoResult()"
// "Owner" text will be set in "onCertificateInfoResult()"
// "Purchase Date" text will be set in "onCertificateInfoResult()"
// "Purchase Price" text will be set in "onCertificateInfoResult()"
errorText.text = "";
} else if (root.certificateStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT
root.useGoldCert = false;
root.certTitleTextColor = hifi.colors.redHighlight;
root.certTextColor = hifi.colors.redHighlight;
root.infoTextColor = hifi.colors.redHighlight;
titleBarText.text = "Request Timed Out";
popText.text = "";
showInMarketplaceButton.visible = false;
root.certInfoReplaceMode = 0;
root.itemName = "";
root.itemEdition = "";
root.itemOwner = "";
root.dateOfPurchase = "";
root.itemCost = "";
errorText.text = "Your request to inspect this item timed out. Please try again later.";
} else if (root.certificateStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED
root.useGoldCert = false;
root.certTitleTextColor = hifi.colors.redHighlight;
root.certTextColor = hifi.colors.redHighlight;
root.infoTextColor = hifi.colors.redHighlight;
titleBarText.text = "Certificate\nNo Longer Valid";
popText.text = "";
showInMarketplaceButton.visible = true;
root.certInfoReplaceMode = 5;
// "Item Name" text will be set in "onCertificateInfoResult()"
// "Edition" text will be set in "onCertificateInfoResult()"
// "Owner" text will be set in "onCertificateInfoResult()"
// "Purchase Date" text will be set in "onCertificateInfoResult()"
// "Purchase Price" text will be set in "onCertificateInfoResult()"
errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item.";
} else if (root.certificateStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED
root.useGoldCert = false;
root.certTitleTextColor = hifi.colors.redHighlight;
root.certTextColor = hifi.colors.redHighlight;
root.infoTextColor = hifi.colors.redHighlight;
titleBarText.text = "Invalid Certificate";
popText.text = "";
showInMarketplaceButton.visible = true;
root.certInfoReplaceMode = 4;
// "Item Name" text will be set in "onCertificateInfoResult()"
root.itemEdition = "Uncertified Copy"
// "Owner" text will be set in "onCertificateInfoResult()"
// "Purchase Date" text will be set in "onCertificateInfoResult()"
// "Purchase Price" text will be set in "onCertificateInfoResult()"
// "Error Text" text will be set in "onCertificateInfoResult()"
} else {
console.log("Unknown certificate status received from ledger signal!");
}
root.certificateStatusPending = false;
// We've gotten cert status - we are GO on getting the cert info
Commerce.certificateInfo(root.certificateId);
}
// This object is always used in a popup.
@ -122,9 +195,35 @@ Rectangle {
hoverEnabled: true;
}
Image {
Rectangle {
id: loadingOverlay;
z: 998;
visible: root.certificateInfoPending || root.certificateStatusPending;
anchors.fill: parent;
source: "images/cert-bg.jpg";
color: Qt.rgba(0.0, 0.0, 0.0, 0.7);
// This object is always used in a popup or full-screen Wallet section.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup/section.
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
AnimatedImage {
source: "../common/images/loader.gif"
width: 96;
height: width;
anchors.verticalCenter: parent.verticalCenter;
anchors.horizontalCenter: parent.horizontalCenter;
}
}
Image {
id: backgroundImage;
anchors.fill: parent;
source: root.useGoldCert ? "images/cert-bg-gold-split.png" : "images/nocert-bg-split.png";
}
// Title text
@ -137,16 +236,17 @@ Rectangle {
anchors.top: parent.top;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.leftMargin: 36;
anchors.right: parent.right;
anchors.rightMargin: 8;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
color: root.certTitleTextColor;
wrapMode: Text.WordWrap;
}
// Title text
RalewayRegular {
id: popText;
text: "Proof of Provenance";
// Text size
size: 16;
// Anchors
@ -154,9 +254,39 @@ Rectangle {
anchors.topMargin: 4;
anchors.left: titleBarText.left;
anchors.right: titleBarText.right;
height: paintedHeight;
height: text === "" ? 0 : paintedHeight;
// Style
color: hifi.colors.darkGray;
color: root.certTitleTextColor;
}
// "Close" button
HiFiGlyphs {
z: 999;
id: closeGlyphButton;
text: hifi.glyphs.close;
color: hifi.colors.white;
size: 26;
anchors.top: parent.top;
anchors.topMargin: 10;
anchors.right: parent.right;
anchors.rightMargin: 10;
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: {
parent.text = hifi.glyphs.closeInverted;
}
onExited: {
parent.text = hifi.glyphs.close;
}
onClicked: {
if (root.isLightbox) {
root.visible = false;
} else {
sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases});
}
}
}
}
//
@ -164,11 +294,13 @@ Rectangle {
//
Item {
id: certificateContainer;
anchors.top: popText.bottom;
anchors.topMargin: 30;
anchors.bottom: buttonsContainer.top;
anchors.top: titleBarText.top;
anchors.topMargin: 110;
anchors.bottom: infoContainer.top;
anchors.left: parent.left;
anchors.leftMargin: titleBarText.anchors.leftMargin;
anchors.right: parent.right;
anchors.rightMargin: 24;
RalewayRegular {
id: itemNameHeader;
@ -178,9 +310,7 @@ Rectangle {
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
@ -197,79 +327,30 @@ Rectangle {
anchors.right: itemNameHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.white;
color: root.certTextColor;
elide: Text.ElideRight;
MouseArea {
enabled: showInMarketplaceButton.visible;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
}
onEntered: itemName.color = hifi.colors.blueHighlight;
onExited: itemName.color = hifi.colors.white;
onExited: itemName.color = root.certTextColor;
}
}
RalewayRegular {
id: ownedByHeader;
text: "OWNER";
// Text size
size: 16;
// Anchors
anchors.top: itemName.bottom;
anchors.topMargin: 28;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
}
RalewayRegular {
id: ownedBy;
text: root.itemOwner;
// Text size
size: 22;
// Anchors
anchors.top: ownedByHeader.bottom;
anchors.topMargin: 8;
anchors.left: ownedByHeader.left;
height: paintedHeight;
// Style
color: hifi.colors.white;
elide: Text.ElideRight;
}
AnonymousProRegular {
id: isMyCertText;
visible: root.isMyCert && !root.isCertificateInvalid;
text: "(Private)";
size: 18;
// Anchors
anchors.top: ownedBy.top;
anchors.topMargin: 4;
anchors.bottom: ownedBy.bottom;
anchors.left: ownedBy.right;
anchors.leftMargin: 6;
anchors.right: ownedByHeader.right;
// Style
color: hifi.colors.white;
elide: Text.ElideRight;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: editionHeader;
text: "EDITION";
// Text size
size: 16;
// Anchors
anchors.top: ownedBy.bottom;
anchors.top: itemName.bottom;
anchors.topMargin: 28;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
@ -286,21 +367,117 @@ Rectangle {
anchors.right: editionHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.white;
color: root.certTextColor;
}
// "Show In Marketplace" button
HifiControlsUit.Button {
id: showInMarketplaceButton;
enabled: root.marketplaceUrl;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 48;
anchors.right: parent.right;
width: 200;
height: 40;
text: "View In Market"
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
}
}
}
//
// "CERTIFICATE" END
//
//
// "INFO CONTAINER" START
//
Item {
id: infoContainer;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: titleBarText.anchors.leftMargin;
anchors.right: parent.right;
anchors.rightMargin: 24;
height: root.useGoldCert ? 220 : 372;
RalewayRegular {
id: errorText;
visible: !root.useGoldCert;
// Text size
size: 20;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 36;
anchors.left: parent.left;
anchors.right: parent.right;
height: 116;
// Style
wrapMode: Text.WordWrap;
color: hifi.colors.baseGray;
verticalAlignment: Text.AlignTop;
}
RalewayRegular {
id: ownedByHeader;
text: "OWNER";
// Text size
size: 16;
// Anchors
anchors.top: errorText.visible ? errorText.bottom : parent.top;
anchors.topMargin: 28;
anchors.left: parent.left;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
}
RalewayRegular {
id: ownedBy;
text: root.itemOwner;
// Text size
size: 22;
// Anchors
anchors.top: ownedByHeader.bottom;
anchors.topMargin: 8;
anchors.left: ownedByHeader.left;
height: paintedHeight;
// Style
color: root.infoTextColor;
elide: Text.ElideRight;
}
AnonymousProRegular {
id: isMyCertText;
visible: root.isMyCert && ownedBy.text !== "--" && ownedBy.text !== "";
text: "(Private)";
size: 18;
// Anchors
anchors.top: ownedBy.top;
anchors.topMargin: 4;
anchors.bottom: ownedBy.bottom;
anchors.left: ownedBy.right;
anchors.leftMargin: 6;
anchors.right: ownedByHeader.right;
// Style
color: root.infoTextColor;
elide: Text.ElideRight;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: dateOfPurchaseHeader;
text: "DATE OF PURCHASE";
text: "PURCHASE DATE";
// Text size
size: 16;
// Anchors
anchors.top: edition.bottom;
anchors.top: ownedBy.bottom;
anchors.topMargin: 28;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
anchors.right: parent.horizontalCenter;
anchors.rightMargin: 8;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
@ -317,73 +494,58 @@ Rectangle {
anchors.right: dateOfPurchaseHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.white;
color: root.infoTextColor;
}
RalewayRegular {
id: errorText;
id: priceHeader;
text: "PURCHASE PRICE";
// Text size
size: 20;
size: 16;
// Anchors
anchors.top: dateOfPurchase.bottom;
anchors.topMargin: 36;
anchors.left: dateOfPurchase.left;
anchors.right: dateOfPurchase.right;
anchors.bottom: parent.bottom;
// Style
wrapMode: Text.WordWrap;
color: hifi.colors.redHighlight;
verticalAlignment: Text.AlignTop;
}
}
//
// "CERTIFICATE" END
//
Item {
id: buttonsContainer;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
height: 50;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 30;
width: parent.width/2 - 50;
height: 50;
text: "close";
onClicked: {
if (root.isLightbox) {
root.visible = false;
} else {
sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases});
}
}
}
// "Show In Marketplace" button
HifiControlsUit.Button {
id: showInMarketplaceButton;
enabled: root.marketplaceUrl;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.top: ownedBy.bottom;
anchors.topMargin: 28;
anchors.left: parent.horizontalCenter;
anchors.right: parent.right;
anchors.rightMargin: 30;
width: parent.width/2 - 50;
height: 50;
text: "View In Market"
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl});
}
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
}
HiFiGlyphs {
id: hfcGlyph;
visible: priceText.text !== "Undisclosed" && priceText.text !== "";
text: hifi.glyphs.hfc;
// Size
size: 24;
// Anchors
anchors.top: priceHeader.bottom;
anchors.topMargin: 8;
anchors.left: priceHeader.left;
width: visible ? paintedWidth + 6 : 0;
height: 40;
// Style
color: root.infoTextColor;
verticalAlignment: Text.AlignTop;
horizontalAlignment: Text.AlignLeft;
}
AnonymousProRegular {
id: priceText;
text: root.itemCost;
// Text size
size: 18;
// Anchors
anchors.top: priceHeader.bottom;
anchors.topMargin: 8;
anchors.left: hfcGlyph.right;
anchors.right: priceHeader.right;
height: paintedHeight;
// Style
color: root.infoTextColor;
}
}
//
// "INFO CONTAINER" END
//
//
// FUNCTION DEFINITIONS START
@ -404,19 +566,17 @@ Rectangle {
function fromScript(message) {
switch (message.method) {
case 'inspectionCertificate_setCertificateId':
resetCert(false);
root.certificateId = message.certificateId;
if (message.entityId === "") {
updateCertificateStatus(1); // CERTIFICATE_STATUS_VERIFICATION_SUCCESS
} else {
root.entityId = message.entityId;
sendToScript({method: 'inspectionCertificate_requestOwnershipVerification', entity: root.entityId});
}
break;
case 'inspectionCertificate_resetCert':
titleBarText.text = "Certificate";
popText.text = "PROOF OF PURCHASE";
root.certificateId = "";
root.itemName = "--";
root.itemOwner = "--";
root.itemEdition = "--";
root.dateOfPurchase = "--";
root.marketplaceUrl = "";
root.isMyCert = false;
errorText.text = "";
resetCert(true);
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
@ -424,7 +584,34 @@ Rectangle {
}
signal sendToScript(var message);
function resetCert(alsoResetCertID) {
if (alsoResetCertID) {
root.entityId = "";
root.certificateId = "";
}
root.certInfoReplaceMode = 5;
root.certificateInfoPending = true;
root.certificateStatusPending = true;
root.useGoldCert = true;
root.certTitleTextColor = hifi.colors.darkGray;
root.certTextColor = hifi.colors.white;
root.infoTextColor = hifi.colors.blueAccent;
titleBarText.text = "Certificate";
popText.text = "";
root.itemName = "--";
root.itemOwner = "--";
root.itemEdition = "--";
root.dateOfPurchase = "--";
root.marketplaceUrl = "";
root.itemCost = "--";
root.isMyCert = false;
errorText.text = "";
}
function getFormattedDate(timestamp) {
if (timestamp === "--") {
return "--";
}
function addLeadingZero(n) {
return n < 10 ? '0' + n : '' + n;
}
@ -449,7 +636,7 @@ Rectangle {
var min = addLeadingZero(a.getMinutes());
var sec = addLeadingZero(a.getSeconds());
return year + '-' + month + '-' + day + '<br>' + drawnHour + ':' + min + amOrPm;
return year + '-' + month + '-' + day + ' ' + drawnHour + ':' + min + amOrPm;
}
//
// FUNCTION DEFINITIONS END

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -1115,7 +1115,7 @@ Item {
AnimatedImage {
id: sendingMoneyImage;
source: "./images/loader.gif"
source: "../../common/images/loader.gif"
width: 96;
height: width;
anchors.verticalCenter: parent.verticalCenter;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

View file

@ -67,7 +67,7 @@ QPushButton#revealLogButton {
font-size: 11px;
}
QPushButton#showAllButton {
QPushButton#allLogsButton {
font-family: Helvetica, Arial, sans-serif;
background-color: #333333;
color: #BBBBBB;
@ -112,4 +112,11 @@ QComboBox::drop-down {
QComboBox::down-arrow {
image: url(:/styles/filter.png);
border-width: 0px;
}
QLabel#messageCount {
font-family: Helvetica, Arial, sans-serif;
text-align: center;
color: #3d3d3d;
font-size: 11px;
}

View file

@ -318,7 +318,7 @@ static QTimer pingTimer;
static bool DISABLE_WATCHDOG = true;
#else
static const QString DISABLE_WATCHDOG_FLAG{ "HIFI_DISABLE_WATCHDOG" };
static bool DISABLE_WATCHDOG = QProcessEnvironment::systemEnvironment().contains(DISABLE_WATCHDOG_FLAG);
static bool DISABLE_WATCHDOG = nsightActive() || QProcessEnvironment::systemEnvironment().contains(DISABLE_WATCHDOG_FLAG);
#endif
#if defined(USE_GLES)
@ -415,20 +415,26 @@ public:
*crashTrigger = 0xDEAD10CC;
}
static void withPause(const std::function<void()>& lambda) {
pause();
lambda();
resume();
}
static void pause() {
_paused = true;
}
static void resume() {
_paused = false;
// Update the heartbeat BEFORE resuming the checks
updateHeartbeat();
_paused = false;
}
void run() override {
while (!_quit) {
QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS);
// Don't do heartbeat detection under nsight
if (nsightActive() || _paused) {
if (_paused) {
continue;
}
uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us
@ -797,6 +803,8 @@ std::shared_ptr<Cube3DOverlay> _keyboardFocusHighlight{ nullptr };
OverlayID _keyboardFocusHighlightID{ UNKNOWN_OVERLAY_ID };
OffscreenGLCanvas* _qmlShareContext { nullptr };
// FIXME hack access to the internal share context for the Chromium helper
// Normally we'd want to use QWebEngine::initialize(), but we can't because
// our primary context is a QGLWidget, which can't easily be initialized to share
@ -1046,7 +1054,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);
// you might think we could just do this in NodeList but we only want this connection for Interface
connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);
connect(&nodeList->getDomainHandler(), SIGNAL(limitOfSilentDomainCheckInsReached()),
nodeList.data(), SLOT(reset()));
auto dialogsManager = DependencyManager::get<DialogsManager>();
connect(accountManager.data(), &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog);
@ -2258,18 +2267,37 @@ void Application::initializeGL() {
_isGLInitialized = true;
}
// Build a shared canvas / context for the Chromium processes
_glWidget->makeCurrent();
#if !defined(DISABLE_QML)
// Chromium rendering uses some GL functions that prevent nSight from capturing
// frames, so we only create the shared context if nsight is NOT active.
if (!nsightActive()) {
_chromiumShareContext = new OffscreenGLCanvas();
_chromiumShareContext->setObjectName("ChromiumShareContext");
_chromiumShareContext->create(_glWidget->qglContext());
_chromiumShareContext->makeCurrent();
if (!_chromiumShareContext->makeCurrent()) {
qCWarning(interfaceapp, "Unable to make chromium shared context current");
}
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
} else {
qCWarning(interfaceapp) << "nSIGHT detected, disabling chrome rendering";
}
#endif
// Build a shared canvas / context for the QML rendering
_glWidget->makeCurrent();
_qmlShareContext = new OffscreenGLCanvas();
_qmlShareContext->setObjectName("QmlShareContext");
_qmlShareContext->create(_glWidget->qglContext());
if (!_qmlShareContext->makeCurrent()) {
qCWarning(interfaceapp, "Unable to make QML shared context current");
}
OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext());
_qmlShareContext->doneCurrent();
_glWidget->makeCurrent();
gpu::Context::init<gpu::gl::GLBackend>();
qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK,
@ -2282,29 +2310,22 @@ void Application::initializeGL() {
initDisplay();
qCDebug(interfaceapp, "Initialized Display.");
#ifdef Q_OS_OSX
// FIXME: on mac os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread.
DeadlockWatchdogThread::pause();
#endif
// Set up the render engine
render::CullFunctor cullFunctor = LODManager::shouldRender;
static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
_renderEngine->addJob<UpdateSceneTask>("UpdateScene");
// FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread.
DeadlockWatchdogThread::withPause([&] {
// Set up the render engine
render::CullFunctor cullFunctor = LODManager::shouldRender;
static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD";
_renderEngine->addJob<UpdateSceneTask>("UpdateScene");
#ifndef Q_OS_ANDROID
_renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED);
_renderEngine->addJob<SecondaryCameraRenderTask>("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED);
#endif
_renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, !DISABLE_DEFERRED, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0);
_renderEngine->addJob<RenderViewTask>("RenderMainView", cullFunctor, !DISABLE_DEFERRED, render::ItemKey::TAG_BITS_0, render::ItemKey::TAG_BITS_0);
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);
_renderEngine->load();
_renderEngine->registerScene(_main3DScene);
// Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success.
DependencyManager::get<GeometryCache>()->initializeShapePipelines();
#ifdef Q_OS_OSX
DeadlockWatchdogThread::resume();
#endif
// Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success.
DependencyManager::get<GeometryCache>()->initializeShapePipelines();
});
_offscreenContext = new OffscreenGLCanvas();
_offscreenContext->setObjectName("MainThreadContext");
@ -2312,20 +2333,23 @@ void Application::initializeGL() {
if (!_offscreenContext->makeCurrent()) {
qFatal("Unable to make offscreen context current");
}
_offscreenContext->doneCurrent();
_renderEventHandler = new RenderEventHandler(_glWidget->qglContext());
// The UI can't be created until the primary OpenGL
// context is created, because it needs to share
// texture resources
// Needs to happen AFTER the render engine initialization to access its configuration
if (!_offscreenContext->makeCurrent()) {
qFatal("Unable to make offscreen context current");
}
initializeUi();
qCDebug(interfaceapp, "Initialized Offscreen UI.");
_glWidget->makeCurrent();
// call Menu getInstance static method to set up the menu
// Needs to happen AFTER the QML UI initialization
_window->setMenuBar(Menu::getInstance());
if (!_offscreenContext->makeCurrent()) {
qFatal("Unable to make offscreen context current");
}
init();
qCDebug(interfaceapp, "init() complete.");
@ -2336,7 +2360,6 @@ void Application::initializeGL() {
_idleLoopStdev.reset();
_renderEventHandler = new RenderEventHandler(_glWidget->qglContext());
// Restore the primary GL content for the main thread
if (!_offscreenContext->makeCurrent()) {
@ -2402,27 +2425,70 @@ void Application::initializeUi() {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
tabletScriptingInterface->getTablet(SYSTEM_TABLET);
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create();
auto surfaceContext = offscreenUi->getSurfaceContext();
auto offscreenUi = DependencyManager::get<OffscreenUi>();
connect(offscreenUi.data(), &hifi::qml::OffscreenSurface::rootContextCreated,
this, &Application::onDesktopRootContextCreated);
connect(offscreenUi.data(), &hifi::qml::OffscreenSurface::rootItemCreated,
this, &Application::onDesktopRootItemCreated);
offscreenUi->setProxyWindow(_window->windowHandle());
// OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to
// support the window management and scripting proxies for VR use
offscreenUi->createDesktop(PathUtils::qmlUrl("hifi/Desktop.qml"));
DeadlockWatchdogThread::withPause([&] {
offscreenUi->createDesktop(PathUtils::qmlUrl("hifi/Desktop.qml"));
});
// FIXME either expose so that dialogs can set this themselves or
// do better detection in the offscreen UI of what has focus
offscreenUi->setNavigationFocused(false);
auto engine = surfaceContext->engine();
connect(engine, &QQmlEngine::quit, [] {
qApp->quit();
});
setupPreferences();
_glWidget->installEventFilter(offscreenUi.data());
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
QPointF result = pt;
auto displayPlugin = getActiveDisplayPlugin();
if (displayPlugin->isHmd()) {
getApplicationCompositor().handleRealMouseMoveEvent(false);
auto resultVec = getApplicationCompositor().getReticlePosition();
result = QPointF(resultVec.x, resultVec.y);
}
return result.toPoint();
});
offscreenUi->resume();
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){
resizeGL();
});
// This will set up the input plugins UI
_activeInputPlugins.clear();
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
if (KeyboardMouseDevice::NAME == inputPlugin->getName()) {
_keyboardMouseDevice = std::dynamic_pointer_cast<KeyboardMouseDevice>(inputPlugin);
}
if (TouchscreenDevice::NAME == inputPlugin->getName()) {
_touchscreenDevice = std::dynamic_pointer_cast<TouchscreenDevice>(inputPlugin);
}
}
auto compositorHelper = DependencyManager::get<CompositorHelper>();
connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, this, [=] {
if (isHMDMode()) {
showCursor(compositorHelper->getAllowMouseCapture() ?
Cursor::Manager::lookupIcon(_preferredCursor.get()) :
Cursor::Icon::SYSTEM);
}
});
// Pre-create a couple of Web3D overlays to speed up tablet UI
auto offscreenSurfaceCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
}
void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
auto engine = surfaceContext->engine();
// in Qt 5.10.0 there is already an "Audio" object in the QML context
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
surfaceContext->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());
@ -2504,48 +2570,12 @@ void Application::initializeUi() {
surfaceContext->setContextProperty("Steam", new SteamScriptingInterface(engine, steamClient.get()));
}
_glWidget->installEventFilter(offscreenUi.data());
offscreenUi->setMouseTranslator([=](const QPointF& pt) {
QPointF result = pt;
auto displayPlugin = getActiveDisplayPlugin();
if (displayPlugin->isHmd()) {
getApplicationCompositor().handleRealMouseMoveEvent(false);
auto resultVec = getApplicationCompositor().getReticlePosition();
result = QPointF(resultVec.x, resultVec.y);
}
return result.toPoint();
});
offscreenUi->resume();
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect& r){
resizeGL();
});
// This will set up the input plugins UI
_activeInputPlugins.clear();
foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
if (KeyboardMouseDevice::NAME == inputPlugin->getName()) {
_keyboardMouseDevice = std::dynamic_pointer_cast<KeyboardMouseDevice>(inputPlugin);
}
if (TouchscreenDevice::NAME == inputPlugin->getName()) {
_touchscreenDevice = std::dynamic_pointer_cast<TouchscreenDevice>(inputPlugin);
}
}
_window->setMenuBar(new Menu());
}
auto compositorHelper = DependencyManager::get<CompositorHelper>();
connect(compositorHelper.data(), &CompositorHelper::allowMouseCaptureChanged, this, [=] {
if (isHMDMode()) {
showCursor(compositorHelper->getAllowMouseCapture() ?
Cursor::Manager::lookupIcon(_preferredCursor.get()) :
Cursor::Icon::SYSTEM);
}
});
// Pre-create a couple of Web3D overlays to speed up tablet UI
auto offscreenSurfaceCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
void Application::onDesktopRootItemCreated(QQuickItem* rootItem) {
Stats::show();
AvatarInputs::show();
}
void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
@ -2784,6 +2814,8 @@ void Application::resizeGL() {
QMutexLocker viewLocker(&_viewMutex);
_myCamera.loadViewFrustum(_viewFrustum);
}
DependencyManager::get<OffscreenUi>()->resize(fromGlm(displayPlugin->getRecommendedUiSize()));
}
void Application::handleSandboxStatus(QNetworkReply* reply) {
@ -3992,7 +4024,7 @@ void Application::idle() {
// Bit of a hack since there's no device pixel ratio change event I can find.
if (offscreenUi->size() != fromGlm(uiSize)) {
qCDebug(interfaceapp) << "Device pixel ratio changed, triggering resize to " << uiSize;
offscreenUi->resize(fromGlm(uiSize), true);
offscreenUi->resize(fromGlm(uiSize));
_offscreenContext->makeCurrent();
}
}
@ -6933,10 +6965,10 @@ void Application::loadAvatarBrowser() const {
DependencyManager::get<HMDScriptingInterface>()->openTablet();
}
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
// Get a screenshot and save it
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename);
// If we're not doing an animated snapshot as well...
if (!includeAnimated) {
// Tell the dependency manager that the capture of the still snapshot has taken place.
@ -6948,9 +6980,9 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
});
}
void Application::takeSecondaryCameraSnapshot() {
postLambdaEvent([this] {
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot());
void Application::takeSecondaryCameraSnapshot(const QString& filename) {
postLambdaEvent([filename, this] {
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename);
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true);
});
}
@ -7324,9 +7356,7 @@ void Application::updateDisplayMode() {
action->setChecked(true);
}
_offscreenContext->makeCurrent();
offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize()));
_offscreenContext->makeCurrent();
getApplicationCompositor().setDisplayPlugin(newDisplayPlugin);
_displayPlugin = newDisplayPlugin;
connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent, Qt::DirectConnection);

View file

@ -266,8 +266,10 @@ public:
float getGameLoopRate() const { return _gameLoopCounter.rate(); }
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f);
void takeSecondaryCameraSnapshot();
// Note that takeSnapshot has a default value, as this method is used internally.
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString());
void takeSecondaryCameraSnapshot(const QString& filename);
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
graphics::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
@ -389,6 +391,8 @@ public slots:
void setPreferredCursor(const QString& cursor);
private slots:
void onDesktopRootItemCreated(QQuickItem* qmlContext);
void onDesktopRootContextCreated(QQmlContext* qmlContext);
void showDesktop();
void clearDomainOctreeDetails();
void clearDomainAvatars();

View file

@ -55,7 +55,7 @@ void Application::paintGL() {
// If a display plugin loses it's underlying support, it
// needs to be able to signal us to not use it
if (!displayPlugin->beginFrameRender(_renderFrameCount)) {
updateDisplayMode();
QMetaObject::invokeMethod(this, "updateDisplayMode");
return;
}
}

View file

@ -121,13 +121,13 @@ void AvatarBookmarks::addBookmark() {
const QVariant& avatarScale = myAvatar->getAvatarScale();
// If Avatar attachments ever change, this is where to update them, when saving remember to also append to AVATAR_BOOKMARK_VERSION
QVariantMap *bookmark = new QVariantMap;
bookmark->insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
bookmark->insert(ENTRY_AVATAR_URL, avatarUrl);
bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale);
bookmark->insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
QVariantMap bookmark;
bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION);
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant());
Bookmarks::addBookmarkToFile(bookmarkName, *bookmark);
Bookmarks::addBookmarkToFile(bookmarkName, bookmark);
});
}

View file

@ -144,13 +144,13 @@ void AvatarEntitiesBookmarks::addBookmark() {
const QString& avatarUrl = myAvatar->getSkeletonModelURL().toString();
const QVariant& avatarScale = myAvatar->getAvatarScale();
QVariantMap *bookmark = new QVariantMap;
bookmark->insert(ENTRY_VERSION, AVATAR_ENTITIES_BOOKMARK_VERSION);
bookmark->insert(ENTRY_AVATAR_URL, avatarUrl);
bookmark->insert(ENTRY_AVATAR_SCALE, avatarScale);
bookmark->insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant());
QVariantMap bookmark;
bookmark.insert(ENTRY_VERSION, AVATAR_ENTITIES_BOOKMARK_VERSION);
bookmark.insert(ENTRY_AVATAR_URL, avatarUrl);
bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale);
bookmark.insert(ENTRY_AVATAR_ENTITIES, myAvatar->getAvatarEntitiesVariant());
Bookmarks::addBookmarkToFile(bookmarkName, *bookmark);
Bookmarks::addBookmarkToFile(bookmarkName, bookmark);
});
}

View file

@ -168,7 +168,7 @@ float LODManager::getDesktopLODDecreaseFPS() const {
}
float LODManager::getDesktopLODIncreaseFPS() const {
return glm::max(((float)MSECS_PER_SECOND / _desktopMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_DESKTOP_FPS);
return glm::min(((float)MSECS_PER_SECOND / _desktopMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_DESKTOP_FPS);
}
void LODManager::setHMDLODDecreaseFPS(float fps) {
@ -184,7 +184,7 @@ float LODManager::getHMDLODDecreaseFPS() const {
}
float LODManager::getHMDLODIncreaseFPS() const {
return glm::max(((float)MSECS_PER_SECOND / _hmdMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_HMD_FPS);
return glm::min(((float)MSECS_PER_SECOND / _hmdMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_HMD_FPS);
}
QString LODManager::getLODFeedbackText() {

View file

@ -25,7 +25,7 @@ const float DEFAULT_DESKTOP_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_
const float DEFAULT_HMD_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_HMD_LOD_DOWN_FPS; // msec
const float MAX_LIKELY_DESKTOP_FPS = 59.0f; // this is essentially, V-synch - 1 fps
const float MAX_LIKELY_HMD_FPS = 74.0f; // this is essentially, V-synch - 1 fps
const float INCREASE_LOD_GAP_FPS = 15.0f; // fps
const float INCREASE_LOD_GAP_FPS = 10.0f; // fps
// The default value DEFAULT_OCTREE_SIZE_SCALE means you can be 400 meters away from a 1 meter object in order to see it (which is ~20:20 vision).
const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;

View file

@ -31,6 +31,9 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit
myAvatar->addHoldAction(this);
}
_positionalTargetSet = true;
_rotationalTargetSet = true;
#if WANT_DEBUG
qDebug() << "AvatarActionHold::AvatarActionHold" << (void*)this;
#endif

View file

@ -959,6 +959,18 @@ void MyAvatar::restoreRoleAnimation(const QString& role) {
_skeletonModel->getRig().restoreRoleAnimation(role);
}
void MyAvatar::saveAvatarUrl() {
Settings settings;
settings.beginGroup("Avatar");
if (qApp->getSaveAvatarOverrideUrl() || !qApp->getAvatarOverrideUrl().isValid() ) {
settings.setValue("fullAvatarURL",
_fullAvatarURLFromPreferences == AvatarData::defaultFullAvatarModelUrl() ?
"" :
_fullAvatarURLFromPreferences.toString());
}
settings.endGroup();
}
void MyAvatar::saveData() {
Settings settings;
settings.beginGroup("Avatar");
@ -1452,6 +1464,7 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE);
_headBoneSet.clear();
_cauterizationNeedsUpdate = true;
saveAvatarUrl();
emit skeletonChanged();
}

View file

@ -646,6 +646,7 @@ private:
void simulate(float deltaTime);
void updateFromTrackers(float deltaTime);
void saveAvatarUrl();
virtual void render(RenderArgs* renderArgs) override;
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); }

View file

@ -1,8 +1,10 @@
#include "LimitlessConnection.h"
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <src/InterfaceLogging.h>
#include <src/ui/AvatarInputs.h>
#include "LimitlessConnection.h"
#include "LimitlessVoiceRecognitionScriptingInterface.h"
LimitlessConnection::LimitlessConnection() :

View file

@ -396,11 +396,11 @@ QString WindowScriptingInterface::protocolSignature() {
}
int WindowScriptingInterface::getInnerWidth() {
return qApp->getDeviceSize().x;
return qApp->getWindow()->geometry().width();
}
int WindowScriptingInterface::getInnerHeight() {
return qApp->getDeviceSize().y;
return qApp->getWindow()->geometry().height() - qApp->getPrimaryMenu()->geometry().height();
}
glm::vec2 WindowScriptingInterface::getDeviceSize() const {
@ -430,12 +430,12 @@ bool WindowScriptingInterface::setDisplayTexture(const QString& name) {
return qApp->getActiveDisplayPlugin()->setDisplayTexture(name); // Plugins that don't know how, answer false.
}
void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
qApp->takeSnapshot(notify, includeAnimated, aspectRatio);
void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
qApp->takeSnapshot(notify, includeAnimated, aspectRatio, filename);
}
void WindowScriptingInterface::takeSecondaryCameraSnapshot() {
qApp->takeSecondaryCameraSnapshot();
void WindowScriptingInterface::takeSecondaryCameraSnapshot(const QString& filename) {
qApp->takeSecondaryCameraSnapshot(filename);
}
void WindowScriptingInterface::shareSnapshot(const QString& path, const QUrl& href) {

View file

@ -334,6 +334,8 @@ public slots:
* @param {number} aspectRatio=0 - The width/height ratio of the snapshot required. If the value is <code>0</code> the
* full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the
* dimensions is adjusted in order to match the aspect ratio.
* @param {string} filename=QString() - If this value is not null then the image will be saved to this filename, with an appended ",jpg".
* otherwise, the image will be saved as 'hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS'
* @example <caption>Using the snapshot function and signals.</caption>
* function onStillSnapshotTaken(path, notify) {
* print("Still snapshot taken: " + path);
@ -355,15 +357,19 @@ public slots:
* var notify = true;
* var animated = true;
* var aspect = 1920 / 1080;
* Window.takeSnapshot(notify, animated, aspect);
* var filename = QString();
* Window.takeSnapshot(notify, animated, aspect, filename);
*/
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f);
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString());
/**jsdoc
* Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API.
* @function Window.takeSecondaryCameraSnapshot
* @param {string} filename=QString() - If this value is not null then the image will be saved to this filename, with an appended ".jpg"
*
* var filename = QString();
*/
void takeSecondaryCameraSnapshot();
void takeSecondaryCameraSnapshot(const QString& filename = QString());
/**jsdoc
* Emit a {@link Window.connectionAdded|connectionAdded} or a {@link Window.connectionError|connectionError} signal that

View file

@ -25,7 +25,6 @@ Setting::Handle<bool> showAudioToolsSetting { QStringList { "AvatarInputs", "sho
AvatarInputs* AvatarInputs::getInstance() {
if (!INSTANCE) {
AvatarInputs::registerType();
AvatarInputs::show();
Q_ASSERT(INSTANCE);
}
return INSTANCE;

View file

@ -29,8 +29,8 @@ const int SEARCH_TOGGLE_BUTTON_WIDTH = 50;
const int SEARCH_TEXT_WIDTH = 240;
const int TIME_STAMP_LENGTH = 16;
const int FONT_WEIGHT = 75;
const QColor HIGHLIGHT_COLOR = QColor("#3366CC");
const QColor BOLD_COLOR = QColor("#445c8c");
const QColor HIGHLIGHT_COLOR = QColor("#00B4EF");
const QColor BOLD_COLOR = QColor("#1080B8");
const QString BOLD_PATTERN = "\\[\\d*\\/.*:\\d*:\\d*\\]";
BaseLogDialog::BaseLogDialog(QWidget* parent) : QDialog(parent, Qt::Window) {
@ -182,6 +182,7 @@ void BaseLogDialog::updateSelection() {
Highlighter::Highlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) {
boldFormat.setFontWeight(FONT_WEIGHT);
boldFormat.setForeground(BOLD_COLOR);
keywordFormat.setFontWeight(FONT_WEIGHT);
keywordFormat.setForeground(HIGHLIGHT_COLOR);
}

View file

@ -15,11 +15,12 @@
#include <QPushButton>
#include <QComboBox>
#include <QPlainTextEdit>
#include <QLabel>
#include <shared/AbstractLoggerInterface.h>
const int REVEAL_BUTTON_WIDTH = 122;
const int CLEAR_FILTER_BUTTON_WIDTH = 80;
const int ALL_LOGS_BUTTON_WIDTH = 90;
const int MARGIN_LEFT = 25;
const int DEBUG_CHECKBOX_WIDTH = 70;
const int INFO_CHECKBOX_WIDTH = 65;
@ -142,6 +143,11 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog
_filterDropdown->addItem("qml");
connect(_filterDropdown, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &LogDialog::handleFilterDropdownChanged);
_leftPad += COMBOBOX_WIDTH + MARGIN_LEFT + MARGIN_LEFT;
_messageCount = new QLabel("", this);
_messageCount->setObjectName("messageCount");
_messageCount->show();
_extraDebuggingBox = new QCheckBox("Extra debugging", this);
if (_logger->extraDebugging()) {
_extraDebuggingBox->setCheckState(Qt::Checked);
@ -149,12 +155,13 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog
_extraDebuggingBox->show();
connect(_extraDebuggingBox, &QCheckBox::stateChanged, this, &LogDialog::handleExtraDebuggingCheckbox);
_clearFilterButton = new QPushButton("Clear Filters", this);
_allLogsButton = new QPushButton("All Messages", this);
// set object name for css styling
_clearFilterButton->setObjectName("showAllButton");
_clearFilterButton->show();
connect(_clearFilterButton, &QPushButton::clicked, this, &LogDialog::handleClearFilterButton);
handleClearFilterButton();
_allLogsButton->setObjectName("allLogsButton");
_allLogsButton->show();
connect(_allLogsButton, &QPushButton::clicked, this, &LogDialog::handleAllLogsButton);
handleAllLogsButton();
auto windowGeometry = _windowGeometry.get();
if (windowGeometry.isValid()) {
@ -168,11 +175,15 @@ void LogDialog::resizeEvent(QResizeEvent* event) {
ELEMENT_MARGIN,
REVEAL_BUTTON_WIDTH,
ELEMENT_HEIGHT);
_clearFilterButton->setGeometry(width() - ELEMENT_MARGIN - CLEAR_FILTER_BUTTON_WIDTH,
_allLogsButton->setGeometry(width() - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH,
THIRD_ROW,
CLEAR_FILTER_BUTTON_WIDTH,
ALL_LOGS_BUTTON_WIDTH,
ELEMENT_HEIGHT);
_extraDebuggingBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - CLEAR_FILTER_BUTTON_WIDTH,
_extraDebuggingBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH,
THIRD_ROW,
COMBOBOX_WIDTH,
ELEMENT_HEIGHT);
_messageCount->setGeometry(_leftPad,
THIRD_ROW,
COMBOBOX_WIDTH,
ELEMENT_HEIGHT);
@ -187,13 +198,13 @@ void LogDialog::handleRevealButton() {
_logger->locateLog();
}
void LogDialog::handleClearFilterButton() {
void LogDialog::handleAllLogsButton() {
_logger->setExtraDebugging(false);
_extraDebuggingBox->setCheckState(Qt::Unchecked);
_logger->setDebugPrint(false);
_debugPrintBox->setCheckState(Qt::Unchecked);
_logger->setInfoPrint(false);
_infoPrintBox->setCheckState(Qt::Unchecked);
_logger->setDebugPrint(true);
_debugPrintBox->setCheckState(Qt::Checked);
_logger->setInfoPrint(true);
_infoPrintBox->setCheckState(Qt::Checked);
_logger->setCriticalPrint(true);
_criticalPrintBox->setCheckState(Qt::Checked);
_logger->setWarningPrint(true);
@ -270,40 +281,67 @@ void LogDialog::appendLogLine(QString logLine) {
if (logLine.contains(DEBUG_TEXT, Qt::CaseSensitive)) {
if (_logger->debugPrint()) {
_logTextBox->appendPlainText(logLine.trimmed());
_count++;
updateMessageCount();
}
} else if (logLine.contains(INFO_TEXT, Qt::CaseSensitive)) {
if (_logger->infoPrint()) {
_logTextBox->appendPlainText(logLine.trimmed());
_count++;
updateMessageCount();
}
} else if (logLine.contains(CRITICAL_TEXT, Qt::CaseSensitive)) {
if (_logger->criticalPrint()) {
_logTextBox->appendPlainText(logLine.trimmed());
_count++;
updateMessageCount();
}
} else if (logLine.contains(WARNING_TEXT, Qt::CaseSensitive)) {
if (_logger->warningPrint()) {
_logTextBox->appendPlainText(logLine.trimmed());
_count++;
updateMessageCount();
}
} else if (logLine.contains(SUPPRESS_TEXT, Qt::CaseSensitive)) {
if (_logger->suppressPrint()) {
_logTextBox->appendPlainText(logLine.trimmed());
_count++;
updateMessageCount();
}
} else if (logLine.contains(FATAL_TEXT, Qt::CaseSensitive)) {
if (_logger->fatalPrint()) {
_logTextBox->appendPlainText(logLine.trimmed());
_count++;
updateMessageCount();
}
} else {
if (_logger->unknownPrint()) {
if (_logger->unknownPrint() && logLine.trimmed() != "") {
_logTextBox->appendPlainText(logLine.trimmed());
_count++;
updateMessageCount();
}
}
}
}
void LogDialog::printLogFile() {
_count = 0;
_logTextBox->clear();
QString log = getCurrentLog();
QStringList logList = log.split('\n');
for (const auto& message : logList) {
appendLogLine(message);
}
updateMessageCount();
}
void LogDialog::updateMessageCount() {
_countLabel = QString::number(_count);
if (_count != 1) {
_countLabel.append(" log messages");
}
else {
_countLabel.append(" log message");
}
_messageCount->setText(_countLabel);
}

View file

@ -18,6 +18,7 @@
class QCheckBox;
class QPushButton;
class QComboBox;
class QLabel;
class QResizeEvent;
class AbstractLoggerInterface;
@ -41,19 +42,21 @@ private slots:
void handleFatalPrintBox(int);
void handleUnknownPrintBox(int);
void handleFilterDropdownChanged(int);
void handleClearFilterButton();
void handleAllLogsButton();
void printLogFile();
protected:
void resizeEvent(QResizeEvent* event) override;
void closeEvent(QCloseEvent* event) override;
QString getCurrentLog() override;
void printLogFile();
void updateMessageCount();
private:
QCheckBox* _extraDebuggingBox;
QPushButton* _revealLogButton;
QPushButton* _clearFilterButton;
QPushButton* _allLogsButton;
QCheckBox* _debugPrintBox;
QCheckBox* _infoPrintBox;
QCheckBox* _criticalPrintBox;
@ -62,10 +65,12 @@ private:
QCheckBox* _fatalPrintBox;
QCheckBox* _unknownPrintBox;
QComboBox* _filterDropdown;
QLabel* _messageCount;
QString _filterSelection;
QString _countLabel;
AbstractLoggerInterface* _logger;
Setting::Handle<QRect> _windowGeometry;
int _count = 0;
};
#endif // hifi_LogDialog_h

View file

@ -73,9 +73,9 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
return data;
}
QString Snapshot::saveSnapshot(QImage image) {
QString Snapshot::saveSnapshot(QImage image, const QString& filename) {
QFile* snapshotFile = savedFileForSnapshot(image, false);
QFile* snapshotFile = savedFileForSnapshot(image, false, filename);
// we don't need the snapshot file, so close it, grab its filename and delete it
snapshotFile->close();
@ -92,7 +92,7 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
return static_cast<QTemporaryFile*>(savedFileForSnapshot(image, true));
}
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename) {
// adding URL to snapshot
QUrl currentURL = DependencyManager::get<AddressManager>()->currentShareableAddress();
@ -104,7 +104,15 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary) {
QDateTime now = QDateTime::currentDateTime();
QString filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT));
// If user has requested specific filename then use it, else create the filename
// 'jpg" is appended, as the image is saved in jpg format. This is the case for all snapshots
// (see definition of FILENAME_PATH_FORMAT)
QString filename;
if (!userSelectedFilename.isNull()) {
filename = userSelectedFilename + ".jpg";
} else {
filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT));
}
const int IMAGE_QUALITY = 100;

View file

@ -37,7 +37,7 @@ class Snapshot : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
static QString saveSnapshot(QImage image);
static QString saveSnapshot(QImage image, const QString& filename);
static QTemporaryFile* saveTempSnapshot(QImage image);
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
@ -51,7 +51,7 @@ public slots:
Q_INVOKABLE QString getSnapshotsLocation();
Q_INVOKABLE void setSnapshotsLocation(const QString& location);
private:
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary);
static QFile* savedFileForSnapshot(QImage & image, bool isTemporary, const QString& userSelectedFilename = QString());
};
#endif // hifi_Snapshot_h

View file

@ -48,7 +48,6 @@ QString getTextureMemoryPressureModeString();
Stats* Stats::getInstance() {
if (!INSTANCE) {
Stats::registerType();
Stats::show();
Q_ASSERT(INSTANCE);
}
return INSTANCE;

View file

@ -181,6 +181,8 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
if (properties["parentID"].isValid()) {
setParentID(QUuid(properties["parentID"].toString()));
bool success;
getParentPointer(success); // call this to hook-up the parent's back-pointers to its child overlays
needRenderItemUpdate = true;
}
if (properties["parentJointIndex"].isValid()) {

View file

@ -274,82 +274,88 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
auto nodeList = DependencyManager::get<NodeList>();
if (entityProperties.getClientOnly()) {
if (entityProperties.verifyStaticCertificateProperties()) {
SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer);
if (entityProperties.verifyStaticCertificateProperties()) {
if (entityProperties.getClientOnly()) {
SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer);
if (entityServer) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
QJsonObject request;
request["certificate_id"] = entityProperties.getCertificateID();
networkRequest.setUrl(requestURL);
if (entityServer) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer");
QJsonObject request;
request["certificate_id"] = entityProperties.getCertificateID();
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = NULL;
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
QNetworkReply* networkReply = NULL;
networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, [=]() {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
connect(networkReply, &QNetworkReply::finished, [=]() {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
if (networkReply->error() == QNetworkReply::NoError) {
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
qCDebug(entities) << "invalid_reason not empty";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
qCDebug(entities) << "'transfer_status' is 'failed'";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
qCDebug(entities) << "'transfer_status' is 'pending'";
} else {
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
QByteArray certID = entityProperties.getCertificateID().toUtf8();
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
int certIDByteArraySize = certID.length();
int textByteArraySize = text.length();
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(textByteArraySize);
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(text);
challengeOwnershipPacket->write(nodeToChallengeByteArray);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
// Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer");
return;
if (networkReply->error() == QNetworkReply::NoError) {
if (!jsonObject["invalid_reason"].toString().isEmpty()) {
qCDebug(entities) << "invalid_reason not empty";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") {
qCDebug(entities) << "'transfer_status' is 'failed'";
} else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") {
qCDebug(entities) << "'transfer_status' is 'pending'";
} else {
startChallengeOwnershipTimer();
}
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() <<
"More info:" << networkReply->readAll();
}
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
networkReply->deleteLater();
});
} else {
qCWarning(context_overlay) << "Couldn't get Entity Server!";
}
QByteArray certID = entityProperties.getCertificateID().toUtf8();
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
int certIDByteArraySize = certID.length();
int textByteArraySize = text.length();
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(textByteArraySize);
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(text);
challengeOwnershipPacket->write(nodeToChallengeByteArray);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
// Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer");
return;
} else {
startChallengeOwnershipTimer();
}
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() <<
"More info:" << networkReply->readAll();
}
networkReply->deleteLater();
});
} else {
qCWarning(context_overlay) << "Couldn't get Entity Server!";
}
} else {
// We don't currently verify ownership of entities that aren't Avatar Entities,
// so they always pass Ownership Verification. It's necessary to emit this signal
// so that the Inspection Certificate can continue its information-grabbing process.
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
}
} else {
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
}
}
@ -357,12 +363,10 @@ static const QString INSPECTION_CERTIFICATE_QML_PATH = "hifi/commerce/inspection
void ContextOverlayInterface::openInspectionCertificate() {
// lets open the tablet to the inspection certificate QML
if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) {
setLastInspectedEntity(_currentEntityWithContextOverlay);
auto tablet = dynamic_cast<TabletProxy*>(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH);
_hmdScriptingInterface->openTablet();
setLastInspectedEntity(_currentEntityWithContextOverlay);
requestOwnershipVerification(_lastInspectedEntity);
}
}

View file

@ -57,7 +57,7 @@ public:
bool getEnabled() { return _enabled; }
bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; }
void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; }
void requestOwnershipVerification(const QUuid& entityID);
Q_INVOKABLE void requestOwnershipVerification(const QUuid& entityID);
EntityPropertyFlags getEntityPropertyFlags() { return _entityPropertyFlags; }
signals:

View file

@ -63,7 +63,7 @@ glm::vec3 Line3DOverlay::getEnd() const {
localEnd = getLocalEnd();
worldEnd = localToWorld(localEnd, getParentID(), getParentJointIndex(), getScalesWithParent(), success);
if (!success) {
qDebug() << "Line3DOverlay::getEnd failed";
qDebug() << "Line3DOverlay::getEnd failed, parentID = " << getParentID();
}
return worldEnd;
}

View file

@ -80,7 +80,6 @@ Web3DOverlay::Web3DOverlay() {
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
_webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
}
Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) :
@ -135,7 +134,11 @@ void Web3DOverlay::destroyWebSurface() {
QObject::disconnect(this, &Web3DOverlay::scriptEventReceived, _webSurface.data(), &OffscreenQmlSurface::emitScriptEvent);
QObject::disconnect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived);
DependencyManager::get<OffscreenQmlSurfaceCache>()->release(QML, _webSurface);
auto offscreenCache = DependencyManager::get<OffscreenQmlSurfaceCache>();
// FIXME prevents crash on shutdown, but we shoudln't have to do this check
if (offscreenCache) {
offscreenCache->release(QML, _webSurface);
}
_webSurface.reset();
}
@ -201,6 +204,11 @@ void Web3DOverlay::setupQmlSurface() {
_webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags);
_webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get<AddressManager>().data());
_webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
_webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED
_webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance());
// in Qt 5.10.0 there is already an "Audio" object in the QML context
// though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface"
_webSurface->getSurfaceContext()->setContextProperty("AudioScriptingInterface", DependencyManager::get<AudioScriptingInterface>().data());

View file

@ -1244,7 +1244,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
int handJointIndex = _animSkeleton->nameToJointIndex("LeftHand");
int armJointIndex = _animSkeleton->nameToJointIndex("LeftArm");
int elbowJointIndex = _animSkeleton->nameToJointIndex("LeftForeArm");
if (!leftArmEnabled && elbowJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
if (!leftArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, true);
// smooth toward desired pole vector from previous pole vector... to reduce jitter
@ -1291,7 +1291,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
int handJointIndex = _animSkeleton->nameToJointIndex("RightHand");
int armJointIndex = _animSkeleton->nameToJointIndex("RightArm");
int elbowJointIndex = _animSkeleton->nameToJointIndex("RightForeArm");
if (!rightArmEnabled && elbowJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
if (!rightArmEnabled && handJointIndex >= 0 && armJointIndex >= 0 && elbowJointIndex >= 0) {
glm::vec3 poleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, hipsIndex, false);
// smooth toward desired pole vector from previous pole vector... to reduce jitter
@ -1754,7 +1754,6 @@ void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
const AnimPoseVec& relativeDefaultPoses = _animSkeleton->getRelativeDefaultPoses();
for (int i = 0; i < numJoints; i++) {
const JointData& data = jointDataVec.at(i);
_internalPoseSet._relativePoses[i].scale() = Vectors::ONE;
_internalPoseSet._relativePoses[i].rot() = rotations[i];
if (data.translationIsDefaultPose) {
_internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans();

View file

@ -49,6 +49,9 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes
audioPacket->writePrimitive(channelFlag);
}
// at this point we'd better be sending the mixer a valid position, or it won't consider us for mixing
assert(!isNaN(transform.getTranslation()));
// pack the three float positions
audioPacket->writePrimitive(transform.getTranslation());
// pack the orientation

View file

@ -17,6 +17,7 @@
#include <glm/detail/func_common.hpp>
#include <QtCore/QDataStream>
#include <LogHandler.h>
#include <Node.h>
#include <udt/PacketHeaders.h>
#include <UUID.h>
@ -76,6 +77,19 @@ int PositionalAudioStream::parsePositionalData(const QByteArray& positionalByteA
QDataStream packetStream(positionalByteArray);
packetStream.readRawData(reinterpret_cast<char*>(&_position), sizeof(_position));
// if the client sends us a bad position, flag it so that we don't consider this stream for mixing
if (glm::isnan(_position.x) || glm::isnan(_position.y) || glm::isnan(_position.z)) {
static const QString INVALID_POSITION_REGEX = "PositionalAudioStream unpacked invalid position for node";
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(INVALID_POSITION_REGEX);
qDebug() << "PositionalAudioStream unpacked invalid position for node" << uuidStringWithoutCurlyBraces(getNodeID());
_hasValidPosition = false;
} else {
_hasValidPosition = true;
}
packetStream.readRawData(reinterpret_cast<char*>(&_orientation), sizeof(_orientation));
packetStream.readRawData(reinterpret_cast<char*>(&_avatarBoundingBoxCorner), sizeof(_avatarBoundingBoxCorner));
packetStream.readRawData(reinterpret_cast<char*>(&_avatarBoundingBoxScale), sizeof(_avatarBoundingBoxScale));

View file

@ -43,12 +43,15 @@ public:
bool shouldLoopbackForNode() const { return _shouldLoopbackForNode; }
bool isStereo() const { return _isStereo; }
PositionalAudioStream::Type getType() const { return _type; }
const glm::vec3& getPosition() const { return _position; }
const glm::quat& getOrientation() const { return _orientation; }
const glm::vec3& getAvatarBoundingBoxCorner() const { return _avatarBoundingBoxCorner; }
const glm::vec3& getAvatarBoundingBoxScale() const { return _avatarBoundingBoxScale; }
bool hasValidPosition() const { return _hasValidPosition; }
protected:
// disallow copying of PositionalAudioStream objects
@ -75,6 +78,8 @@ protected:
float _quietestTrailingFrameLoudness;
float _quietestFrameLoudness;
int _frameCounter;
bool _hasValidPosition { false };
};
#endif // hifi_PositionalAudioStream_h

View file

@ -1,6 +1,6 @@
set(TARGET_NAME avatars-renderer)
AUTOSCRIBE_SHADER_LIB(gpu graphics render render-utils)
setup_hifi_library(Widgets Network Script)
setup_hifi_library(Network Script)
link_hifi_libraries(shared gpu graphics animation model-networking script-engine render render-utils image trackers entities-renderer)
include_hifi_library_headers(avatars)
include_hifi_library_headers(networking)

View file

@ -707,7 +707,11 @@ public slots:
void setJointMappingsFromNetworkReply();
void setSessionUUID(const QUuid& sessionUUID) {
if (sessionUUID != getID()) {
setID(sessionUUID);
if (sessionUUID == QUuid()) {
setID(AVATAR_SELF_ID);
} else {
setID(sessionUUID);
}
emit sessionUUIDChanged();
}
}

View file

@ -1,6 +1,6 @@
set(TARGET_NAME display-plugins)
AUTOSCRIBE_SHADER_LIB(gpu display-plugins)
setup_hifi_library(OpenGL)
setup_hifi_library(Gui)
link_hifi_libraries(shared plugins ui-plugins gl ui render-utils ${PLATFORM_GL_BACKEND})
include_hifi_library_headers(gpu)
include_hifi_library_headers(model-networking)

View file

@ -395,8 +395,8 @@ void HmdDisplayPlugin::HUDRenderer::build() {
void HmdDisplayPlugin::HUDRenderer::updatePipeline() {
if (!pipeline) {
auto vs = gpu::Shader::createVertex(std::string(hmd_ui_vert));
auto ps = gpu::Shader::createPixel(std::string(hmd_ui_frag));
auto vs = hmd_ui_vert::getShader();
auto ps = hmd_ui_frag::getShader();
auto program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::makeProgram(*program, gpu::Shader::BindingSet());
uniformsLocation = program->getUniformBuffers().findLocation("hudBuffer");

View file

@ -1,7 +1,7 @@
set(TARGET_NAME entities-renderer)
AUTOSCRIBE_SHADER_LIB(gpu graphics procedural render render-utils)
setup_hifi_library(Widgets Network Script)
link_hifi_libraries(shared gpu procedural graphics model-networking script-engine render render-utils image ui pointers)
setup_hifi_library(Network Script)
link_hifi_libraries(shared gpu procedural graphics model-networking script-engine render render-utils image qml ui pointers)
include_hifi_library_headers(networking)
include_hifi_library_headers(gl)
include_hifi_library_headers(ktx)

View file

@ -36,8 +36,8 @@ static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, co
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
PrepareStencil::testMask(*state);
auto vertShader = gpu::Shader::createVertex(std::string(textured_particle_vert));
auto fragShader = gpu::Shader::createPixel(std::string(textured_particle_frag));
auto vertShader = textured_particle_vert::getShader();
auto fragShader = textured_particle_frag::getShader();
auto program = gpu::Shader::createProgram(vertShader, fragShader);
_texturedPipeline = texturedPipeline = gpu::Pipeline::create(program, state);

View file

@ -48,8 +48,8 @@ struct PolyLineUniforms {
static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) {
if (!polylinePipeline) {
auto VS = gpu::Shader::createVertex(std::string(paintStroke_vert));
auto PS = gpu::Shader::createPixel(std::string(paintStroke_frag));
auto VS = paintStroke_vert::getShader();
auto PS = paintStroke_frag::getShader();
gpu::ShaderPointer program = gpu::Shader::createProgram(VS, PS);
#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT
auto fadeVS = gpu::Shader::createVertex(std::string(paintStroke_fade_vert));

View file

@ -27,6 +27,7 @@
#include <StencilMaskPass.h>
#include "EntityTreeRenderer.h"
#include "polyvox_vert.h"
#include "polyvox_frag.h"
#include "polyvox_fade_vert.h"
@ -70,6 +71,7 @@
#include "StencilMaskPass.h"
#include "EntityTreeRenderer.h"
#include "polyvox_vert.h"
#include "polyvox_frag.h"
#include "polyvox_fade_vert.h"
@ -1459,8 +1461,8 @@ static gpu::Stream::FormatPointer _vertexFormat;
ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch) {
if (!_pipelines[0]) {
gpu::ShaderPointer vertexShaders[2] = { gpu::Shader::createVertex(std::string(polyvox_vert)), gpu::Shader::createVertex(std::string(polyvox_fade_vert)) };
gpu::ShaderPointer pixelShaders[2] = { gpu::Shader::createPixel(std::string(polyvox_frag)), gpu::Shader::createPixel(std::string(polyvox_fade_frag)) };
gpu::ShaderPointer vertexShaders[2] = { polyvox_vert::getShader(), polyvox_fade_vert::getShader() };
gpu::ShaderPointer pixelShaders[2] = { polyvox_frag::getShader(), polyvox_fade_frag::getShader() };
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), MATERIAL_GPU_SLOT));

View file

@ -165,7 +165,7 @@ public:
PolyVoxEntityRenderer(const EntityItemPointer& entity);
protected:
virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape(); }
virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); }
virtual ShapeKey getShapeKey() override;
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;

View file

@ -16,8 +16,8 @@
#include <GeometryCache.h>
#include <PerfStat.h>
#include <render-utils/simple_vert.h>
#include <render-utils/simple_frag.h>
#include "render-utils/simple_vert.h"
#include "render-utils/simple_frag.h"
//#define SHAPE_ENTITY_USE_FADE_EFFECT
#ifdef SHAPE_ENTITY_USE_FADE_EFFECT
@ -32,8 +32,8 @@ static const float SPHERE_ENTITY_SCALE = 0.5f;
ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {
_procedural._vertexSource = simple_vert;
_procedural._fragmentSource = simple_frag;
_procedural._vertexSource = simple_vert::getSource();
_procedural._fragmentSource = simple_frag::getSource();
_procedural._opaqueState->setCullMode(gpu::State::CULL_NONE);
_procedural._opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
PrepareStencil::testMaskDrawShape(*_procedural._opaqueState);

View file

@ -221,19 +221,17 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
});
};
{
// FIXME use the surface cache instead of explicit creation
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), deleter);
_webSurface->create();
}
// FIXME use the surface cache instead of explicit creation
_webSurface = QSharedPointer<OffscreenQmlSurface>(new OffscreenQmlSurface(), deleter);
// FIXME, the max FPS could be better managed by being dynamic (based on the number of current surfaces
// and the current rendering load)
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
// FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
_webSurface->getSurfaceContext()->setContextProperty("desktop", QVariant());
// Let us interact with the keyboard
_webSurface->getSurfaceContext()->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
QObject::connect(_webSurface.data(), &OffscreenQmlSurface::rootContextCreated, [this](QQmlContext* surfaceContext) {
// FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
surfaceContext->setContextProperty("desktop", QVariant());
// Let us interact with the keyboard
surfaceContext->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
});
_fadeStartTime = usecTimestampNow();
loadSourceURL();
_webSurface->resume();

View file

@ -241,7 +241,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
}
#endif
updateKeyZoneItemFromEntity();
updateKeyZoneItemFromEntity(entity);
if (keyLightChanged) {
updateKeySunFromEntity(entity);
@ -329,7 +329,7 @@ void ZoneEntityRenderer::updateKeySunFromEntity(const TypedEntityPointer& entity
// Set the keylight
sunLight->setColor(ColorUtils::toVec3(_keyLightProperties.getColor()));
sunLight->setIntensity(_keyLightProperties.getIntensity());
sunLight->setDirection(_keyLightProperties.getDirection());
sunLight->setDirection(entity->getTransform().getRotation() * _keyLightProperties.getDirection());
sunLight->setCastShadows(_keyLightProperties.getCastShadows());
}
@ -350,6 +350,8 @@ void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer&
} else {
setAmbientURL(_ambientLightProperties.getAmbientURL());
}
ambientLight->setTransform(entity->getTransform().getInverseMatrix());
}
void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) {
@ -379,7 +381,7 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity)
haze->setHazeKeyLightRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeKeyLightRange()));
haze->setHazeKeyLightAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(_hazeProperties.getHazeKeyLightAltitude()));
haze->setZoneTransform(entity->getTransform().getMatrix());
haze->setTransform(entity->getTransform().getMatrix());
}
void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) {
@ -391,7 +393,10 @@ void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer&
setSkyboxURL(_skyboxProperties.getURL());
}
void ZoneEntityRenderer::updateKeyZoneItemFromEntity() {
void ZoneEntityRenderer::updateKeyZoneItemFromEntity(const TypedEntityPointer& entity) {
// Update rotation values
editSkybox()->setOrientation(entity->getTransform().getRotation());
/* TODO: Implement the sun model behavior / Keep this code here for reference, this is how we
{
// Set the stage

View file

@ -45,7 +45,7 @@ protected:
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
private:
void updateKeyZoneItemFromEntity();
void updateKeyZoneItemFromEntity(const TypedEntityPointer& entity);
void updateKeySunFromEntity(const TypedEntityPointer& entity);
void updateAmbientLightFromEntity(const TypedEntityPointer& entity);
void updateHazeFromEntity(const TypedEntityPointer& entity);

View file

@ -180,6 +180,8 @@ public:
float emissiveIntensity{ 1.0f };
float ambientFactor{ 1.0f };
float bumpMultiplier { 1.0f }; // TODO: to be implemented
QString materialID;
QString name;
QString shadingModel;

View file

@ -15,6 +15,7 @@
#include "OBJReader.h"
#include <ctype.h> // .obj files are not locale-specific. The C/ASCII charset applies.
#include <sstream>
#include <QtCore/QBuffer>
#include <QtCore/QIODevice>
@ -35,6 +36,11 @@ QHash<QString, float> COMMENT_SCALE_HINTS = {{"This file uses centimeters as uni
const QString SMART_DEFAULT_MATERIAL_NAME = "High Fidelity smart default material name";
const float ILLUMINATION_MODEL_MIN_OPACITY = 0.1f;
const float ILLUMINATION_MODEL_APPLY_SHININESS = 0.5f;
const float ILLUMINATION_MODEL_APPLY_ROUGHNESS = 1.0f;
const float ILLUMINATION_MODEL_APPLY_NON_METALLIC = 0.0f;
namespace {
template<class T>
T& checked_at(QVector<T>& vector, int i) {
@ -70,6 +76,7 @@ int OBJTokenizer::nextToken(bool allowSpaceChar /*= false*/) {
}
switch (ch) {
case '#': {
_datum = "";
_comment = _device->readLine(); // stash comment for a future call to getComment
return COMMENT_TOKEN;
}
@ -256,7 +263,14 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
default:
materials[matName] = currentMaterial;
#ifdef WANT_DEBUG
qCDebug(modelformat) << "OBJ Reader Last material shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity << " diffuse color:" << currentMaterial.diffuseColor << " specular color:" << currentMaterial.specularColor << " diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" << currentMaterial.specularTextureFilename;
qCDebug(modelformat) << "OBJ Reader Last material illumination model:" << currentMaterial.illuminationModel <<
" shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity <<
" diffuse color:" << currentMaterial.diffuseColor << " specular color:" <<
currentMaterial.specularColor << " emissive color:" << currentMaterial.emissiveColor <<
" diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" <<
currentMaterial.specularTextureFilename << " emissive texture:" <<
currentMaterial.emissiveTextureFilename << " bump texture:" <<
currentMaterial.bumpTextureFilename;
#endif
return;
}
@ -272,20 +286,46 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
qCDebug(modelformat) << "OBJ Reader Starting new material definition " << matName;
#endif
currentMaterial.diffuseTextureFilename = "";
currentMaterial.emissiveTextureFilename = "";
currentMaterial.specularTextureFilename = "";
currentMaterial.bumpTextureFilename = "";
} else if (token == "Ns") {
currentMaterial.shininess = tokenizer.getFloat();
} else if ((token == "d") || (token == "Tr")) {
} else if (token == "Ni") {
#ifdef WANT_DEBUG
qCDebug(modelformat) << "OBJ Reader Ignoring material Ni " << tokenizer.getFloat();
#else
tokenizer.getFloat();
#endif
} else if (token == "d") {
currentMaterial.opacity = tokenizer.getFloat();
} else if (token == "Tr") {
currentMaterial.opacity = 1.0f - tokenizer.getFloat();
} else if (token == "illum") {
currentMaterial.illuminationModel = tokenizer.getFloat();
} else if (token == "Tf") {
#ifdef WANT_DEBUG
qCDebug(modelformat) << "OBJ Reader Ignoring material Tf " << tokenizer.getVec3();
#else
tokenizer.getVec3();
#endif
} else if (token == "Ka") {
#ifdef WANT_DEBUG
qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3();
qCDebug(modelformat) << "OBJ Reader Ignoring material Ka " << tokenizer.getVec3();;
#else
tokenizer.getVec3();
#endif
} else if (token == "Kd") {
currentMaterial.diffuseColor = tokenizer.getVec3();
} else if (token == "Ke") {
currentMaterial.emissiveColor = tokenizer.getVec3();
} else if (token == "Ks") {
currentMaterial.specularColor = tokenizer.getVec3();
} else if ((token == "map_Kd") || (token == "map_Ks")) {
QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8();
} else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump")) {
const QByteArray textureLine = tokenizer.getLineAsDatum();
QByteArray filename;
OBJMaterialTextureOptions textureOptions;
parseTextureLine(textureLine, filename, textureOptions);
if (filename.endsWith(".tga")) {
#ifdef WANT_DEBUG
qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << _url;
@ -294,11 +334,104 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
}
if (token == "map_Kd") {
currentMaterial.diffuseTextureFilename = filename;
} else if( token == "map_Ks" ) {
} else if (token == "map_Ke") {
currentMaterial.emissiveTextureFilename = filename;
} else if (token == "map_Ks" ) {
currentMaterial.specularTextureFilename = filename;
} else if ((token == "map_bump") || (token == "bump")) {
currentMaterial.bumpTextureFilename = filename;
currentMaterial.bumpTextureOptions = textureOptions;
}
}
}
}
void OBJReader::parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions) {
// Texture options reference http://paulbourke.net/dataformats/mtl/
// and https://wikivisually.com/wiki/Material_Template_Library
std::istringstream iss(textureLine.toStdString());
const std::vector<std::string> parser(std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>());
uint i = 0;
while (i < parser.size()) {
if (i + 1 < parser.size() && parser[i][0] == '-') {
const std::string& option = parser[i++];
if (option == "-blendu" || option == "-blendv") {
#ifdef WANT_DEBUG
const std::string& onoff = parser[i++];
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << onoff.c_str();
#endif
} else if (option == "-bm") {
const std::string& bm = parser[i++];
textureOptions.bumpMultiplier = std::stof(bm);
} else if (option == "-boost") {
#ifdef WANT_DEBUG
const std::string& boost = parser[i++];
float boostFloat = std::stof(boost);
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << boost.c_str();
#endif
} else if (option == "-cc") {
#ifdef WANT_DEBUG
const std::string& onoff = parser[i++];
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << onoff.c_str();
#endif
} else if (option == "-clamp") {
#ifdef WANT_DEBUG
const std::string& onoff = parser[i++];
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << onoff.c_str();
#endif
} else if (option == "-imfchan") {
#ifdef WANT_DEBUG
const std::string& imfchan = parser[i++];
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << imfchan.c_str();
#endif
} else if (option == "-mm") {
if (i + 1 < parser.size()) {
#ifdef WANT_DEBUG
const std::string& mmBase = parser[i++];
const std::string& mmGain = parser[i++];
float mmBaseFloat = std::stof(mmBase);
float mmGainFloat = std::stof(mmGain);
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << mmBase.c_str() << mmGain.c_str();
#endif
}
} else if (option == "-o" || option == "-s" || option == "-t") {
if (i + 2 < parser.size()) {
#ifdef WANT_DEBUG
const std::string& u = parser[i++];
const std::string& v = parser[i++];
const std::string& w = parser[i++];
float uFloat = std::stof(u);
float vFloat = std::stof(v);
float wFloat = std::stof(w);
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << u.c_str() << v.c_str() << w.c_str();
#endif
}
} else if (option == "-texres") {
#ifdef WANT_DEBUG
const std::string& texres = parser[i++];
float texresFloat = std::stof(texres);
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << texres.c_str();
#endif
} else if (option == "-type") {
#ifdef WANT_DEBUG
const std::string& type = parser[i++];
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring texture option" << option.c_str() << type.c_str();
#endif
} else if (option[0] == '-') {
#ifdef WANT_DEBUG
qCDebug(modelformat) << "OBJ Reader WARNING: Ignoring unsupported texture option" << option.c_str();
#endif
}
} else { // assume filename at end when no more options
std::string filenameString = parser[i++];
while (i < parser.size()) { // filename has space in it
filenameString += " " + parser[i++];
}
filename = filenameString.c_str();
}
}
}
std::tuple<bool, QByteArray> requestData(QUrl& url) {
@ -745,7 +878,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
}
geometry.materials[materialID] = FBXMaterial(objMaterial.diffuseColor,
objMaterial.specularColor,
glm::vec3(0.0f),
objMaterial.emissiveColor,
objMaterial.shininess,
objMaterial.opacity);
FBXMaterial& fbxMaterial = geometry.materials[materialID];
@ -759,17 +892,88 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
if (!objMaterial.specularTextureFilename.isEmpty()) {
fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename;
}
if (!objMaterial.emissiveTextureFilename.isEmpty()) {
fbxMaterial.emissiveTexture.filename = objMaterial.emissiveTextureFilename;
}
if (!objMaterial.bumpTextureFilename.isEmpty()) {
fbxMaterial.normalTexture.filename = objMaterial.bumpTextureFilename;
fbxMaterial.normalTexture.isBumpmap = true;
fbxMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier;
}
modelMaterial->setEmissive(fbxMaterial.emissiveColor);
modelMaterial->setAlbedo(fbxMaterial.diffuseColor);
modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor));
modelMaterial->setRoughness(graphics::Material::shininessToRoughness(fbxMaterial.shininess));
if (fbxMaterial.opacity <= 0.0f) {
modelMaterial->setOpacity(1.0f);
} else {
modelMaterial->setOpacity(fbxMaterial.opacity);
bool applyTransparency = false;
bool applyShininess = false;
bool applyRoughness = false;
bool applyNonMetallic = false;
bool fresnelOn = false;
// Illumination model reference http://paulbourke.net/dataformats/mtl/
switch (objMaterial.illuminationModel) {
case 0: // Color on and Ambient off
// We don't support ambient = do nothing?
break;
case 1: // Color on and Ambient on
// We don't support ambient = do nothing?
break;
case 2: // Highlight on
// Change specular intensity = do nothing for now?
break;
case 3: // Reflection on and Ray trace on
applyShininess = true;
break;
case 4: // Transparency: Glass on and Reflection: Ray trace on
applyTransparency = true;
applyShininess = true;
break;
case 5: // Reflection: Fresnel on and Ray trace on
applyShininess = true;
fresnelOn = true;
break;
case 6: // Transparency: Refraction on and Reflection: Fresnel off and Ray trace on
applyTransparency = true;
applyNonMetallic = true;
applyShininess = true;
break;
case 7: // Transparency: Refraction on and Reflection: Fresnel on and Ray trace on
applyTransparency = true;
applyNonMetallic = true;
applyShininess = true;
fresnelOn = true;
break;
case 8: // Reflection on and Ray trace off
applyShininess = true;
break;
case 9: // Transparency: Glass on and Reflection: Ray trace off
applyTransparency = true;
applyNonMetallic = true;
applyRoughness = true;
break;
case 10: // Casts shadows onto invisible surfaces
// Do nothing?
break;
}
if (applyTransparency) {
fbxMaterial.opacity = std::max(fbxMaterial.opacity, ILLUMINATION_MODEL_MIN_OPACITY);
}
if (applyShininess) {
modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_SHININESS);
} else if (applyRoughness) {
modelMaterial->setRoughness(ILLUMINATION_MODEL_APPLY_ROUGHNESS);
}
if (applyNonMetallic) {
modelMaterial->setMetallic(ILLUMINATION_MODEL_APPLY_NON_METALLIC);
}
if (fresnelOn) {
modelMaterial->setFresnel(glm::vec3(1.0f));
}
modelMaterial->setOpacity(fbxMaterial.opacity);
}
return geometryPtr;

View file

@ -48,6 +48,11 @@ private:
void addFrom(const OBJFace* face, int index);
};
class OBJMaterialTextureOptions {
public:
float bumpMultiplier { 1.0f };
}
;
// Materials and references to material names can come in any order, and different mesh parts can refer to the same material.
// Therefore it would get pretty hacky to try to use FBXMeshPart to store these as we traverse the files.
class OBJMaterial {
@ -56,11 +61,16 @@ public:
float opacity;
glm::vec3 diffuseColor;
glm::vec3 specularColor;
glm::vec3 emissiveColor;
QByteArray diffuseTextureFilename;
QByteArray specularTextureFilename;
QByteArray emissiveTextureFilename;
QByteArray bumpTextureFilename;
OBJMaterialTextureOptions bumpTextureOptions;
int illuminationModel;
bool used { false };
bool userSpecifiesUV { false };
OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f) {}
OBJMaterial() : shininess(0.0f), opacity(1.0f), diffuseColor(0.9f), specularColor(0.9f), emissiveColor(0.0f), illuminationModel(-1) {}
};
class OBJReader: public QObject { // QObject so we can make network requests.
@ -84,6 +94,7 @@ private:
bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry,
float& scaleGuess, bool combineParts);
void parseMaterialLibrary(QIODevice* device);
void parseTextureLine(const QByteArray& textureLine, QByteArray& filename, OBJMaterialTextureOptions& textureOptions);
bool isValidTexture(const QByteArray &filename); // true if the file exists. TODO?: check content-type header and that it is a supported format.
int _partCounter { 0 };

View file

@ -1,5 +1,5 @@
set(TARGET_NAME gl)
setup_hifi_library(OpenGL Qml Quick)
setup_hifi_library(Gui Widgets Qml Quick)
link_hifi_libraries(shared)
target_opengl()

View file

@ -81,6 +81,10 @@ bool isRenderThread() {
return QThread::currentThread() == RENDER_THREAD;
}
#if defined(Q_OS_ANDROID)
#define USE_GLES 1
#endif
namespace gl {
void withSavedContext(const std::function<void()>& f) {
// Save the original GL context, because creating a QML surface will create a new context
@ -91,4 +95,47 @@ namespace gl {
savedContext->makeCurrent(savedSurface);
}
}
bool checkGLError(const char* name) {
GLenum error = glGetError();
if (!error) {
return false;
}
switch (error) {
case GL_INVALID_ENUM:
qCWarning(glLogging) << "GLBackend" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag.";
break;
case GL_INVALID_VALUE:
qCWarning(glLogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag";
break;
case GL_INVALID_OPERATION:
qCWarning(glLogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag..";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
qCWarning(glLogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag.";
break;
case GL_OUT_OF_MEMORY:
qCWarning(glLogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
break;
#if !defined(USE_GLES)
case GL_STACK_UNDERFLOW:
qCWarning(glLogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to underflow.";
break;
case GL_STACK_OVERFLOW:
qCWarning(glLogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to overflow.";
break;
#endif
}
return true;
}
bool checkGLErrorDebug(const char* name) {
#ifdef DEBUG
return checkGLError(name);
#else
Q_UNUSED(name);
return false;
#endif
}
}

View file

@ -13,6 +13,8 @@
#include <functional>
#include <QJsonObject>
#include "GLLogging.h"
// 16 bits of depth precision
#define DEFAULT_GL_DEPTH_BUFFER_BITS 16
// 8 bits of stencil buffer (typically you really only need 1 bit for functionality
@ -43,6 +45,13 @@ bool isRenderThread();
namespace gl {
void withSavedContext(const std::function<void()>& f);
}
bool checkGLError(const char* name);
bool checkGLErrorDebug(const char* name);
} // namespace gl
#define CHECK_GL_ERROR() ::gl::checkGLErrorDebug(__FUNCTION__)
#endif

View file

@ -8,12 +8,13 @@
#include "GLShared.h"
#include <mutex>
#include <fstream>
#include <QtCore/QThread>
#include <gl/GLHelpers.h>
#include <GPUIdent.h>
#include <NumericalConstants.h>
#include <fstream>
Q_LOGGING_CATEGORY(gpugllogging, "hifi.gpu.gl")
Q_LOGGING_CATEGORY(trace_render_gpu_gl, "trace.render.gpu.gl")
@ -21,47 +22,6 @@ Q_LOGGING_CATEGORY(trace_render_gpu_gl_detail, "trace.render.gpu.gl.detail")
namespace gpu { namespace gl {
bool checkGLError(const char* name) {
GLenum error = glGetError();
if (!error) {
return false;
} else {
switch (error) {
case GL_INVALID_ENUM:
qCWarning(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag.";
break;
case GL_INVALID_VALUE:
qCWarning(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag";
break;
case GL_INVALID_OPERATION:
qCWarning(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag..";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
qCWarning(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag.";
break;
case GL_OUT_OF_MEMORY:
qCWarning(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
break;
case GL_STACK_UNDERFLOW:
qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to underflow.";
break;
case GL_STACK_OVERFLOW:
qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to overflow.";
break;
}
return true;
}
}
bool checkGLErrorDebug(const char* name) {
#ifdef DEBUG
return checkGLError(name);
#else
Q_UNUSED(name);
return false;
#endif
}
gpu::Size getFreeDedicatedMemory() {
Size result { 0 };
static bool nvidiaMemorySupported { true };

View file

@ -9,6 +9,7 @@
#define hifi_gpu_GLShared_h
#include <gl/Config.h>
#include <gl/GLHelpers.h>
#include <gpu/Forward.h>
#include <gpu/Format.h>
#include <gpu/Context.h>
@ -114,9 +115,6 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
GL_INT_2_10_10_10_REV,
};
bool checkGLError(const char* name = nullptr);
bool checkGLErrorDebug(const char* name = nullptr);
class GLBackend;
template <typename GPUType>
@ -141,11 +139,8 @@ class GLShader;
class GLTexture;
struct ShaderObject;
} } // namespace gpu::gl
#define CHECK_GL_ERROR() gpu::gl::checkGLErrorDebug(__FUNCTION__)
#endif

View file

@ -413,7 +413,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui
sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize;
sourceMip._offset = bufferOffset;
bufferOffset += sourceMip._size;
gpu::gl::checkGLError();
::gl::checkGLError(__FUNCTION__);
}
(void)CHECK_GL_ERROR();
@ -458,7 +458,7 @@ void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLui
#endif
glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat,
sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize));
gpu::gl::checkGLError();
::gl::checkGLError(__FUNCTION__);
}
}

View file

@ -1,5 +1,5 @@
set(TARGET_NAME gpu-gles)
setup_hifi_library(Concurrent)
setup_hifi_library(Gui Concurrent)
link_hifi_libraries(shared gl gpu)
GroupSources("src")
target_opengl()

View file

@ -9,11 +9,14 @@
#define hifi_gpu_GLShared_h
#include <gl/Config.h>
#include <gl/GLHelpers.h>
#include <gpu/Forward.h>
#include <gpu/Format.h>
#include <gpu/Context.h>
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(gpugllogging)
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl)
Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail)
@ -143,8 +146,6 @@ struct ShaderObject;
} } // namespace gpu::gl
#define CHECK_GL_ERROR() gpu::gl::checkGLErrorDebug(__FUNCTION__)
#endif

View file

@ -16,7 +16,7 @@
#include <memory>
#include <set>
#include <map>
#include <functional>
#include <QUrl>
namespace gpu {

View file

@ -73,42 +73,42 @@ ShaderPointer StandardShaderLib::getProgram(GetShader getVS, GetShader getPS) {
ShaderPointer StandardShaderLib::getDrawUnitQuadTexcoordVS() {
if (!_drawUnitQuadTexcoordVS) {
_drawUnitQuadTexcoordVS = gpu::Shader::createVertex(std::string(DrawUnitQuadTexcoord_vert));
_drawUnitQuadTexcoordVS = DrawUnitQuadTexcoord_vert::getShader();
}
return _drawUnitQuadTexcoordVS;
}
ShaderPointer StandardShaderLib::getDrawTransformUnitQuadVS() {
if (!_drawTransformUnitQuadVS) {
_drawTransformUnitQuadVS = gpu::Shader::createVertex(std::string(DrawTransformUnitQuad_vert));
_drawTransformUnitQuadVS = DrawTransformUnitQuad_vert::getShader();
}
return _drawTransformUnitQuadVS;
}
ShaderPointer StandardShaderLib::getDrawTexcoordRectTransformUnitQuadVS() {
if (!_drawTexcoordRectTransformUnitQuadVS) {
_drawTexcoordRectTransformUnitQuadVS = gpu::Shader::createVertex(std::string(DrawTexcoordRectTransformUnitQuad_vert));
_drawTexcoordRectTransformUnitQuadVS = DrawTexcoordRectTransformUnitQuad_vert::getShader();
}
return _drawTexcoordRectTransformUnitQuadVS;
}
ShaderPointer StandardShaderLib::getDrawViewportQuadTransformTexcoordVS() {
if (!_drawViewportQuadTransformTexcoordVS) {
_drawViewportQuadTransformTexcoordVS = gpu::Shader::createVertex(std::string(DrawViewportQuadTransformTexcoord_vert));
_drawViewportQuadTransformTexcoordVS = DrawViewportQuadTransformTexcoord_vert::getShader();
}
return _drawViewportQuadTransformTexcoordVS;
}
ShaderPointer StandardShaderLib::getDrawVertexPositionVS() {
if (!_drawVertexPositionVS) {
_drawVertexPositionVS = gpu::Shader::createVertex(std::string(DrawVertexPosition_vert));
_drawVertexPositionVS = DrawVertexPosition_vert::getShader();
}
return _drawVertexPositionVS;
}
ShaderPointer StandardShaderLib::getDrawTransformVertexPositionVS() {
if (!_drawTransformVertexPositionVS) {
_drawTransformVertexPositionVS = gpu::Shader::createVertex(std::string(DrawTransformVertexPosition_vert));
_drawTransformVertexPositionVS = DrawTransformVertexPosition_vert::getShader();
}
return _drawTransformVertexPositionVS;
}
@ -122,42 +122,42 @@ ShaderPointer StandardShaderLib::getDrawNadaPS() {
ShaderPointer StandardShaderLib::getDrawWhitePS() {
if (!_drawWhitePS) {
_drawWhitePS = gpu::Shader::createPixel(std::string(DrawWhite_frag));
_drawWhitePS = DrawWhite_frag::getShader();
}
return _drawWhitePS;
}
ShaderPointer StandardShaderLib::getDrawColorPS() {
if (!_drawColorPS) {
_drawColorPS = gpu::Shader::createPixel(std::string(DrawColor_frag));
_drawColorPS = DrawColor_frag::getShader();
}
return _drawColorPS;
}
ShaderPointer StandardShaderLib::getDrawTexturePS() {
if (!_drawTexturePS) {
_drawTexturePS = gpu::Shader::createPixel(std::string(DrawTexture_frag));
_drawTexturePS = DrawTexture_frag::getShader();
}
return _drawTexturePS;
}
ShaderPointer StandardShaderLib::getDrawTextureMirroredXPS() {
if (!_drawTextureMirroredXPS) {
_drawTextureMirroredXPS = gpu::Shader::createPixel(std::string(DrawTextureMirroredX_frag));
_drawTextureMirroredXPS = DrawTextureMirroredX_frag::getShader();
}
return _drawTextureMirroredXPS;
}
ShaderPointer StandardShaderLib::getDrawTextureOpaquePS() {
if (!_drawTextureOpaquePS) {
_drawTextureOpaquePS = gpu::Shader::createPixel(std::string(DrawTextureOpaque_frag));
_drawTextureOpaquePS = DrawTextureOpaque_frag::getShader();
}
return _drawTextureOpaquePS;
}
ShaderPointer StandardShaderLib::getDrawColoredTexturePS() {
if (!_drawColoredTexturePS) {
_drawColoredTexturePS = gpu::Shader::createPixel(std::string(DrawColoredTexture_frag));
_drawColoredTexturePS = DrawColoredTexture_frag::getShader();
}
return _drawColoredTexturePS;
}

View file

@ -182,11 +182,11 @@ void Haze::setHazeBackgroundBlend(const float hazeBackgroundBlend) {
}
}
void Haze::setZoneTransform(const glm::mat4& zoneTransform) {
void Haze::setTransform(const glm::mat4& transform) {
auto& params = _hazeParametersBuffer.get<Parameters>();
if (params.zoneTransform == zoneTransform) {
_hazeParametersBuffer.edit<Parameters>().zoneTransform = zoneTransform;
if (params.transform != transform) {
_hazeParametersBuffer.edit<Parameters>().transform = transform;
}
}

View file

@ -92,7 +92,7 @@ namespace graphics {
void setHazeBackgroundBlend(const float hazeBackgroundBlend);
void setZoneTransform(const glm::mat4& zoneTransform);
void setTransform(const glm::mat4& transform);
using UniformBufferView = gpu::BufferView;
UniformBufferView getHazeParametersBuffer() const { return _hazeParametersBuffer; }
@ -113,7 +113,7 @@ namespace graphics {
// bit 2 - set to activate directional light attenuation mode
// bit 3 - set to blend between blend-in and blend-out colours
glm::mat4 zoneTransform;
glm::mat4 transform;
// Amount of background (skybox) to display, overriding the haze effect for the background
float hazeBackgroundBlend{ INITIAL_HAZE_BACKGROUND_BLEND };

View file

@ -165,3 +165,9 @@ void Light::setAmbientMapNumMips(uint16_t numMips) {
_ambientSchemaBuffer.edit().mapNumMips = (float)numMips;
}
void Light::setTransform(const glm::mat4& transform) {
if (_ambientSchemaBuffer.edit().transform != transform) {
_ambientSchemaBuffer.edit().transform = transform;
}
}

View file

@ -152,6 +152,8 @@ public:
void setAmbientMapNumMips(uint16_t numMips);
uint16_t getAmbientMapNumMips() const { return (uint16_t) _ambientSchemaBuffer->mapNumMips; }
void setTransform(const glm::mat4& transform);
// Light Schema
class LightSchema {
public:
@ -165,7 +167,9 @@ public:
float mapNumMips { 0.0f };
float spare1;
float spare2;
gpu::SphericalHarmonics ambientSphere;
glm::mat4 transform;
};
using LightSchemaBuffer = gpu::StructBuffer<LightSchema>;

View file

@ -34,7 +34,9 @@ vec3 getLightIrradiance(Light l) { return lightIrradiance_getIrradiance(l.irradi
// Light Ambient
struct LightAmbient {
vec4 _ambient;
SphericalHarmonics _ambientSphere;
mat4 transform;
};
SphericalHarmonics getLightAmbientSphere(LightAmbient l) { return l._ambientSphere; }

View file

@ -37,6 +37,12 @@ void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
}
}
void Skybox::setOrientation(const glm::quat& orientation) {
// The zone rotations need to be negated
_orientation = orientation;
_orientation.w = -_orientation.w;
}
void Skybox::updateSchemaBuffer() const {
auto blend = 0.0f;
if (getCubemap() && getCubemap()->isDefined()) {
@ -85,8 +91,8 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
static std::once_flag once;
std::call_once(once, [&] {
{
auto skyVS = gpu::Shader::createVertex(std::string(skybox_vert));
auto skyFS = gpu::Shader::createPixel(std::string(skybox_frag));
auto skyVS = skybox_vert::getShader();
auto skyFS = skybox_frag::getShader();
auto skyShader = gpu::Shader::createProgram(skyVS, skyFS);
batch.runLambda([skyShader] {
@ -115,6 +121,10 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
Transform viewTransform;
viewFrustum.evalViewTransform(viewTransform);
// Orientate view transform to be relative to zone
viewTransform.setRotation(skybox.getOrientation() * viewTransform.getRotation());
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewTransform);
batch.setModelTransform(Transform()); // only for Mac

View file

@ -37,6 +37,9 @@ public:
void setCubemap(const gpu::TexturePointer& cubemap);
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
void setOrientation(const glm::quat& orientation);
const glm::quat getOrientation() const { return _orientation; }
virtual bool empty() { return _empty; }
virtual void clear();
@ -61,6 +64,8 @@ protected:
mutable gpu::BufferView _schemaBuffer;
gpu::TexturePointer _cubemap;
glm::quat _orientation;
bool _empty{ true };
};
typedef std::shared_ptr<Skybox> SkyboxPointer;

View file

@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QApplication>
#include <QGuiApplication>
#include <QClipboard>
#include <QDebug>
#include <QJsonDocument>
@ -666,7 +666,7 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should
}
emit locationChangeRequired(newPosition, orientationChanged,
LookupTrigger::VisitUserFromPAL ? cancelOutRollAndPitch(newOrientation): newOrientation,
trigger == LookupTrigger::VisitUserFromPAL ? cancelOutRollAndPitch(newOrientation): newOrientation,
shouldFace
);
@ -764,7 +764,7 @@ void AddressManager::copyAddress() {
}
// assume that the address is being copied because the user wants a shareable address
QApplication::clipboard()->setText(currentShareableAddress().toString());
QGuiApplication::clipboard()->setText(currentShareableAddress().toString());
}
void AddressManager::copyPath() {
@ -773,7 +773,7 @@ void AddressManager::copyPath() {
return;
}
QApplication::clipboard()->setText(currentPath());
QGuiApplication::clipboard()->setText(currentPath());
}
QString AddressManager::getDomainID() const {

View file

@ -98,6 +98,7 @@ void DomainHandler::softReset() {
clearSettings();
_connectionDenialsSinceKeypairRegen = 0;
_checkInPacketsSinceLastReply = 0;
// cancel the failure timeout for any pending requests for settings
QMetaObject::invokeMethod(&_settingsTimer, "stop");
@ -382,6 +383,9 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
// we're hearing from this domain-server, don't need to refresh API info
_apiRefreshTimer.stop();
// this counts as a reply from the DS after a check in or connect packet, so reset that counter now
_checkInPacketsSinceLastReply = 0;
// Read deny reason from packet
uint8_t reasonCodeWire;
@ -426,3 +430,14 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
}
}
}
void DomainHandler::sentCheckInPacket() {
++_checkInPacketsSinceLastReply;
if (_checkInPacketsSinceLastReply >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
// so emit our signal that says that
qCDebug(networking) << "Limit of silent domain checkins reached";
emit limitOfSilentDomainCheckInsReached();
}
}

View file

@ -31,6 +31,8 @@ const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103;
const quint16 DOMAIN_SERVER_HTTP_PORT = 40100;
const quint16 DOMAIN_SERVER_HTTPS_PORT = 40101;
const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
class DomainHandler : public QObject {
Q_OBJECT
public:
@ -84,6 +86,10 @@ public:
void softReset();
int getCheckInPacketsSinceLastReply() const { return _checkInPacketsSinceLastReply; }
void sentCheckInPacket();
void domainListReceived() { _checkInPacketsSinceLastReply = 0; }
/**jsdoc
* <p>The reasons that you may be refused connection to a domain are defined by numeric values:</p>
* <table>
@ -165,6 +171,8 @@ signals:
void domainConnectionRefused(QString reasonMessage, int reason, const QString& extraInfo);
void limitOfSilentDomainCheckInsReached();
private:
bool reasonSuggestsLogin(ConnectionRefusedReason reasonCode);
void sendDisconnectPacket();
@ -187,6 +195,7 @@ private:
QSet<QString> _domainConnectionRefusals;
bool _hasCheckedForAccessToken { false };
int _connectionDenialsSinceKeypairRegen { 0 };
int _checkInPacketsSinceLastReply { 0 };
QTimer _apiRefreshTimer;
};

View file

@ -44,7 +44,6 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
_ownerType(newOwnerType),
_nodeTypesOfInterest(),
_domainHandler(this),
_numNoReplyDomainCheckIns(0),
_assignmentServerSocket(),
_keepAlivePingTimer(this)
{
@ -75,7 +74,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
connect(this, &LimitedNodeList::publicSockAddrChanged, this, &NodeList::sendDomainServerCheckIn);
// clear our NodeList when the domain changes
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset);
connect(&_domainHandler, SIGNAL(disconnectedFromDomain()), this, SLOT(resetFromDomainHandler()));
// send an ICE heartbeat as soon as we get ice server information
connect(&_domainHandler, &DomainHandler::iceSocketAndIDReceived, this, &NodeList::handleICEConnectionToDomainServer);
@ -92,10 +91,10 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
connect(accountManager.data(), &AccountManager::newKeypair, this, &NodeList::sendDomainServerCheckIn);
// clear out NodeList when login is finished
connect(accountManager.data(), &AccountManager::loginComplete , this, &NodeList::reset);
connect(accountManager.data(), SIGNAL(loginComplete()) , this, SLOT(reset()));
// clear our NodeList when logout is requested
connect(accountManager.data(), &AccountManager::logoutComplete , this, &NodeList::reset);
connect(accountManager.data(), SIGNAL(logoutComplete()) , this, SLOT(reset()));
// anytime we get a new node we will want to attempt to punch to it
connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch);
@ -231,16 +230,14 @@ void NodeList::processICEPingPacket(QSharedPointer<ReceivedMessage> message) {
sendPacket(std::move(replyPacket), message->getSenderSockAddr());
}
void NodeList::reset() {
void NodeList::reset(bool skipDomainHandlerReset) {
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "reset");
QMetaObject::invokeMethod(this, "reset", Q_ARG(bool, skipDomainHandlerReset));
return;
}
LimitedNodeList::reset();
_numNoReplyDomainCheckIns = 0;
// lock and clear our set of ignored IDs
_ignoredSetLock.lockForWrite();
_ignoredNodeIDs.clear();
@ -255,7 +252,7 @@ void NodeList::reset() {
_avatarGainMap.clear();
_avatarGainMapLock.unlock();
if (sender() != &_domainHandler) {
if (!skipDomainHandlerReset) {
// clear the domain connection information, unless they're the ones that asked us to reset
_domainHandler.softReset();
}
@ -410,15 +407,8 @@ void NodeList::sendDomainServerCheckIn() {
sendPacket(std::move(domainPacket), _domainHandler.getSockAddr());
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
// so emit our signal that says that
qCDebug(networking) << "Limit of silent domain checkins reached";
emit limitOfSilentDomainCheckInsReached();
}
// increment the count of un-replied check-ins
_numNoReplyDomainCheckIns++;
// let the domain handler know we sent another check in or connect packet
_domainHandler.sentCheckInPacket();
}
}
@ -585,7 +575,7 @@ void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message)
}
// this is a packet from the domain server, reset the count of un-replied check-ins
_numNoReplyDomainCheckIns = 0;
_domainHandler.domainListReceived();
// emit our signal so listeners know we just heard from the DS
emit receivedDomainServerList();
@ -999,7 +989,7 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
}
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
QWriteLocker{ &_avatarGainMapLock };
QWriteLocker lock{ &_avatarGainMapLock };
_avatarGainMap[nodeID] = gain;
} else {
@ -1011,7 +1001,7 @@ void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
}
float NodeList::getAvatarGain(const QUuid& nodeID) {
QReadLocker{ &_avatarGainMapLock };
QReadLocker lock{ &_avatarGainMapLock };
auto it = _avatarGainMap.find(nodeID);
if (it != _avatarGainMap.cend()) {
return it->second;

View file

@ -38,8 +38,6 @@
const quint64 DOMAIN_SERVER_CHECK_IN_MSECS = 1 * 1000;
const int MAX_SILENT_DOMAIN_SERVER_CHECK_INS = 5;
using PacketOrPacketList = std::pair<std::unique_ptr<NLPacket>, std::unique_ptr<NLPacketList>>;
using NodePacketOrPacketListPair = std::pair<SharedNodePointer, PacketOrPacketList>;
@ -62,7 +60,6 @@ public:
Q_INVOKABLE qint64 sendStats(QJsonObject statsObject, HifiSockAddr destination);
Q_INVOKABLE qint64 sendStatsToDomainServer(QJsonObject statsObject);
int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; }
DomainHandler& getDomainHandler() { return _domainHandler; }
const NodeSet& getNodeInterestSet() const { return _nodeTypesOfInterest; }
@ -96,7 +93,9 @@ public:
void removeFromIgnoreMuteSets(const QUuid& nodeID);
public slots:
void reset();
void reset(bool skipDomainHandlerReset = false);
void resetFromDomainHandler() { reset(true); }
void sendDomainServerCheckIn();
void handleDSPathQuery(const QString& newPath);
@ -119,7 +118,6 @@ public slots:
#endif
signals:
void limitOfSilentDomainCheckInsReached();
void receivedDomainServerList();
void ignoredNode(const QUuid& nodeID, bool enabled);
void ignoreRadiusEnabledChanged(bool isIgnored);
@ -161,7 +159,6 @@ private:
std::atomic<NodeType_t> _ownerType;
NodeSet _nodeTypesOfInterest;
DomainHandler _domainHandler;
int _numNoReplyDomainCheckIns;
HifiSockAddr _assignmentServerSocket;
bool _isShuttingDown { false };
QTimer _keepAlivePingTimer;

View file

@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::AvatarData:
case PacketType::BulkAvatarData:
case PacketType::KillAvatar:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarJointDefaultPoseFlags);
return static_cast<PacketVersion>(AvatarMixerPacketVersion::FBXReaderNodeReparenting);
case PacketType::MessagesData:
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
case PacketType::ICEServerHeartbeat:

View file

@ -77,13 +77,18 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) {
return false;
}
bool doLinearTraction = _positionalTargetSet && (_linearTimeScale < MAX_TRACTOR_TIMESCALE);
bool doAngularTraction = _rotationalTargetSet && (_angularTimeScale < MAX_TRACTOR_TIMESCALE);
if (!doLinearTraction && !doAngularTraction) {
// nothing to do
return false;
}
glm::quat rotation;
glm::vec3 position;
glm::vec3 angularVelocity;
bool linearValid = false;
int linearTractorCount = 0;
bool angularValid = false;
int angularTractorCount = 0;
QList<EntityDynamicPointer> tractorDerivedActions;
@ -105,7 +110,6 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) {
linearTimeScale, angularTimeScale);
if (success) {
if (angularTimeScale < MAX_TRACTOR_TIMESCALE) {
angularValid = true;
angularTractorCount++;
angularVelocity += angularVelocityForAction;
if (tractorAction.get() == this) {
@ -115,43 +119,40 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) {
}
if (linearTimeScale < MAX_TRACTOR_TIMESCALE) {
linearValid = true;
linearTractorCount++;
position += positionForAction;
}
}
}
if ((angularValid && angularTractorCount > 0) || (linearValid && linearTractorCount > 0)) {
if (angularTractorCount > 0 || linearTractorCount > 0) {
withWriteLock([&]{
if (linearValid && linearTractorCount > 0) {
if (doLinearTraction && linearTractorCount > 0) {
position /= linearTractorCount;
if (_positionalTargetSet) {
_lastPositionTarget = _positionalTarget;
} else {
_lastPositionTarget = position;
}
_lastPositionTarget = _positionalTarget;
_positionalTarget = position;
if (deltaTimeStep > EPSILON) {
// blend the new velocity with the old (low-pass filter)
glm::vec3 newVelocity = (1.0f / deltaTimeStep) * (position - _lastPositionTarget);
const float blend = 0.25f;
_linearVelocityTarget = (1.0f - blend) * _linearVelocityTarget + blend * newVelocity;
if (_havePositionTargetHistory) {
// blend the new velocity with the old (low-pass filter)
glm::vec3 newVelocity = (1.0f / deltaTimeStep) * (_positionalTarget - _lastPositionTarget);
const float blend = 0.25f;
_linearVelocityTarget = (1.0f - blend) * _linearVelocityTarget + blend * newVelocity;
} else {
_havePositionTargetHistory = true;
}
}
_positionalTargetSet = true;
_active = true;
}
if (angularValid && angularTractorCount > 0) {
if (doAngularTraction && angularTractorCount > 0) {
angularVelocity /= angularTractorCount;
_rotationalTarget = rotation;
_angularVelocityTarget = angularVelocity;
_rotationalTargetSet = true;
_active = true;
}
});
}
return linearValid || angularValid;
return true;
}
@ -241,7 +242,9 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) {
// targets are required, tractor-constants are optional
bool ok = true;
positionalTarget = EntityDynamicInterface::extractVec3Argument("tractor action", arguments, "targetPosition", ok, false);
if (!ok) {
if (ok) {
_positionalTargetSet = true;
} else {
positionalTarget = _desiredPositionalTarget;
}
ok = true;
@ -252,7 +255,9 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) {
ok = true;
rotationalTarget = EntityDynamicInterface::extractQuatArgument("tractor action", arguments, "targetRotation", ok, false);
if (!ok) {
if (ok) {
_rotationalTargetSet = true;
} else {
rotationalTarget = _desiredRotationalTarget;
}

View file

@ -39,6 +39,7 @@ protected:
glm::vec3 _lastPositionTarget;
float _linearTimeScale;
bool _positionalTargetSet;
bool _havePositionTargetHistory { false };
glm::quat _rotationalTarget;
glm::quat _desiredRotationalTarget;

View file

@ -1,4 +1,4 @@
set(TARGET_NAME plugins)
setup_hifi_library(OpenGL)
setup_hifi_library(Gui)
link_hifi_libraries(shared networking)
include_hifi_library_headers(gpu)

View file

@ -214,9 +214,10 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() {
}
void PluginManager::disableDisplayPlugin(const QString& name) {
std::remove_if(_displayPlugins.begin(), _displayPlugins.end(), [&](const DisplayPluginPointer& plugin){
auto it = std::remove_if(_displayPlugins.begin(), _displayPlugins.end(), [&](const DisplayPluginPointer& plugin){
return plugin->getName() == name;
});
_displayPlugins.erase(it, _displayPlugins.end());
}

View file

@ -241,7 +241,7 @@ void Procedural::prepare(gpu::Batch& batch, const glm::vec3& position, const glm
std::string fragmentShaderSource = _fragmentSource;
size_t replaceIndex = fragmentShaderSource.find(PROCEDURAL_COMMON_BLOCK);
if (replaceIndex != std::string::npos) {
fragmentShaderSource.replace(replaceIndex, PROCEDURAL_COMMON_BLOCK.size(), ProceduralCommon_frag);
fragmentShaderSource.replace(replaceIndex, PROCEDURAL_COMMON_BLOCK.size(), ProceduralCommon_frag::getSource());
}
replaceIndex = fragmentShaderSource.find(PROCEDURAL_VERSION);

View file

@ -19,8 +19,8 @@
#include <graphics/skybox_frag.h>
ProceduralSkybox::ProceduralSkybox() : graphics::Skybox() {
_procedural._vertexSource = skybox_vert;
_procedural._fragmentSource = skybox_frag;
_procedural._vertexSource = skybox_vert::getSource();
_procedural._fragmentSource = skybox_frag::getSource();
// Adjust the pipeline state for background using the stencil test
_procedural.setDoesFade(false);
// Must match PrepareStencil::STENCIL_BACKGROUND

View file

@ -0,0 +1,6 @@
set(TARGET_NAME qml)
setup_hifi_library(Multimedia Network Qml Quick WebChannel WebSockets ${PLATFORM_QT_COMPONENTS})
link_hifi_libraries(shared networking gl)
# Required for some low level GL interaction in the OffscreenQMLSurface
target_opengl()

View file

@ -0,0 +1,11 @@
//
// Created by Bradley Austin Davis 2016/03/01
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Logging.h"
Q_LOGGING_CATEGORY(qmlLogging, "hifi.qml")

View file

@ -0,0 +1,16 @@
//
// Created by Bradley Austin Davis 2018/01/04
// Copyright 2013-2018 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_Controllers_Logging_h
#define hifi_Controllers_Logging_h
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(qmlLogging)
#endif

View file

@ -0,0 +1,355 @@
//
// Created by Bradley Austin Davis on 2015-05-13
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OffscreenSurface.h"
#include <unordered_set>
#include <unordered_map>
#include <QtCore/QThread>
#include <QtQml/QtQml>
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlComponent>
#include <QtQuick/QQuickItem>
#include <QtQuick/QQuickWindow>
#include <QtQuick/QQuickRenderControl>
#include <GLMHelpers.h>
#include <gl/OffscreenGLCanvas.h>
#include <shared/ReadWriteLockable.h>
#include "Logging.h"
#include "impl/SharedObject.h"
#include "impl/TextureCache.h"
using namespace hifi::qml;
using namespace hifi::qml::impl;
static uvec2 clampSize(const uvec2& size, uint32_t maxDimension) {
return glm::clamp(size, glm::uvec2(1), glm::uvec2(maxDimension));
}
static QSize clampSize(const QSize& qsize, uint32_t maxDimension) {
return fromGlm(clampSize(toGlm(qsize), maxDimension));
}
const QmlContextObjectCallback OffscreenSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QQuickItem*) {};
void OffscreenSurface::initializeEngine(QQmlEngine* engine) {
}
using namespace hifi::qml::impl;
size_t OffscreenSurface::getUsedTextureMemory() {
return SharedObject::getTextureCache().getUsedTextureMemory();
}
void OffscreenSurface::setSharedContext(QOpenGLContext* sharedContext) {
SharedObject::setSharedContext(sharedContext);
}
std::function<void(uint32_t, void*)> OffscreenSurface::getDiscardLambda() {
return [](uint32_t texture, void* fence) {
SharedObject::getTextureCache().releaseTexture({ texture, static_cast<GLsync>(fence) });
};
}
OffscreenSurface::OffscreenSurface()
: _sharedObject(new impl::SharedObject()) {
}
OffscreenSurface::~OffscreenSurface() {
disconnect(qApp);
_sharedObject->destroy();
}
bool OffscreenSurface::fetchTexture(TextureAndFence& textureAndFence) {
if (!_sharedObject) {
return false;
}
hifi::qml::impl::TextureAndFence typedTextureAndFence;
bool result = _sharedObject->fetchTexture(typedTextureAndFence);
textureAndFence = typedTextureAndFence;
return result;
}
void OffscreenSurface::resize(const QSize& newSize_) {
const uint32_t MAX_OFFSCREEN_DIMENSION = 4096;
_sharedObject->setSize(clampSize(newSize_, MAX_OFFSCREEN_DIMENSION));
}
QQuickItem* OffscreenSurface::getRootItem() {
return _sharedObject->getRootItem();
}
void OffscreenSurface::clearCache() {
_sharedObject->getContext()->engine()->clearComponentCache();
}
QPointF OffscreenSurface::mapToVirtualScreen(const QPointF& originalPoint) {
return _mouseTranslator(originalPoint);
}
///////////////////////////////////////////////////////
//
// Event handling customization
//
bool OffscreenSurface::filterEnabled(QObject* originalDestination, QEvent* event) const {
if (!_sharedObject || _sharedObject->getWindow() == originalDestination) {
return false;
}
// Only intercept events while we're in an active state
if (_sharedObject->isPaused()) {
return false;
}
return true;
}
bool OffscreenSurface::eventFilter(QObject* originalDestination, QEvent* event) {
if (!filterEnabled(originalDestination, event)) {
return false;
}
#ifdef DEBUG
// Don't intercept our own events, or we enter an infinite recursion
{
auto rootItem = _sharedObject->getRootItem();
auto quickWindow = _sharedObject->getWindow();
QObject* recurseTest = originalDestination;
while (recurseTest) {
Q_ASSERT(recurseTest != rootItem && recurseTest != quickWindow);
recurseTest = recurseTest->parent();
}
}
#endif
switch (event->type()) {
case QEvent::KeyPress:
case QEvent::KeyRelease: {
event->ignore();
if (QCoreApplication::sendEvent(_sharedObject->getWindow(), event)) {
return event->isAccepted();
}
break;
}
case QEvent::Wheel: {
QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
QPointF transformedPos = mapToVirtualScreen(wheelEvent->pos());
QWheelEvent mappedEvent(transformedPos, wheelEvent->delta(), wheelEvent->buttons(), wheelEvent->modifiers(),
wheelEvent->orientation());
mappedEvent.ignore();
if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &mappedEvent)) {
return mappedEvent.isAccepted();
}
break;
}
case QEvent::MouseMove: {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos());
QMouseEvent mappedEvent(mouseEvent->type(), transformedPos, mouseEvent->screenPos(), mouseEvent->button(),
mouseEvent->buttons(), mouseEvent->modifiers());
if (event->type() == QEvent::MouseMove) {
// TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install
// need to investigate into why this crash is happening.
//_qmlContext->setContextProperty("lastMousePosition", transformedPos);
}
mappedEvent.ignore();
if (QCoreApplication::sendEvent(_sharedObject->getWindow(), &mappedEvent)) {
return mappedEvent.isAccepted();
}
break;
}
default:
break;
}
return false;
}
void OffscreenSurface::pause() {
_sharedObject->pause();
}
void OffscreenSurface::resume() {
_sharedObject->resume();
}
bool OffscreenSurface::isPaused() const {
return _sharedObject->isPaused();
}
void OffscreenSurface::setProxyWindow(QWindow* window) {
_sharedObject->setProxyWindow(window);
}
QObject* OffscreenSurface::getEventHandler() {
return getWindow();
}
QQuickWindow* OffscreenSurface::getWindow() {
return _sharedObject->getWindow();
}
QSize OffscreenSurface::size() const {
return _sharedObject->getSize();
}
QQmlContext* OffscreenSurface::getSurfaceContext() {
return _sharedObject->getContext();
}
void OffscreenSurface::setMaxFps(uint8_t maxFps) {
_sharedObject->setMaxFps(maxFps);
}
void OffscreenSurface::load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback) {
loadInternal(qmlSource, false, parent, [callback](QQmlContext* context, QQuickItem* newItem) {
QJSValue(callback).call(QJSValueList() << context->engine()->newQObject(newItem));
});
}
void OffscreenSurface::load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& callback) {
loadInternal(qmlSource, createNewContext, nullptr, callback);
}
void OffscreenSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
load(qmlSource, true, callback);
}
void OffscreenSurface::load(const QUrl& qmlSource, const QmlContextObjectCallback& callback) {
load(qmlSource, false, callback);
}
void OffscreenSurface::load(const QString& qmlSourceFile, const QmlContextObjectCallback& callback) {
return load(QUrl(qmlSourceFile), callback);
}
void OffscreenSurface::loadInternal(const QUrl& qmlSource,
bool createNewContext,
QQuickItem* parent,
const QmlContextObjectCallback& callback) {
if (QThread::currentThread() != thread()) {
qFatal("Called load on a non-surface thread");
}
// Synchronous loading may take a while; restart the deadlock timer
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
if (!getRootItem()) {
_sharedObject->create(this);
}
QUrl finalQmlSource = qmlSource;
if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) {
finalQmlSource = getSurfaceContext()->resolvedUrl(qmlSource);
}
if (!getRootItem()) {
_sharedObject->setObjectName(finalQmlSource.toString());
}
auto targetContext = contextForUrl(finalQmlSource, parent, createNewContext);
auto qmlComponent = new QQmlComponent(getSurfaceContext()->engine(), finalQmlSource, QQmlComponent::PreferSynchronous);
if (qmlComponent->isLoading()) {
connect(qmlComponent, &QQmlComponent::statusChanged, this,
[=](QQmlComponent::Status) { finishQmlLoad(qmlComponent, targetContext, parent, callback); });
return;
}
finishQmlLoad(qmlComponent, targetContext, parent, callback);
}
void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent,
QQmlContext* qmlContext,
QQuickItem* parent,
const QmlContextObjectCallback& callback) {
disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0);
if (qmlComponent->isError()) {
for (const auto& error : qmlComponent->errors()) {
qCWarning(qmlLogging) << error.url() << error.line() << error;
}
qmlComponent->deleteLater();
return;
}
QObject* newObject = qmlComponent->beginCreate(qmlContext);
if (qmlComponent->isError()) {
for (const auto& error : qmlComponent->errors()) {
qCWarning(qmlLogging) << error.url() << error.line() << error;
}
if (!getRootItem()) {
qFatal("Unable to finish loading QML root");
}
qmlComponent->deleteLater();
return;
}
if (!newObject) {
if (!getRootItem()) {
qFatal("Could not load object as root item");
return;
}
qCWarning(qmlLogging) << "Unable to load QML item";
return;
}
qmlContext->engine()->setObjectOwnership(this, QQmlEngine::CppOwnership);
// All quick items should be focusable
QQuickItem* newItem = qobject_cast<QQuickItem*>(newObject);
if (newItem) {
// Make sure we make items focusable (critical for
// supporting keyboard shortcuts)
newItem->setFlag(QQuickItem::ItemIsFocusScope, true);
}
bool rootCreated = getRootItem() != nullptr;
// Make sure we will call callback for this codepath
// Call this before qmlComponent->completeCreate() otherwise ghost window appears
// If we already have a root, just set a couple of flags and the ancestry
if (rootCreated) {
callback(qmlContext, newItem);
if (!parent) {
parent = getRootItem();
}
// Allow child windows to be destroyed from JS
QQmlEngine::setObjectOwnership(newObject, QQmlEngine::JavaScriptOwnership);
newObject->setParent(parent);
newItem->setParentItem(parent);
} else {
// The root item is ready. Associate it with the window.
_sharedObject->setRootItem(newItem);
}
qmlComponent->completeCreate();
qmlComponent->deleteLater();
onItemCreated(qmlContext, newItem);
connect(newItem, SIGNAL(sendToScript(QVariant)), this, SIGNAL(fromQml(QVariant)));
if (!rootCreated) {
onRootCreated();
emit rootItemCreated(newItem);
// Call this callback after rootitem is set, otherwise VrMenu wont work
callback(qmlContext, newItem);
}
}
QQmlContext* OffscreenSurface::contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext) {
QQmlContext* targetContext = parent ? QQmlEngine::contextForObject(parent) : getSurfaceContext();
if (!targetContext) {
targetContext = getSurfaceContext();
}
if (getRootItem() && forceNewContext) {
targetContext = new QQmlContext(targetContext, targetContext->engine());
}
return targetContext;
}

View file

@ -0,0 +1,127 @@
//
// Created by Bradley Austin Davis on 2015-04-04
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_qml_OffscreenSurface_h
#define hifi_qml_OffscreenSurface_h
#include <atomic>
#include <queue>
#include <map>
#include <functional>
#include <QtCore/QUrl>
#include <QtCore/QSize>
#include <QtCore/QPointF>
#include <QtCore/QSharedPointer>
#include <QtCore/QTimer>
#include <QtQml/QJSValue>
class QWindow;
class QOpenGLContext;
class QQmlContext;
class QQmlEngine;
class QQmlComponent;
class QQuickWindow;
class QQuickItem;
class OffscreenQmlSharedObject;
namespace hifi { namespace qml {
namespace impl {
class SharedObject;
}
using QmlContextObjectCallback = ::std::function<void(QQmlContext*, QQuickItem*)>;
class OffscreenSurface : public QObject {
Q_OBJECT
public:
static const QmlContextObjectCallback DEFAULT_CONTEXT_CALLBACK;
using TextureAndFence = std::pair<uint32_t, void*>;
using MouseTranslator = std::function<QPoint(const QPointF&)>;
static void setSharedContext(QOpenGLContext* context);
OffscreenSurface();
virtual ~OffscreenSurface();
QSize size() const;
virtual void resize(const QSize& size);
void clearCache();
void setMaxFps(uint8_t maxFps);
// Optional values for event handling
void setProxyWindow(QWindow* window);
void setMouseTranslator(const MouseTranslator& mouseTranslator) { _mouseTranslator = mouseTranslator; }
void pause();
void resume();
bool isPaused() const;
QQuickItem* getRootItem();
QQuickWindow* getWindow();
QObject* getEventHandler();
QQmlContext* getSurfaceContext();
// Checks to see if a new texture is available. If one is, the function returns true and
// textureAndFence will be populated with the texture ID and a fence which will be signalled
// when the texture is safe to read.
// Returns false if no new texture is available
bool fetchTexture(TextureAndFence& textureAndFence);
static std::function<void(uint32_t, void*)> getDiscardLambda();
static size_t getUsedTextureMemory();
QPointF mapToVirtualScreen(const QPointF& originalPoint);
// For use from QML/JS
Q_INVOKABLE void load(const QUrl& qmlSource, QQuickItem* parent, const QJSValue& callback);
// For use from C++
Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void load(const QString& qmlSourceFile, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextObjectCallback& callback = DEFAULT_CONTEXT_CALLBACK);
public slots:
virtual void onFocusObjectChanged(QObject* newFocus) {}
signals:
void rootContextCreated(QQmlContext* rootContext);
void rootItemCreated(QQuickItem* rootContext);
protected:
bool eventFilter(QObject* originalDestination, QEvent* event) override;
bool filterEnabled(QObject* originalDestination, QEvent* event) const;
virtual void initializeEngine(QQmlEngine* engine);
virtual void loadInternal(const QUrl& qmlSource,
bool createNewContext,
QQuickItem* parent,
const QmlContextObjectCallback& callback) final;
virtual void finishQmlLoad(QQmlComponent* qmlComponent,
QQmlContext* qmlContext,
QQuickItem* parent,
const QmlContextObjectCallback& onQmlLoadedCallback) final;
virtual void onRootCreated() {}
virtual void onItemCreated(QQmlContext* context, QQuickItem* newItem) {}
virtual void onRootContextCreated(QQmlContext* qmlContext) {}
virtual QQmlContext* contextForUrl(const QUrl& qmlSource, QQuickItem* parent, bool forceNewContext);
private:
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } };
friend class hifi::qml::impl::SharedObject;
impl::SharedObject* const _sharedObject;
};
}} // namespace hifi::qml
#endif

Some files were not shown because too many files have changed in this diff Show more