Merge remote-tracking branch 'upstream/master' into avatar-verification

This commit is contained in:
Simon Walton 2019-04-04 14:01:35 -07:00
commit d6dc579331
99 changed files with 3710 additions and 2164 deletions

View file

@ -33,7 +33,7 @@
#include <NodeType.h>
#include <SharedUtil.h>
#include <PathUtils.h>
#include <image/Image.h>
#include <image/TextureProcessing.h>
#include "AssetServerLogging.h"
#include "BakeAssetTask.h"

View file

@ -22,7 +22,6 @@
#include <ScriptCache.h>
#include <EntityEditFilters.h>
#include <NetworkingConstants.h>
#include <AddressManager.h>
#include <hfm/ModelFormatRegistry.h>
#include "../AssignmentDynamicFactory.h"
@ -471,77 +470,7 @@ void EntityServer::startDynamicDomainVerification() {
qCDebug(entities) << "Starting Dynamic Domain Verification...";
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());
QHashIterator<QString, EntityItemID> i(localMap);
qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap";
while (i.hasNext()) {
i.next();
const auto& certificateID = i.key();
const auto& entityID = i.value();
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
if (entity) {
if (!entity->getProperties().verifyStaticCertificateProperties()) {
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << entityID << "failed"
<< "static certificate verification.";
// Delete the entity if it doesn't pass static certificate verification
tree->withWriteLock([&] {
tree->deleteEntity(entityID, true);
});
} else {
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/location");
QJsonObject request;
request["certificate_id"] = certificateID;
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] {
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
if (networkReply->error() == QNetworkReply::NoError) {
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
if (jsonObject["domain_id"].toString() != thisDomainID) {
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
if (!entity) {
qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID;
networkReply->deleteLater();
return;
}
if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) {
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID;
tree->withWriteLock([&] {
tree->deleteEntity(entityID, true);
});
} else {
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID;
}
} else {
qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID;
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID
<< "More info:" << jsonObject;
}
networkReply->deleteLater();
});
}
} else {
qCWarning(entities) << "During DDV, an entity with ID" << entityID << "was NOT found in the Entity Tree!";
}
}
tree->startDynamicDomainVerificationOnServer((float) _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS / MSECS_PER_SECOND);
int nextInterval = qrand() % ((_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS;
qCDebug(entities) << "Restarting Dynamic Domain Verification timer for" << nextInterval / 1000 << "seconds";

View file

@ -17,8 +17,8 @@ if (WIN32)
ExternalProject_Add(
${EXTERNAL_NAME}
URL https://public.highfidelity.com/dependencies/ovr_sdk_win_1.26.0_public.zip
URL_MD5 06804ff9727b910dcd04a37c800053b5
URL https://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.35.0.zip
URL_MD5 1e3e8b2101387af07ff9c841d0ea285e
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" <SOURCE_DIR>/CMakeLists.txt
LOG_DOWNLOAD 1

View file

@ -53,10 +53,5 @@ macro(add_crashpad)
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CRASHPAD_HANDLER_EXE_PATH} "$<TARGET_FILE_DIR:${TARGET_NAME}>/"
)
install(
PROGRAMS ${CRASHPAD_HANDLER_EXE_PATH}
DESTINATION ${INTERFACE_INSTALL_DIR}
COMPONENT ${CLIENT_COMPONENT}
)
endif ()
endmacro()

View file

@ -0,0 +1,74 @@
#
# Copyright 2015 High Fidelity, Inc.
# Created by Olivier Prat on 2019/03/26
#
# Distributed under the Apache License, Version 2.0.
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#
macro(TARGET_OPENEXR)
if (NOT ANDROID)
set(openexr_config_file "${VCPKG_INSTALL_ROOT}/include/OpenEXR/OpenEXRConfig.h")
if(EXISTS ${openexr_config_file})
file(STRINGS
${openexr_config_file}
TMP
REGEX "#define OPENEXR_VERSION_STRING.*$")
string(REGEX MATCHALL "[0-9.]+" OPENEXR_VERSION ${TMP})
file(STRINGS
${openexr_config_file}
TMP
REGEX "#define OPENEXR_VERSION_MAJOR.*$")
string(REGEX MATCHALL "[0-9]" OPENEXR_MAJOR_VERSION ${TMP})
file(STRINGS
${openexr_config_file}
TMP
REGEX "#define OPENEXR_VERSION_MINOR.*$")
string(REGEX MATCHALL "[0-9]" OPENEXR_MINOR_VERSION ${TMP})
endif()
foreach(OPENEXR_LIB
IlmImf
IlmImfUtil
Half
Iex
IexMath
Imath
IlmThread)
# OpenEXR libraries may be suffixed with the version number, so we search
# using both versioned and unversioned names.
find_library(OPENEXR_${OPENEXR_LIB}_LIBRARY_RELEASE
NAMES
${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION}_s
${OPENEXR_LIB}_s
PATHS ${VCPKG_INSTALL_ROOT}/lib NO_DEFAULT_PATH
)
#mark_as_advanced(OPENEXR_${OPENEXR_LIB}_LIBRARY)
if(OPENEXR_${OPENEXR_LIB}_LIBRARY_RELEASE)
list(APPEND OPENEXR_LIBRARY_RELEASE ${OPENEXR_${OPENEXR_LIB}_LIBRARY_RELEASE})
endif()
# OpenEXR libraries may be suffixed with the version number, so we search
# using both versioned and unversioned names.
find_library(OPENEXR_${OPENEXR_LIB}_LIBRARY_DEBUG
NAMES
${OPENEXR_LIB}-${OPENEXR_MAJOR_VERSION}_${OPENEXR_MINOR_VERSION}_s_d
${OPENEXR_LIB}_s_d
PATHS ${VCPKG_INSTALL_ROOT}/debug/lib NO_DEFAULT_PATH
)
#mark_as_advanced(OPENEXR_${OPENEXR_LIB}_DEBUG_LIBRARY)
if(OPENEXR_${OPENEXR_LIB}_LIBRARY_DEBUG)
list(APPEND OPENEXR_LIBRARY_DEBUG ${OPENEXR_${OPENEXR_LIB}_LIBRARY_DEBUG})
endif()
endforeach(OPENEXR_LIB)
select_library_configurations(OPENEXR)
target_link_libraries(${TARGET_NAME} ${OPENEXR_LIBRARY})
endif()
endmacro()

View file

@ -1,4 +1,4 @@
Source: hifi-deps
Version: 0
Version: 0.1
Description: Collected dependencies for High Fidelity applications
Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openssl (windows), tbb (!android&!osx), zlib
Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openexr (!android), openssl (windows), tbb (!android&!osx), zlib

View file

@ -0,0 +1,4 @@
Source: openexr
Version: 2.3.0-2
Description: OpenEXR is a high dynamic-range (HDR) image file format developed by Industrial Light & Magic for use in computer imaging applications
Build-Depends: zlib

View file

@ -0,0 +1,87 @@
include(FindPackageHandleStandardArgs)
find_path(OpenEXR_INCLUDE_DIRS OpenEXR/OpenEXRConfig.h)
find_path(OPENEXR_INCLUDE_PATHS NAMES ImfRgbaFile.h PATH_SUFFIXES OpenEXR)
file(STRINGS "${OpenEXR_INCLUDE_DIRS}/OpenEXR/OpenEXRConfig.h" OPENEXR_CONFIG_H)
string(REGEX REPLACE "^.*define OPENEXR_VERSION_MAJOR ([0-9]+).*$" "\\1" OpenEXR_VERSION_MAJOR "${OPENEXR_CONFIG_H}")
string(REGEX REPLACE "^.*define OPENEXR_VERSION_MINOR ([0-9]+).*$" "\\1" OpenEXR_VERSION_MINOR "${OPENEXR_CONFIG_H}")
set(OpenEXR_LIB_SUFFIX "${OpenEXR_VERSION_MAJOR}_${OpenEXR_VERSION_MINOR}")
include(SelectLibraryConfigurations)
if(NOT OpenEXR_BASE_LIBRARY)
find_library(OpenEXR_BASE_LIBRARY_RELEASE NAMES IlmImf-${OpenEXR_LIB_SUFFIX})
find_library(OpenEXR_BASE_LIBRARY_DEBUG NAMES IlmImf-${OpenEXR_LIB_SUFFIX}_d)
select_library_configurations(OpenEXR_BASE)
endif()
if(NOT OpenEXR_UTIL_LIBRARY)
find_library(OpenEXR_UTIL_LIBRARY_RELEASE NAMES IlmImfUtil-${OpenEXR_LIB_SUFFIX})
find_library(OpenEXR_UTIL_LIBRARY_DEBUG NAMES IlmImfUtil-${OpenEXR_LIB_SUFFIX}_d)
select_library_configurations(OpenEXR_UTIL)
endif()
if(NOT OpenEXR_HALF_LIBRARY)
find_library(OpenEXR_HALF_LIBRARY_RELEASE NAMES Half-${OpenEXR_LIB_SUFFIX})
find_library(OpenEXR_HALF_LIBRARY_DEBUG NAMES Half-${OpenEXR_LIB_SUFFIX}_d)
select_library_configurations(OpenEXR_HALF)
endif()
if(NOT OpenEXR_IEX_LIBRARY)
find_library(OpenEXR_IEX_LIBRARY_RELEASE NAMES Iex-${OpenEXR_LIB_SUFFIX})
find_library(OpenEXR_IEX_LIBRARY_DEBUG NAMES Iex-${OpenEXR_LIB_SUFFIX}_d)
select_library_configurations(OpenEXR_IEX)
endif()
if(NOT OpenEXR_MATH_LIBRARY)
find_library(OpenEXR_MATH_LIBRARY_RELEASE NAMES Imath-${OpenEXR_LIB_SUFFIX})
find_library(OpenEXR_MATH_LIBRARY_DEBUG NAMES Imath-${OpenEXR_LIB_SUFFIX}_d)
select_library_configurations(OpenEXR_MATH)
endif()
if(NOT OpenEXR_THREAD_LIBRARY)
find_library(OpenEXR_THREAD_LIBRARY_RELEASE NAMES IlmThread-${OpenEXR_LIB_SUFFIX})
find_library(OpenEXR_THREAD_LIBRARY_DEBUG NAMES IlmThread-${OpenEXR_LIB_SUFFIX}_d)
select_library_configurations(OpenEXR_THREAD)
endif()
if(NOT OpenEXR_IEXMATH_LIBRARY)
find_library(OpenEXR_IEXMATH_LIBRARY_RELEASE NAMES IexMath-${OpenEXR_LIB_SUFFIX})
find_library(OpenEXR_IEXMATH_LIBRARY_DEBUG NAMES IexMath-${OpenEXR_LIB_SUFFIX}d)
select_library_configurations(OpenEXR_IEXMATH)
endif()
set(OPENEXR_HALF_LIBRARY "${OpenEXR_HALF_LIBRARY}")
set(OPENEXR_IEX_LIBRARY "${OpenEXR_IEX_LIBRARY}")
set(OPENEXR_IMATH_LIBRARY "${OpenEXR_MATH_LIBRARY}")
set(OPENEXR_ILMIMF_LIBRARY "${OpenEXR_BASE_LIBRARY}")
set(OPENEXR_ILMIMFUTIL_LIBRARY "${OpenEXR_UTIL_LIBRARY}")
set(OPENEXR_ILMTHREAD_LIBRARY "${OpenEXR_THREAD_LIBRARY}")
set(OpenEXR_LIBRARY "${OpenEXR_BASE_LIBRARY}")
set(OpenEXR_LIBRARIES
${OpenEXR_LIBRARY}
${OpenEXR_MATH_LIBRARY}
${OpenEXR_IEXMATH_LIBRARY}
${OpenEXR_UTIL_LIBRARY}
${OpenEXR_HALF_LIBRARY}
${OpenEXR_IEX_LIBRARY}
${OpenEXR_THREAD_LIBRARY}
)
set(OPENEXR_LIBRARIES
${OPENEXR_HALF_LIBRARY}
${OPENEXR_IEX_LIBRARY}
${OPENEXR_IMATH_LIBRARY}
${OPENEXR_ILMIMF_LIBRARY}
${OPENEXR_ILMTHREAD_LIBRARY}
)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenEXR REQUIRED_VARS OpenEXR_LIBRARIES OpenEXR_INCLUDE_DIRS)
if(OpenEXR_FOUND)
set(OPENEXR_FOUND 1)
endif()

View file

@ -0,0 +1,19 @@
diff --git a/OpenEXR/IlmImf/CMakeLists.txt b/OpenEXR/IlmImf/CMakeLists.txt
index e1a8740..d31cf68 100644
--- a/OpenEXR/IlmImf/CMakeLists.txt
+++ b/OpenEXR/IlmImf/CMakeLists.txt
@@ -2,14 +2,6 @@
SET(CMAKE_INCLUDE_CURRENT_DIR 1)
-IF (WIN32)
- SET(RUNTIME_DIR ${OPENEXR_PACKAGE_PREFIX}/bin)
- SET(WORKING_DIR ${RUNTIME_DIR})
-ELSE ()
- SET(RUNTIME_DIR ${OPENEXR_PACKAGE_PREFIX}/lib)
- SET(WORKING_DIR .)
-ENDIF ()
-
SET(BUILD_B44EXPLOGTABLE OFF)
IF (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/b44ExpLogTable.h")
SET(BUILD_B44EXPLOGTABLE ON)

View file

@ -0,0 +1,74 @@
include(vcpkg_common_functions)
set(OPENEXR_VERSION 2.3.0)
set(OPENEXR_HASH 268ae64b40d21d662f405fba97c307dad1456b7d996a447aadafd41b640ca736d4851d9544b4741a94e7b7c335fe6e9d3b16180e710671abfc0c8b2740b147b2)
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO openexr/openexr
REF v${OPENEXR_VERSION}
SHA512 ${OPENEXR_HASH}
HEAD_REF master
PATCHES "fix_install_ilmimf.patch"
)
set(OPENEXR_STATIC ON)
set(OPENEXR_SHARED OFF)
vcpkg_configure_cmake(SOURCE_PATH ${SOURCE_PATH}
PREFER_NINJA
OPTIONS
-DOPENEXR_BUILD_PYTHON_LIBS=OFF
-DOPENEXR_BUILD_VIEWERS=OFF
-DOPENEXR_ENABLE_TESTS=OFF
-DOPENEXR_RUN_FUZZ_TESTS=OFF
-DOPENEXR_BUILD_SHARED=${OPENEXR_SHARED}
-DOPENEXR_BUILD_STATIC=${OPENEXR_STATIC}
OPTIONS_DEBUG
-DILMBASE_PACKAGE_PREFIX=${CURRENT_INSTALLED_DIR}/debug
OPTIONS_RELEASE
-DILMBASE_PACKAGE_PREFIX=${CURRENT_INSTALLED_DIR})
vcpkg_install_cmake()
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share)
# NOTE: Only use ".exe" extension on Windows executables.
# Is there a cleaner way to do this?
if(WIN32)
set(EXECUTABLE_SUFFIX ".exe")
else()
set(EXECUTABLE_SUFFIX "")
endif()
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrenvmap${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrheader${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmakepreview${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmaketiled${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmultipart${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrmultiview${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/debug/bin/exrstdattr${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrenvmap${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrheader${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmakepreview${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmaketiled${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmultipart${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrmultiview${EXECUTABLE_SUFFIX})
file(REMOVE ${CURRENT_PACKAGES_DIR}/bin/exrstdattr${EXECUTABLE_SUFFIX})
vcpkg_copy_pdbs()
if (OPENEXR_STATIC)
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin)
endif()
if (VCPKG_CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(OPENEXR_PORT_DIR "openexr")
else()
set(OPENEXR_PORT_DIR "OpenEXR")
endif()
file(COPY ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR})
file(RENAME ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR}/LICENSE ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR}/copyright)
file(COPY ${CMAKE_CURRENT_LIST_DIR}/FindOpenEXR.cmake DESTINATION ${CURRENT_PACKAGES_DIR}/share/${OPENEXR_PORT_DIR})

View file

@ -1734,7 +1734,7 @@ void DomainServer::processOctreeDataPersistMessage(QSharedPointer<ReceivedMessag
f.write(data);
OctreeUtils::RawEntityData entityData;
if (entityData.readOctreeDataInfoFromData(data)) {
qCDebug(domain_server) << "Wrote new entities file" << entityData.id << entityData.version;
qCDebug(domain_server) << "Wrote new entities file" << entityData.id << entityData.dataVersion;
} else {
qCDebug(domain_server) << "Failed to read new octree data info";
}

View file

@ -6709,6 +6709,11 @@ void Application::updateRenderArgs(float deltaTime) {
// Configure the type of display / stereo
appRenderArgs._renderArgs._displayMode = (isHMDMode() ? RenderArgs::STEREO_HMD : RenderArgs::STEREO_MONITOR);
}
appRenderArgs._renderArgs._stencilMode = getActiveDisplayPlugin()->getStencilMaskMode();
if (appRenderArgs._renderArgs._stencilMode == StencilMode::MESH) {
appRenderArgs._renderArgs._stencilMaskOperator = getActiveDisplayPlugin()->getStencilMaskMeshOperator();
}
}
{

View file

@ -497,7 +497,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
// it might not fire until after we create a new instance for the same remote avatar, which creates a race
// on the creation of entities for that avatar instance and the deletion of entities for this instance
avatar->removeAvatarEntitiesFromTree();
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble || removalReason == KillAvatarReason::NoReason) {
emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID());
emit DependencyManager::get<UsersScriptingInterface>()->enteredIgnoreRadius();

View file

@ -17,6 +17,7 @@
#include <QJsonObject>
#include <DependencyManager.h>
#include <QtNetwork/QNetworkReply>
#include <EntityItemID.h>
#include "AccountManager.h"
@ -65,7 +66,7 @@ signals:
void availableUpdatesResult(QJsonObject result);
void updateItemResult(QJsonObject result);
void updateCertificateStatus(const QString& certID, uint certStatus);
void updateCertificateStatus(const EntityItemID& entityID, uint certStatus);
public slots:
void buySuccess(QNetworkReply* reply);

View file

@ -19,6 +19,7 @@
#include <QPixmap>
#include <EntityItemID.h>
#include <DependencyManager.h>
class QmlCommerce : public QObject, public Dependency {
@ -49,7 +50,7 @@ signals:
void availableUpdatesResult(QJsonObject result);
void updateItemResult(QJsonObject result);
void updateCertificateStatus(const QString& certID, uint certStatus);
void updateCertificateStatus(const EntityItemID& entityID, uint certStatus);
void transferAssetToNodeResult(QJsonObject result);
void transferAssetToUsernameResult(QJsonObject result);

View file

@ -816,18 +816,18 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> pack
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
int status;
int certIDByteArraySize;
int idByteArraySize;
int textByteArraySize;
int challengingNodeUUIDByteArraySize;
packet->readPrimitive(&certIDByteArraySize);
packet->readPrimitive(&idByteArraySize);
packet->readPrimitive(&textByteArraySize); // returns a cast char*, size
if (challengeOriginatedFromClient) {
packet->readPrimitive(&challengingNodeUUIDByteArraySize);
}
// "encryptedText" is now a series of random bytes, a nonce
QByteArray certID = packet->read(certIDByteArraySize);
QByteArray id = packet->read(idByteArraySize);
QByteArray text = packet->read(textByteArraySize);
QByteArray challengingNodeUUID;
if (challengeOriginatedFromClient) {
@ -853,32 +853,32 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> pack
textByteArray = sig.toUtf8();
}
textByteArraySize = textByteArray.size();
int certIDSize = certID.size();
int idSize = id.size();
// setup the packet
if (challengeOriginatedFromClient) {
auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
certIDSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
true);
textPacket->writePrimitive(certIDSize);
textPacket->writePrimitive(idSize);
textPacket->writePrimitive(textByteArraySize);
textPacket->writePrimitive(challengingNodeUUIDByteArraySize);
textPacket->write(certID);
textPacket->write(id);
textPacket->write(textByteArray);
textPacket->write(challengingNodeUUID);
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID;
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for id" << id;
nodeList->sendPacket(std::move(textPacket), *sendingNode);
} else {
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + textByteArraySize + 2 * sizeof(int), true);
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true);
textPacket->writePrimitive(certIDSize);
textPacket->writePrimitive(idSize);
textPacket->writePrimitive(textByteArraySize);
textPacket->write(certID);
textPacket->write(id);
textPacket->write(textByteArray);
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID;
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id;
nodeList->sendPacket(std::move(textPacket), *sendingNode);
}

View file

@ -292,7 +292,7 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
setLastInspectedEntity(entityID);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityID, _entityPropertyFlags);
auto nodeList = DependencyManager::get<NodeList>();
@ -328,31 +328,31 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
} else {
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
QByteArray certID = entityProperties.getCertificateID().toUtf8();
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
QByteArray id = entityID.toByteArray();
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(entityID, ownerKey);
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
int certIDByteArraySize = certID.length();
int idByteArraySize = id.length();
int textByteArraySize = text.length();
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
idByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(idByteArraySize);
challengeOwnershipPacket->writePrimitive(textByteArraySize);
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(id);
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");
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityID));
return;
} else {
startChallengeOwnershipTimer();
startChallengeOwnershipTimer(entityID);
}
}
} else {
@ -370,14 +370,14 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
// 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>();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
emit ledger->updateCertificateStatus(entityID, (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!";
emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(entityID);
qCDebug(context_overlay) << "Entity" << entityID << "failed static certificate verification!";
}
}
@ -395,14 +395,14 @@ void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) {
}
}
void ContextOverlayInterface::startChallengeOwnershipTimer() {
void ContextOverlayInterface::startChallengeOwnershipTimer(const EntityItemID& entityItemID) {
auto ledger = DependencyManager::get<Ledger>();
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags);
connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() {
qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity;
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
qCDebug(entities) << "Ownership challenge timed out for" << entityItemID;
emit ledger->updateCertificateStatus(entityItemID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(entityItemID);
});
_challengeOwnershipTimeoutTimer.start(5000);
@ -413,23 +413,22 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer
_challengeOwnershipTimeoutTimer.stop();
int certIDByteArraySize;
int idByteArraySize;
int textByteArraySize;
packet->readPrimitive(&certIDByteArraySize);
packet->readPrimitive(&idByteArraySize);
packet->readPrimitive(&textByteArraySize);
QString certID(packet->read(certIDByteArraySize));
EntityItemID id(packet->read(idByteArraySize));
QString text(packet->read(textByteArraySize));
EntityItemID id;
bool verificationSuccess = DependencyManager::get<EntityTreeRenderer>()->getTree()->verifyNonce(certID, text, id);
bool verificationSuccess = DependencyManager::get<EntityTreeRenderer>()->getTree()->verifyNonce(id, text);
if (verificationSuccess) {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationSuccess(_lastInspectedEntity);
emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationSuccess(id);
} else {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(id);
}
}

View file

@ -46,7 +46,7 @@ public:
ContextOverlayInterface();
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; }
void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; }
void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); }
void setEnabled(bool enabled);
bool getEnabled() { return _enabled; }
bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; }
@ -83,7 +83,6 @@ private:
EntityItemID _mouseDownEntity;
quint64 _mouseDownEntityTimestamp;
EntityItemID _currentEntityWithContextOverlay;
EntityItemID _lastInspectedEntity;
QString _entityMarketplaceID;
bool _contextOverlayJustClicked { false };
@ -94,7 +93,7 @@ private:
void deletingEntity(const EntityItemID& entityItemID);
Q_INVOKABLE void startChallengeOwnershipTimer();
Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID);
QTimer _challengeOwnershipTimeoutTimer;
};

View file

@ -16,7 +16,7 @@
#include <QtCore/QFile>
#include <QtNetwork/QNetworkReply>
#include <image/Image.h>
#include <image/TextureProcessing.h>
#include <ktx/KTX.h>
#include <NetworkAccessManager.h>
#include <SharedUtil.h>

View file

@ -18,7 +18,7 @@
#include <QDir>
#include <QImageReader>
#include <image/Image.h>
#include <image/TextureProcessing.h>
#include "Baker.h"

View file

@ -15,7 +15,7 @@
#include <QtCore/QFileInfo>
#include <QHash>
#include <image/Image.h>
#include <image/TextureProcessing.h>
class TextureFileNamer {
public:

View file

@ -109,7 +109,7 @@ bool Basic2DWindowOpenGLDisplayPlugin::internalActivate() {
return Parent::internalActivate();
}
void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPointer& compositeFramebuffer) {
void Basic2DWindowOpenGLDisplayPlugin::compositeExtra() {
#if defined(Q_OS_ANDROID)
auto& virtualPadManager = VirtualPad::Manager::instance();
if(virtualPadManager.getLeftVirtualPad()->isShown()) {
@ -121,7 +121,7 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPoin
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setFramebuffer(compositeFramebuffer);
batch.setFramebuffer(_compositeFramebuffer);
batch.resetViewTransform();
batch.setProjectionTransform(mat4());
batch.setPipeline(_cursorPipeline);
@ -140,7 +140,7 @@ void Basic2DWindowOpenGLDisplayPlugin::compositeExtra(const gpu::FramebufferPoin
});
}
#endif
Parent::compositeExtra(compositeFramebuffer);
Parent::compositeExtra();
}
static const uint32_t MIN_THROTTLE_CHECK_FRAMES = 60;

View file

@ -33,7 +33,7 @@ public:
virtual bool isThrottled() const override;
virtual void compositeExtra(const gpu::FramebufferPointer&) override;
virtual void compositeExtra() override;
virtual void pluginUpdate() override {};

View file

@ -379,6 +379,14 @@ void OpenGLDisplayPlugin::customizeContext() {
scissorState->setDepthTest(gpu::State::DepthTest(false));
scissorState->setScissorEnable(true);
{
#ifdef Q_OS_ANDROID
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB);
#else
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture);
#endif
_simplePipeline = gpu::Pipeline::create(program, scissorState);
}
{
#ifdef Q_OS_ANDROID
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureGammaLinearToSRGB);
@ -388,59 +396,29 @@ void OpenGLDisplayPlugin::customizeContext() {
_presentPipeline = gpu::Pipeline::create(program, scissorState);
}
// HUD operator
{
gpu::PipelinePointer hudPipeline;
{
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture);
hudPipeline = gpu::Pipeline::create(program, blendState);
}
gpu::PipelinePointer hudMirrorPipeline;
{
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX);
hudMirrorPipeline = gpu::Pipeline::create(program, blendState);
}
_hudOperator = [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, const gpu::FramebufferPointer& compositeFramebuffer, bool mirror) {
auto hudStereo = isStereo();
auto hudCompositeFramebufferSize = compositeFramebuffer->getSize();
std::array<glm::ivec4, 2> hudEyeViewports;
for_each_eye([&](Eye eye) {
hudEyeViewports[eye] = eyeViewport(eye);
});
if (hudPipeline && hudTexture) {
batch.enableStereo(false);
batch.setPipeline(mirror ? hudMirrorPipeline : hudPipeline);
batch.setResourceTexture(0, hudTexture);
if (hudStereo) {
for_each_eye([&](Eye eye) {
batch.setViewportTransform(hudEyeViewports[eye]);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
}
};
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture);
_hudPipeline = gpu::Pipeline::create(program, blendState);
}
{
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTextureMirroredX);
_mirrorHUDPipeline = gpu::Pipeline::create(program, blendState);
}
{
gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTransformedTexture);
_cursorPipeline = gpu::Pipeline::create(program, blendState);
}
}
updateCompositeFramebuffer();
}
void OpenGLDisplayPlugin::uncustomizeContext() {
_presentPipeline.reset();
_cursorPipeline.reset();
_hudOperator = DEFAULT_HUD_OPERATOR;
_hudPipeline.reset();
_mirrorHUDPipeline.reset();
_compositeFramebuffer.reset();
withPresentThreadLock([&] {
_currentFrame.reset();
_lastFrame = nullptr;
@ -532,16 +510,24 @@ void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const {
});
}
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor) {
renderFromTexture(batch, texture, viewport, scissor, nullptr);
}
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& destFbo, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) {
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) {
auto fbo = gpu::FramebufferPointer();
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(destFbo);
batch.setFramebuffer(fbo);
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
batch.setStateScissorRect(scissor);
batch.setViewportTransform(viewport);
batch.setResourceTexture(0, texture);
#ifndef USE_GLES
batch.setPipeline(_presentPipeline);
#else
batch.setPipeline(_simplePipeline);
#endif
batch.draw(gpu::TRIANGLE_STRIP, 4);
if (copyFbo) {
gpu::Vec4i copyFboRect(0, 0, copyFbo->getWidth(), copyFbo->getHeight());
@ -567,7 +553,7 @@ void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::Textur
batch.setViewportTransform(copyFboRect);
batch.setStateScissorRect(copyFboRect);
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, {0.0f, 0.0f, 0.0f, 1.0f});
batch.blit(destFbo, sourceRect, copyFbo, copyRect);
batch.blit(fbo, sourceRect, copyFbo, copyRect);
}
}
@ -595,14 +581,41 @@ void OpenGLDisplayPlugin::updateFrameData() {
});
}
void OpenGLDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compositeFramebuffer) {
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> OpenGLDisplayPlugin::getHUDOperator() {
auto hudPipeline = _hudPipeline;
auto hudMirrorPipeline = _mirrorHUDPipeline;
auto hudStereo = isStereo();
auto hudCompositeFramebufferSize = _compositeFramebuffer->getSize();
std::array<glm::ivec4, 2> hudEyeViewports;
for_each_eye([&](Eye eye) {
hudEyeViewports[eye] = eyeViewport(eye);
});
return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, bool mirror) {
if (hudPipeline && hudTexture) {
batch.enableStereo(false);
batch.setPipeline(mirror ? hudMirrorPipeline : hudPipeline);
batch.setResourceTexture(0, hudTexture);
if (hudStereo) {
for_each_eye([&](Eye eye) {
batch.setViewportTransform(hudEyeViewports[eye]);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
batch.setViewportTransform(ivec4(uvec2(0), hudCompositeFramebufferSize));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
}
};
}
void OpenGLDisplayPlugin::compositePointer() {
auto& cursorManager = Cursor::Manager::instance();
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
auto cursorTransform = DependencyManager::get<CompositorHelper>()->getReticleTransform(glm::mat4());
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setProjectionTransform(mat4());
batch.setFramebuffer(compositeFramebuffer);
batch.setFramebuffer(_compositeFramebuffer);
batch.setPipeline(_cursorPipeline);
batch.setResourceTexture(0, cursorData.texture);
batch.resetViewTransform();
@ -613,13 +626,34 @@ void OpenGLDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compos
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
} else {
batch.setViewportTransform(ivec4(uvec2(0), compositeFramebuffer->getSize()));
batch.setViewportTransform(ivec4(uvec2(0), _compositeFramebuffer->getSize()));
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
});
}
void OpenGLDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& compositeFramebuffer) {
void OpenGLDisplayPlugin::compositeScene() {
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setFramebuffer(_compositeFramebuffer);
batch.setViewportTransform(ivec4(uvec2(), _compositeFramebuffer->getSize()));
batch.setStateScissorRect(ivec4(uvec2(), _compositeFramebuffer->getSize()));
batch.resetViewTransform();
batch.setProjectionTransform(mat4());
batch.setPipeline(_simplePipeline);
batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
}
void OpenGLDisplayPlugin::compositeLayers() {
updateCompositeFramebuffer();
{
PROFILE_RANGE_EX(render_detail, "compositeScene", 0xff0077ff, (uint64_t)presentCount())
compositeScene();
}
#ifdef HIFI_ENABLE_NSIGHT_DEBUG
if (false) // do not draw the HUD if running nsight debug
#endif
@ -633,35 +667,23 @@ void OpenGLDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& composi
{
PROFILE_RANGE_EX(render_detail, "compositeExtra", 0xff0077ff, (uint64_t)presentCount())
compositeExtra(compositeFramebuffer);
compositeExtra();
}
// Draw the pointer last so it's on top of everything
auto compositorHelper = DependencyManager::get<CompositorHelper>();
if (compositorHelper->getReticleVisible()) {
PROFILE_RANGE_EX(render_detail, "compositePointer", 0xff0077ff, (uint64_t)presentCount())
compositePointer(compositeFramebuffer);
compositePointer();
}
}
void OpenGLDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
void OpenGLDisplayPlugin::internalPresent() {
render([&](gpu::Batch& batch) {
// Note: _displayTexture must currently be the same size as the display.
uvec2 dims = _displayTexture ? uvec2(_displayTexture->getDimensions()) : getSurfacePixels();
auto viewport = ivec4(uvec2(0), dims);
gpu::TexturePointer finalTexture;
if (_displayTexture) {
finalTexture = _displayTexture;
} else if (compositeFramebuffer) {
finalTexture = compositeFramebuffer->getRenderBuffer(0);
} else {
qCWarning(displayPlugins) << "No valid texture for output";
}
if (finalTexture) {
renderFromTexture(batch, finalTexture, viewport, viewport);
}
renderFromTexture(batch, _displayTexture ? _displayTexture : _compositeFramebuffer->getRenderBuffer(0), viewport, viewport);
});
swapBuffers();
_presentRate.increment();
@ -678,7 +700,7 @@ void OpenGLDisplayPlugin::present() {
}
incrementPresentCount();
if (_currentFrame && _currentFrame->framebuffer) {
if (_currentFrame) {
auto correction = getViewCorrection();
getGLBackend()->setCameraCorrection(correction, _prevRenderView);
_prevRenderView = correction * _currentFrame->view;
@ -698,18 +720,18 @@ void OpenGLDisplayPlugin::present() {
// Write all layers to a local framebuffer
{
PROFILE_RANGE_EX(render, "composite", 0xff00ffff, frameId)
compositeLayers(_currentFrame->framebuffer);
compositeLayers();
}
// Take the composite framebuffer and send it to the output device
{
PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId)
internalPresent(_currentFrame->framebuffer);
internalPresent();
}
gpu::Backend::freeGPUMemSize.set(gpu::gl::getFreeDedicatedMemory());
} else if (alwaysPresent()) {
internalPresent(nullptr);
internalPresent();
}
_movingAveragePresent.addSample((float)(usecTimestampNow() - startPresent));
}
@ -766,12 +788,7 @@ bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) {
}
QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const {
if (!_currentFrame || !_currentFrame->framebuffer) {
return QImage();
}
auto compositeFramebuffer = _currentFrame->framebuffer;
auto size = compositeFramebuffer->getSize();
auto size = _compositeFramebuffer->getSize();
if (isHmd()) {
size.x /= 2;
}
@ -789,7 +806,7 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const {
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32);
withOtherThreadContext([&] {
glBackend->downloadFramebuffer(compositeFramebuffer, ivec4(corner, bestSize), screenshot);
glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot);
});
return screenshot.mirrored(false, true);
}
@ -841,7 +858,7 @@ bool OpenGLDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
}
ivec4 OpenGLDisplayPlugin::eyeViewport(Eye eye) const {
auto vpSize = glm::uvec2(getRecommendedRenderSize());
uvec2 vpSize = _compositeFramebuffer->getSize();
vpSize.x /= 2;
uvec2 vpPos;
if (eye == Eye::Right) {
@ -874,6 +891,14 @@ void OpenGLDisplayPlugin::render(std::function<void(gpu::Batch& batch)> f) {
OpenGLDisplayPlugin::~OpenGLDisplayPlugin() {
}
void OpenGLDisplayPlugin::updateCompositeFramebuffer() {
auto renderSize = glm::uvec2(getRecommendedRenderSize());
if (!_compositeFramebuffer || _compositeFramebuffer->getSize() != renderSize) {
_compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_RGBA_32, renderSize.x, renderSize.y));
// _compositeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("OpenGLDisplayPlugin::composite", gpu::Element::COLOR_SRGBA_32, renderSize.x, renderSize.y));
}
}
void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) {
#if !defined(USE_GLES)
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();

View file

@ -94,10 +94,14 @@ protected:
// is not populated
virtual bool alwaysPresent() const { return false; }
void updateCompositeFramebuffer();
virtual QThread::Priority getPresentPriority() { return QThread::HighPriority; }
virtual void compositeLayers(const gpu::FramebufferPointer&);
virtual void compositePointer(const gpu::FramebufferPointer&);
virtual void compositeExtra(const gpu::FramebufferPointer&) {};
virtual void compositeLayers();
virtual void compositeScene();
virtual std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> getHUDOperator();
virtual void compositePointer();
virtual void compositeExtra() {};
// These functions must only be called on the presentation thread
virtual void customizeContext();
@ -112,10 +116,10 @@ protected:
virtual void deactivateSession() {}
// Plugin specific functionality to send the composed scene to the output window or device
virtual void internalPresent(const gpu::FramebufferPointer&);
virtual void internalPresent();
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& destFbo = nullptr, const gpu::FramebufferPointer& copyFbo = nullptr);
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& fbo);
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor);
virtual void updateFrameData();
virtual glm::mat4 getViewCorrection() { return glm::mat4(); }
@ -138,8 +142,14 @@ protected:
gpu::FramePointer _currentFrame;
gpu::Frame* _lastFrame { nullptr };
mat4 _prevRenderView;
gpu::FramebufferPointer _compositeFramebuffer;
gpu::PipelinePointer _hudPipeline;
gpu::PipelinePointer _mirrorHUDPipeline;
gpu::ShaderPointer _mirrorHUDPS;
gpu::PipelinePointer _simplePipeline;
gpu::PipelinePointer _presentPipeline;
gpu::PipelinePointer _cursorPipeline;
gpu::TexturePointer _displayTexture;
gpu::TexturePointer _displayTexture{};
float _compositeHUDAlpha { 1.0f };
struct CursorData {
@ -175,9 +185,5 @@ protected:
// be serialized through this mutex
mutable Mutex _presentMutex;
float _hudAlpha{ 1.0f };
private:
gpu::PipelinePointer _presentPipeline;
};

View file

@ -24,7 +24,7 @@ public:
protected:
void updatePresentPose() override;
void hmdPresent(const gpu::FramebufferPointer&) override {}
void hmdPresent() override {}
bool isHmdMounted() const override { return true; }
bool internalActivate() override;
private:

View file

@ -114,23 +114,20 @@ void HmdDisplayPlugin::internalDeactivate() {
void HmdDisplayPlugin::customizeContext() {
Parent::customizeContext();
_hudOperator = _hudRenderer.build();
_hudRenderer.build();
}
void HmdDisplayPlugin::uncustomizeContext() {
// This stops the weirdness where if the preview was disabled, on switching back to 2D,
// the vsync was stuck in the disabled state. No idea why that happens though.
_disablePreview = false;
if (_currentFrame && _currentFrame->framebuffer) {
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(_currentFrame->framebuffer);
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
});
}
_hudRenderer = {};
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(_compositeFramebuffer);
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(0));
});
_hudRenderer = HUDRenderer();
_previewTexture.reset();
Parent::uncustomizeContext();
}
@ -177,11 +174,11 @@ float HmdDisplayPlugin::getLeftCenterPixel() const {
return leftCenterPixel;
}
void HmdDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
void HmdDisplayPlugin::internalPresent() {
PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)presentCount())
// Composite together the scene, hud and mouse cursor
hmdPresent(compositeFramebuffer);
hmdPresent();
if (_displayTexture) {
// Note: _displayTexture must currently be the same size as the display.
@ -263,7 +260,7 @@ void HmdDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeF
viewport.z *= 2;
}
renderFromTexture(batch, compositeFramebuffer->getRenderBuffer(0), viewport, scissor, nullptr, fbo);
renderFromTexture(batch, _compositeFramebuffer->getRenderBuffer(0), viewport, scissor, fbo);
});
swapBuffers();
@ -348,7 +345,7 @@ glm::mat4 HmdDisplayPlugin::getViewCorrection() {
}
}
DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
void HmdDisplayPlugin::HUDRenderer::build() {
vertices = std::make_shared<gpu::Buffer>();
indices = std::make_shared<gpu::Buffer>();
@ -383,7 +380,7 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
indexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE;
// Compute indices order
std::vector<GLushort> indexData;
std::vector<GLushort> indices;
for (int i = 0; i < stacks - 1; i++) {
for (int j = 0; j < slices - 1; j++) {
GLushort bottomLeftIndex = i * slices + j;
@ -391,21 +388,24 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
GLushort topLeftIndex = bottomLeftIndex + slices;
GLushort topRightIndex = topLeftIndex + 1;
// FIXME make a z-order curve for better vertex cache locality
indexData.push_back(topLeftIndex);
indexData.push_back(bottomLeftIndex);
indexData.push_back(topRightIndex);
indices.push_back(topLeftIndex);
indices.push_back(bottomLeftIndex);
indices.push_back(topRightIndex);
indexData.push_back(topRightIndex);
indexData.push_back(bottomLeftIndex);
indexData.push_back(bottomRightIndex);
indices.push_back(topRightIndex);
indices.push_back(bottomLeftIndex);
indices.push_back(bottomRightIndex);
}
}
indices->append(indexData);
this->indices->append(indices);
format = std::make_shared<gpu::Stream::Format>(); // 1 for everyone
format->setAttribute(gpu::Stream::POSITION, gpu::Stream::POSITION, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), 0);
format->setAttribute(gpu::Stream::TEXCOORD, gpu::Stream::TEXCOORD, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV));
uniformsBuffer = std::make_shared<gpu::Buffer>(sizeof(Uniforms), nullptr);
updatePipeline();
}
void HmdDisplayPlugin::HUDRenderer::updatePipeline() {
if (!pipeline) {
auto program = gpu::Shader::createProgram(shader::render_utils::program::hmd_ui);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
@ -416,6 +416,10 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
pipeline = gpu::Pipeline::create(program, state);
}
}
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> HmdDisplayPlugin::HUDRenderer::render(HmdDisplayPlugin& plugin) {
updatePipeline();
auto hudPipeline = pipeline;
auto hudFormat = format;
@ -424,9 +428,9 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
auto hudUniformBuffer = uniformsBuffer;
auto hudUniforms = uniforms;
auto hudIndexCount = indexCount;
return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, const gpu::FramebufferPointer&, const bool mirror) {
if (pipeline && hudTexture) {
batch.setPipeline(pipeline);
return [=](gpu::Batch& batch, const gpu::TexturePointer& hudTexture, bool mirror) {
if (hudPipeline && hudTexture) {
batch.setPipeline(hudPipeline);
batch.setInputFormat(hudFormat);
gpu::BufferView posView(hudVertices, VERTEX_OFFSET, hudVertices->getSize(), VERTEX_STRIDE, hudFormat->getAttributes().at(gpu::Stream::POSITION)._element);
@ -450,7 +454,7 @@ DisplayPlugin::HUDOperator HmdDisplayPlugin::HUDRenderer::build() {
};
}
void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& compositeFramebuffer) {
void HmdDisplayPlugin::compositePointer() {
auto& cursorManager = Cursor::Manager::instance();
const auto& cursorData = _cursorsData[cursorManager.getCursor()->getIcon()];
auto compositorHelper = DependencyManager::get<CompositorHelper>();
@ -459,7 +463,7 @@ void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& composite
render([&](gpu::Batch& batch) {
// FIXME use standard gpu stereo rendering for this.
batch.enableStereo(false);
batch.setFramebuffer(compositeFramebuffer);
batch.setFramebuffer(_compositeFramebuffer);
batch.setPipeline(_cursorPipeline);
batch.setResourceTexture(0, cursorData.texture);
batch.resetViewTransform();
@ -474,6 +478,10 @@ void HmdDisplayPlugin::compositePointer(const gpu::FramebufferPointer& composite
});
}
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> HmdDisplayPlugin::getHUDOperator() {
return _hudRenderer.render(*this);
}
HmdDisplayPlugin::~HmdDisplayPlugin() {
}

View file

@ -48,20 +48,23 @@ public:
void pluginUpdate() override {};
virtual StencilMode getStencilMaskMode() const override { return StencilMode::PAINT; }
signals:
void hmdMountedChanged();
void hmdVisibleChanged(bool visible);
protected:
virtual void hmdPresent(const gpu::FramebufferPointer&) = 0;
virtual void hmdPresent() = 0;
virtual bool isHmdMounted() const = 0;
virtual void postPreview() {};
virtual void updatePresentPose();
bool internalActivate() override;
void internalDeactivate() override;
void compositePointer(const gpu::FramebufferPointer&) override;
void internalPresent(const gpu::FramebufferPointer&) override;
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> getHUDOperator() override;
void compositePointer() override;
void internalPresent() override;
void customizeContext() override;
void uncustomizeContext() override;
void updateFrameData() override;
@ -119,6 +122,8 @@ private:
static const size_t TEXTURE_OFFSET { offsetof(Vertex, uv) };
static const int VERTEX_STRIDE { sizeof(Vertex) };
HUDOperator build();
void build();
void updatePipeline();
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> render(HmdDisplayPlugin& plugin);
} _hudRenderer;
};

View file

@ -37,13 +37,13 @@ glm::uvec2 InterleavedStereoDisplayPlugin::getRecommendedRenderSize() const {
return result;
}
void InterleavedStereoDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
void InterleavedStereoDisplayPlugin::internalPresent() {
render([&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.resetViewTransform();
batch.setFramebuffer(gpu::FramebufferPointer());
batch.setViewportTransform(ivec4(uvec2(0), getSurfacePixels()));
batch.setResourceTexture(0, compositeFramebuffer->getRenderBuffer(0));
batch.setResourceTexture(0, _currentFrame->framebuffer->getRenderBuffer(0));
batch.setPipeline(_interleavedPresentPipeline);
batch.draw(gpu::TRIANGLE_STRIP, 4);
});

View file

@ -21,7 +21,7 @@ protected:
// initialize OpenGL context settings needed by the plugin
void customizeContext() override;
void uncustomizeContext() override;
void internalPresent(const gpu::FramebufferPointer&) override;
void internalPresent() override;
private:
static const QString NAME;

View file

@ -307,10 +307,6 @@ void RenderableModelEntityItem::setShapeType(ShapeType type) {
}
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
// because the caching system only allows one Geometry per url, and because this url might also be used
// as a visual model, we need to change this url in some way. We add a "collision-hull" query-arg so it
// will end up in a different hash-key in ResourceCache. TODO: It would be better to use the same URL and
// parse it twice.
auto currentCompoundShapeURL = getCompoundShapeURL();
ModelEntityItem::setCompoundShapeURL(url);
if (getCompoundShapeURL() != currentCompoundShapeURL || !getModel()) {

View file

@ -1,4 +1,4 @@
//
//
// RenderableParticleEffectEntityItem.cpp
// interface/src
//
@ -9,12 +9,12 @@
//
#include "RenderableParticleEffectEntityItem.h"
#include <StencilMaskPass.h>
#include <GeometryCache.h>
#include <shaders/Shaders.h>
#include <glm/gtx/transform.hpp>
using namespace render;
using namespace render::entities;
@ -79,6 +79,14 @@ bool ParticleEffectEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedE
return true;
}
if (_shapeType != entity->getShapeType()) {
return true;
}
if (_compoundShapeURL != entity->getCompoundShapeURL()) {
return true;
}
return false;
}
@ -87,10 +95,10 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
if (!newParticleProperties.valid()) {
qCWarning(entitiesrenderer) << "Bad particle properties";
}
if (resultWithReadLock<bool>([&]{ return _particleProperties != newParticleProperties; })) {
if (resultWithReadLock<bool>([&] { return _particleProperties != newParticleProperties; })) {
_timeUntilNextEmit = 0;
withWriteLock([&]{
withWriteLock([&] {
_particleProperties = newParticleProperties;
if (!_prevEmitterShouldTrailInitialized) {
_prevEmitterShouldTrailInitialized = true;
@ -101,13 +109,20 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
withWriteLock([&] {
_pulseProperties = entity->getPulseProperties();
_shapeType = entity->getShapeType();
QString compoundShapeURL = entity->getCompoundShapeURL();
if (_compoundShapeURL != compoundShapeURL) {
_compoundShapeURL = compoundShapeURL;
_hasComputedTriangles = false;
fetchGeometryResource();
}
});
_emitting = entity->getIsEmitting();
bool textureEmpty = resultWithReadLock<bool>([&]{ return _particleProperties.textures.isEmpty(); });
bool textureEmpty = resultWithReadLock<bool>([&] { return _particleProperties.textures.isEmpty(); });
if (textureEmpty) {
if (_networkTexture) {
withWriteLock([&] {
withWriteLock([&] {
_networkTexture.reset();
});
}
@ -116,11 +131,11 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
entity->setVisuallyReady(true);
});
} else {
bool textureNeedsUpdate = resultWithReadLock<bool>([&]{
bool textureNeedsUpdate = resultWithReadLock<bool>([&] {
return !_networkTexture || _networkTexture->getURL() != QUrl(_particleProperties.textures);
});
if (textureNeedsUpdate) {
withWriteLock([&] {
withWriteLock([&] {
_networkTexture = DependencyManager::get<TextureCache>()->getTexture(_particleProperties.textures);
});
}
@ -144,7 +159,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
// Fill in Uniforms structure
ParticleUniforms particleUniforms;
withReadLock([&]{
withReadLock([&] {
particleUniforms.radius.start = _particleProperties.radius.range.start;
particleUniforms.radius.middle = _particleProperties.radius.gradient.target;
particleUniforms.radius.finish = _particleProperties.radius.range.finish;
@ -181,9 +196,32 @@ Item::Bound ParticleEffectEntityRenderer::getBound() {
return _bound;
}
static const size_t VERTEX_PER_PARTICLE = 4;
// FIXME: these methods assume uniform emitDimensions, need to importance sample based on dimensions
float importanceSample2DDimension(float startDim) {
float dimension = 1.0f;
if (startDim < 1.0f) {
float innerDimensionSquared = startDim * startDim;
float outerDimensionSquared = 1.0f; // pow(particle::MAXIMUM_EMIT_RADIUS_START, 2);
float randDimensionSquared = randFloatInRange(innerDimensionSquared, outerDimensionSquared);
dimension = std::sqrt(randDimensionSquared);
}
return dimension;
}
ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties) {
float importanceSample3DDimension(float startDim) {
float dimension = 1.0f;
if (startDim < 1.0f) {
float innerDimensionCubed = startDim * startDim * startDim;
float outerDimensionCubed = 1.0f; // pow(particle::MAXIMUM_EMIT_RADIUS_START, 3);
float randDimensionCubed = randFloatInRange(innerDimensionCubed, outerDimensionCubed);
dimension = std::cbrt(randDimensionCubed);
}
return dimension;
}
ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties,
const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource,
const TriangleInfo& triangleInfo) {
CpuParticle particle;
const auto& accelerationSpread = particleProperties.emission.acceleration.spread;
@ -221,33 +259,130 @@ ParticleEffectEntityRenderer::CpuParticle ParticleEffectEntityRenderer::createPa
float azimuth;
if (azimuthFinish >= azimuthStart) {
azimuth = azimuthStart + (azimuthFinish - azimuthStart) * randFloat();
azimuth = azimuthStart + (azimuthFinish - azimuthStart) * randFloat();
} else {
azimuth = azimuthStart + (TWO_PI + azimuthFinish - azimuthStart) * randFloat();
}
// TODO: azimuth and elevation are only used for ellipsoids/circles, but could be used for other shapes too
if (emitDimensions == Vectors::ZERO) {
// Point
emitDirection = glm::quat(glm::vec3(PI_OVER_TWO - elevation, 0.0f, azimuth)) * Vectors::UNIT_Z;
} else {
// Ellipsoid
float radiusScale = 1.0f;
if (emitRadiusStart < 1.0f) {
float randRadius =
emitRadiusStart + randFloatInRange(0.0f, particle::MAXIMUM_EMIT_RADIUS_START - emitRadiusStart);
radiusScale = 1.0f - std::pow(1.0f - randRadius, 3.0f);
glm::vec3 emitPosition;
switch (shapeType) {
case SHAPE_TYPE_BOX: {
glm::vec3 dim = importanceSample3DDimension(emitRadiusStart) * 0.5f * emitDimensions;
int side = randIntInRange(0, 5);
int axis = side % 3;
float direction = side > 2 ? 1.0f : -1.0f;
emitDirection[axis] = direction;
emitPosition[axis] = direction * dim[axis];
axis = (axis + 1) % 3;
emitPosition[axis] = dim[axis] * randFloatInRange(-1.0f, 1.0f);
axis = (axis + 1) % 3;
emitPosition[axis] = dim[axis] * randFloatInRange(-1.0f, 1.0f);
break;
}
case SHAPE_TYPE_CYLINDER_X:
case SHAPE_TYPE_CYLINDER_Y:
case SHAPE_TYPE_CYLINDER_Z: {
glm::vec3 radii = importanceSample2DDimension(emitRadiusStart) * 0.5f * emitDimensions;
int axis = shapeType - SHAPE_TYPE_CYLINDER_X;
emitPosition[axis] = emitDimensions[axis] * randFloatInRange(-0.5f, 0.5f);
emitDirection[axis] = 0.0f;
axis = (axis + 1) % 3;
emitPosition[axis] = radii[axis] * glm::cos(azimuth);
emitDirection[axis] = radii[axis] > 0.0f ? emitPosition[axis] / (radii[axis] * radii[axis]) : 0.0f;
axis = (axis + 1) % 3;
emitPosition[axis] = radii[axis] * glm::sin(azimuth);
emitDirection[axis] = radii[axis] > 0.0f ? emitPosition[axis] / (radii[axis] * radii[axis]) : 0.0f;
emitDirection = glm::normalize(emitDirection);
break;
}
case SHAPE_TYPE_CIRCLE: {
glm::vec2 radii = importanceSample2DDimension(emitRadiusStart) * 0.5f * glm::vec2(emitDimensions.x, emitDimensions.z);
float x = radii.x * glm::cos(azimuth);
float z = radii.y * glm::sin(azimuth);
emitPosition = glm::vec3(x, 0.0f, z);
emitDirection = Vectors::UP;
break;
}
case SHAPE_TYPE_PLANE: {
glm::vec2 dim = importanceSample2DDimension(emitRadiusStart) * 0.5f * glm::vec2(emitDimensions.x, emitDimensions.z);
int side = randIntInRange(0, 3);
int axis = side % 2;
float direction = side > 1 ? 1.0f : -1.0f;
glm::vec2 pos;
pos[axis] = direction * dim[axis];
axis = (axis + 1) % 2;
pos[axis] = dim[axis] * randFloatInRange(-1.0f, 1.0f);
emitPosition = glm::vec3(pos.x, 0.0f, pos.y);
emitDirection = Vectors::UP;
break;
}
case SHAPE_TYPE_COMPOUND: {
// if we get here we know that geometryResource is loaded
size_t index = randFloat() * triangleInfo.totalSamples;
Triangle triangle;
for (size_t i = 0; i < triangleInfo.samplesPerTriangle.size(); i++) {
size_t numSamples = triangleInfo.samplesPerTriangle[i];
if (index < numSamples) {
triangle = triangleInfo.triangles[i];
break;
}
index -= numSamples;
}
float edgeLength1 = glm::length(triangle.v1 - triangle.v0);
float edgeLength2 = glm::length(triangle.v2 - triangle.v1);
float edgeLength3 = glm::length(triangle.v0 - triangle.v2);
float perimeter = edgeLength1 + edgeLength2 + edgeLength3;
float fraction1 = randFloatInRange(0.0f, 1.0f);
float fractionEdge1 = glm::min(fraction1 * perimeter / edgeLength1, 1.0f);
float fraction2 = fraction1 - edgeLength1 / perimeter;
float fractionEdge2 = glm::clamp(fraction2 * perimeter / edgeLength2, 0.0f, 1.0f);
float fraction3 = fraction2 - edgeLength2 / perimeter;
float fractionEdge3 = glm::clamp(fraction3 * perimeter / edgeLength3, 0.0f, 1.0f);
float dim = importanceSample2DDimension(emitRadiusStart);
triangle = triangle * (glm::scale(emitDimensions) * triangleInfo.transform);
glm::vec3 center = (triangle.v0 + triangle.v1 + triangle.v2) / 3.0f;
glm::vec3 v0 = (dim * (triangle.v0 - center)) + center;
glm::vec3 v1 = (dim * (triangle.v1 - center)) + center;
glm::vec3 v2 = (dim * (triangle.v2 - center)) + center;
emitPosition = glm::mix(v0, glm::mix(v1, glm::mix(v2, v0, fractionEdge3), fractionEdge2), fractionEdge1);
emitDirection = triangle.getNormal();
break;
}
case SHAPE_TYPE_SPHERE:
case SHAPE_TYPE_ELLIPSOID:
default: {
glm::vec3 radii = importanceSample3DDimension(emitRadiusStart) * 0.5f * emitDimensions;
float x = radii.x * glm::cos(elevation) * glm::cos(azimuth);
float y = radii.y * glm::cos(elevation) * glm::sin(azimuth);
float z = radii.z * glm::sin(elevation);
emitPosition = glm::vec3(x, y, z);
emitDirection = glm::normalize(glm::vec3(radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f,
radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f,
radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f));
break;
}
}
glm::vec3 radii = radiusScale * 0.5f * emitDimensions;
float x = radii.x * glm::cos(elevation) * glm::cos(azimuth);
float y = radii.y * glm::cos(elevation) * glm::sin(azimuth);
float z = radii.z * glm::sin(elevation);
glm::vec3 emitPosition = glm::vec3(x, y, z);
emitDirection = glm::normalize(glm::vec3(
radii.x > 0.0f ? x / (radii.x * radii.x) : 0.0f,
radii.y > 0.0f ? y / (radii.y * radii.y) : 0.0f,
radii.z > 0.0f ? z / (radii.z * radii.z) : 0.0f
));
particle.relativePosition += emitOrientation * emitPosition;
}
}
@ -267,20 +402,28 @@ void ParticleEffectEntityRenderer::stepSimulation() {
const auto now = usecTimestampNow();
const auto interval = std::min<uint64_t>(USECS_PER_SECOND / 60, now - _lastSimulated);
_lastSimulated = now;
particle::Properties particleProperties;
withReadLock([&]{
ShapeType shapeType;
GeometryResource::Pointer geometryResource;
withReadLock([&] {
particleProperties = _particleProperties;
shapeType = _shapeType;
geometryResource = _geometryResource;
});
const auto& modelTransform = getModelTransform();
if (_emitting && particleProperties.emitting()) {
if (_emitting && particleProperties.emitting() &&
(shapeType != SHAPE_TYPE_COMPOUND || (geometryResource && geometryResource->isLoaded()))) {
uint64_t emitInterval = particleProperties.emitIntervalUsecs();
if (emitInterval > 0 && interval >= _timeUntilNextEmit) {
auto timeRemaining = interval;
while (timeRemaining > _timeUntilNextEmit) {
if (_shapeType == SHAPE_TYPE_COMPOUND && !_hasComputedTriangles) {
computeTriangles(geometryResource->getHFMModel());
}
// emit particle
_cpuParticles.push_back(createParticle(now, modelTransform, particleProperties));
_cpuParticles.push_back(createParticle(now, modelTransform, particleProperties, shapeType, geometryResource, _triangleInfo));
_timeUntilNextEmit = emitInterval;
if (emitInterval < timeRemaining) {
timeRemaining -= emitInterval;
@ -297,7 +440,7 @@ void ParticleEffectEntityRenderer::stepSimulation() {
}
const float deltaTime = (float)interval / (float)USECS_PER_SECOND;
// update the particles
// update the particles
for (auto& particle : _cpuParticles) {
if (_prevEmitterShouldTrail != particleProperties.emission.shouldTrail) {
if (_prevEmitterShouldTrail) {
@ -313,7 +456,7 @@ void ParticleEffectEntityRenderer::stepSimulation() {
static GpuParticles gpuParticles;
gpuParticles.clear();
gpuParticles.reserve(_cpuParticles.size()); // Reserve space
std::transform(_cpuParticles.begin(), _cpuParticles.end(), std::back_inserter(gpuParticles), [&particleProperties, &modelTransform](const CpuParticle& particle) {
std::transform(_cpuParticles.begin(), _cpuParticles.end(), std::back_inserter(gpuParticles), [&particleProperties, &modelTransform] (const CpuParticle& particle) {
glm::vec3 position = particle.relativePosition + (particleProperties.emission.shouldTrail ? particle.basePosition : modelTransform.getTranslation());
return GpuParticle(position, glm::vec2(particle.lifetime, particle.seed));
});
@ -356,5 +499,131 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
batch.setInputBuffer(0, _particleBuffer, 0, sizeof(GpuParticle));
auto numParticles = _particleBuffer->getSize() / sizeof(GpuParticle);
static const size_t VERTEX_PER_PARTICLE = 4;
batch.drawInstanced((gpu::uint32)numParticles, gpu::TRIANGLE_STRIP, (gpu::uint32)VERTEX_PER_PARTICLE);
}
void ParticleEffectEntityRenderer::fetchGeometryResource() {
QUrl hullURL(_compoundShapeURL);
if (hullURL.isEmpty()) {
_geometryResource.reset();
} else {
_geometryResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
}
}
// FIXME: this is very similar to Model::calculateTriangleSets
void ParticleEffectEntityRenderer::computeTriangles(const hfm::Model& hfmModel) {
PROFILE_RANGE(render, __FUNCTION__);
int numberOfMeshes = hfmModel.meshes.size();
_hasComputedTriangles = true;
_triangleInfo.triangles.clear();
_triangleInfo.samplesPerTriangle.clear();
std::vector<float> areas;
float minArea = FLT_MAX;
AABox bounds;
for (int i = 0; i < numberOfMeshes; i++) {
const HFMMesh& mesh = hfmModel.meshes.at(i);
const int numberOfParts = mesh.parts.size();
for (int j = 0; j < numberOfParts; j++) {
const HFMMeshPart& part = mesh.parts.at(j);
const int INDICES_PER_TRIANGLE = 3;
const int INDICES_PER_QUAD = 4;
const int TRIANGLES_PER_QUAD = 2;
// tell our triangleSet how many triangles to expect.
int numberOfQuads = part.quadIndices.size() / INDICES_PER_QUAD;
int numberOfTris = part.triangleIndices.size() / INDICES_PER_TRIANGLE;
int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris;
_triangleInfo.triangles.reserve(_triangleInfo.triangles.size() + totalTriangles);
areas.reserve(areas.size() + totalTriangles);
auto meshTransform = hfmModel.offset * mesh.modelTransform;
if (part.quadIndices.size() > 0) {
int vIndex = 0;
for (int q = 0; q < numberOfQuads; q++) {
int i0 = part.quadIndices[vIndex++];
int i1 = part.quadIndices[vIndex++];
int i2 = part.quadIndices[vIndex++];
int i3 = part.quadIndices[vIndex++];
// track the model space version... these points will be transformed by the FST's offset,
// which includes the scaling, rotation, and translation specified by the FST/FBX,
// this can't change at runtime, so we can safely store these in our TriangleSet
glm::vec3 v0 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f));
glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f));
glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f));
glm::vec3 v3 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i3], 1.0f));
Triangle tri1 = { v0, v1, v3 };
Triangle tri2 = { v1, v2, v3 };
_triangleInfo.triangles.push_back(tri1);
_triangleInfo.triangles.push_back(tri2);
float area1 = tri1.getArea();
areas.push_back(area1);
if (area1 > EPSILON) {
minArea = std::min(minArea, area1);
}
float area2 = tri2.getArea();
areas.push_back(area2);
if (area2 > EPSILON) {
minArea = std::min(minArea, area2);
}
bounds += v0;
bounds += v1;
bounds += v2;
bounds += v3;
}
}
if (part.triangleIndices.size() > 0) {
int vIndex = 0;
for (int t = 0; t < numberOfTris; t++) {
int i0 = part.triangleIndices[vIndex++];
int i1 = part.triangleIndices[vIndex++];
int i2 = part.triangleIndices[vIndex++];
// track the model space version... these points will be transformed by the FST's offset,
// which includes the scaling, rotation, and translation specified by the FST/FBX,
// this can't change at runtime, so we can safely store these in our TriangleSet
glm::vec3 v0 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i0], 1.0f));
glm::vec3 v1 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i1], 1.0f));
glm::vec3 v2 = glm::vec3(meshTransform * glm::vec4(mesh.vertices[i2], 1.0f));
Triangle tri = { v0, v1, v2 };
_triangleInfo.triangles.push_back(tri);
float area = tri.getArea();
areas.push_back(area);
if (area > EPSILON) {
minArea = std::min(minArea, area);
}
bounds += v0;
bounds += v1;
bounds += v2;
}
}
}
}
_triangleInfo.totalSamples = 0;
for (auto& area : areas) {
size_t numSamples = area / minArea;
_triangleInfo.samplesPerTriangle.push_back(numSamples);
_triangleInfo.totalSamples += numSamples;
}
glm::vec3 scale = bounds.getScale();
_triangleInfo.transform = glm::scale(1.0f / scale) * glm::translate(-bounds.calcCenter());
}

View file

@ -81,7 +81,18 @@ private:
glm::vec2 spare;
};
static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties);
void computeTriangles(const hfm::Model& hfmModel);
bool _hasComputedTriangles{ false };
struct TriangleInfo {
std::vector<Triangle> triangles;
std::vector<size_t> samplesPerTriangle;
size_t totalSamples;
glm::mat4 transform;
} _triangleInfo;
static CpuParticle createParticle(uint64_t now, const Transform& baseTransform, const particle::Properties& particleProperties,
const ShapeType& shapeType, const GeometryResource::Pointer& geometryResource,
const TriangleInfo& triangleInfo);
void stepSimulation();
particle::Properties _particleProperties;
@ -90,11 +101,16 @@ private:
CpuParticles _cpuParticles;
bool _emitting { false };
uint64_t _timeUntilNextEmit { 0 };
BufferPointer _particleBuffer{ std::make_shared<Buffer>() };
BufferPointer _particleBuffer { std::make_shared<Buffer>() };
BufferView _uniformBuffer;
quint64 _lastSimulated { 0 };
PulsePropertyGroup _pulseProperties;
ShapeType _shapeType;
QString _compoundShapeURL;
void fetchGeometryResource();
GeometryResource::Pointer _geometryResource;
NetworkTexturePointer _networkTexture;
ScenePointer _scene;

View file

@ -1698,10 +1698,7 @@ AACube EntityItem::getQueryAACube(bool& success) const {
}
bool EntityItem::shouldPuffQueryAACube() const {
bool hasGrabs = _grabsLock.resultWithReadLock<bool>([&] {
return _grabs.count() > 0;
});
return hasActions() || isChildOfMyAvatar() || isMovingRelativeToParent() || hasGrabs;
return hasActions() || isChildOfMyAvatar() || isMovingRelativeToParent();
}
// TODO: get rid of all users of this function...
@ -1896,7 +1893,8 @@ void EntityItem::setScaledDimensions(const glm::vec3& value) {
void EntityItem::setUnscaledDimensions(const glm::vec3& value) {
glm::vec3 newDimensions = glm::max(value, glm::vec3(ENTITY_ITEM_MIN_DIMENSION));
if (getUnscaledDimensions() != newDimensions) {
const float MIN_SCALE_CHANGE_SQUARED = 1.0e-6f;
if (glm::length2(getUnscaledDimensions() - newDimensions) > MIN_SCALE_CHANGE_SQUARED) {
withWriteLock([&] {
_unscaledDimensions = newDimensions;
});
@ -2086,7 +2084,7 @@ void EntityItem::computeCollisionGroupAndFinalMask(int32_t& group, int32_t& mask
} else {
if (getDynamic()) {
group = BULLET_COLLISION_GROUP_DYNAMIC;
} else if (isMovingRelativeToParent() || hasActions()) {
} else if (hasActions() || isMovingRelativeToParent()) {
group = BULLET_COLLISION_GROUP_KINEMATIC;
} else {
group = BULLET_COLLISION_GROUP_STATIC;
@ -3057,30 +3055,18 @@ bool EntityItem::getCollisionless() const {
}
uint16_t EntityItem::getCollisionMask() const {
uint16_t result;
withReadLock([&] {
result = _collisionMask;
});
return result;
return _collisionMask;
}
bool EntityItem::getDynamic() const {
if (SHAPE_TYPE_STATIC_MESH == getShapeType()) {
return false;
}
bool result;
withReadLock([&] {
result = _dynamic;
});
return result;
return _dynamic;
}
bool EntityItem::getLocked() const {
bool result;
withReadLock([&] {
result = _locked;
});
return result;
return _locked;
}
void EntityItem::setLocked(bool value) {
@ -3152,7 +3138,6 @@ uint32_t EntityItem::getDirtyFlags() const {
return result;
}
void EntityItem::markDirtyFlags(uint32_t mask) {
withWriteLock([&] {
mask &= Simulation::DIRTY_FLAGS;

View file

@ -448,7 +448,7 @@ public:
bool clearActions(EntitySimulationPointer simulation);
void setDynamicData(QByteArray dynamicData);
const QByteArray getDynamicData() const;
bool hasActions() const { return !_objectActions.empty(); }
bool hasActions() const { return !_objectActions.empty() || !_grabActions.empty(); }
QList<QUuid> getActionIDs() const { return _objectActions.keys(); }
QVariantMap getActionArguments(const QUuid& actionID) const;
void deserializeActions();

View file

@ -127,6 +127,7 @@ void buildStringToShapeTypeLookup() {
addShapeType(SHAPE_TYPE_SIMPLE_COMPOUND);
addShapeType(SHAPE_TYPE_STATIC_MESH);
addShapeType(SHAPE_TYPE_ELLIPSOID);
addShapeType(SHAPE_TYPE_CIRCLE);
}
QHash<QString, MaterialMappingMode> stringToMaterialMappingModeLookup;
@ -1114,23 +1115,28 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* default, particles emit along the entity's local z-axis, and <code>azimuthStart</code> and <code>azimuthFinish</code>
* are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e.,
* the particles emit vertically.
* @property {Vec3} emitDimensions=0,0,0 - The dimensions of the ellipsoid from which particles are emitted.
* @property {number} emitRadiusStart=1 - The starting radius within the ellipsoid at which particles start being emitted;
* range <code>0.0</code> &ndash; <code>1.0</code> for the ellipsoid center to the ellipsoid surface, respectively.
* Particles are emitted from the portion of the ellipsoid that lies between <code>emitRadiusStart</code> and the
* ellipsoid's surface.
* @property {Vec3} emitDimensions=0,0,0 - The dimensions of the shape from which particles are emitted. The shape is specified with
* <code>shapeType</code>.
* @property {number} emitRadiusStart=1 - The starting radius within the shape at which particles start being emitted;
* range <code>0.0</code> &ndash; <code>1.0</code> for the center to the surface, respectively.
* Particles are emitted from the portion of the shape that lies between <code>emitRadiusStart</code> and the
* shape's surface.
* @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted
* within the ellipsoid; range <code>0</code> &ndash; <code>Math.PI</code>. Particles are emitted from the portion of the
* ellipsoid that lies between <code>polarStart<code> and <code>polarFinish</code>.
* within the shape; range <code>0</code> &ndash; <code>Math.PI</code>. Particles are emitted from the portion of the
* shape that lies between <code>polarStart<code> and <code>polarFinish</code>. Only used if <code>shapeType</code> is
* <code>ellipsoid</code> or <code>sphere</code>.
* @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted
* within the ellipsoid; range <code>0</code> &ndash; <code>Math.PI</code>. Particles are emitted from the portion of the
* ellipsoid that lies between <code>polarStart<code> and <code>polarFinish</code>.
* within the shape; range <code>0</code> &ndash; <code>Math.PI</code>. Particles are emitted from the portion of the
* shape that lies between <code>polarStart<code> and <code>polarFinish</code>. Only used if <code>shapeType</code> is
* <code>ellipsoid</code> or <code>sphere</code>.
* @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local
* z-axis at which particles start being emitted; range <code>-Math.PI</code> &ndash; <code>Math.PI</code>. Particles are
* emitted from the portion of the ellipsoid that lies between <code>azimuthStart<code> and <code>azimuthFinish</code>.
* emitted from the portion of the shape that lies between <code>azimuthStart<code> and <code>azimuthFinish</code>.
* Only used if <code>shapeType</code> is <code>ellipsoid</code>, <code>sphere</code>, or <code>circle</code>.
* @property {number} azimuthFinish=Math.PI - The angle in radians from the entity's local x-axis about the entity's local
* z-axis at which particles stop being emitted; range <code>-Math.PI</code> &ndash; <code>Math.PI</code>. Particles are
* emitted from the portion of the ellipsoid that lies between <code>azimuthStart<code> and <code>azimuthFinish</code>.
* emitted from the portion of the shape that lies between <code>azimuthStart<code> and <code>azimuthFinish</code>.
* Only used if <code>shapeType</code> is <code>ellipsoid</code>, <code>sphere</code>, or <code>circle</code>..
*
* @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency,
* use PNG format.
@ -1170,7 +1176,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* up in the world. If true, they will point towards the entity's up vector, based on its orientation.
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
*
* @property {ShapeType} shapeType="none" - <em>Currently not used.</em> <em>Read-only.</em>
* @property {ShapeType} shapeType="ellipsoid" - The shape of the collision hull used if collisions are enabled.
* @property {string} compoundShapeURL="" - The model file to use for the compound shape if <code>shapeType</code> is
* <code>"compound"</code>.
*
* @example <caption>Create a ball of green smoke.</caption>
* particles = Entities.addEntity({
@ -1658,6 +1666,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
// Particles only
if (_type == EntityTypes::ParticleEffect) {
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString());
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL);
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
@ -3104,6 +3113,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
if (properties.getType() == EntityTypes::ParticleEffect) {
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType()));
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL());
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
_staticPulse.setProperties(properties);
@ -3584,6 +3594,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
if (properties.getType() == EntityTypes::ParticleEffect) {
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);

View file

@ -26,6 +26,7 @@
#include <Extents.h>
#include <PerfStat.h>
#include <Profile.h>
#include <AddressManager.h>
#include "EntitySimulation.h"
#include "VariantMapToScriptValue.h"
@ -286,27 +287,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
assert(entity);
if (getIsServer()) {
QString certID(entity->getCertificateID());
EntityItemID entityItemID = entity->getEntityItemID();
EntityItemID existingEntityItemID;
{
QWriteLocker locker(&_entityCertificateIDMapLock);
existingEntityItemID = _entityCertificateIDMap.value(certID);
if (!certID.isEmpty()) {
_entityCertificateIDMap.insert(certID, entityItemID);
qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID;
}
}
// Delete an already-existing entity from the tree if it has the same
// CertificateID as the entity we're trying to add.
if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) {
qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID"
<< existingEntityItemID << ". Deleting existing entity.";
deleteEntity(existingEntityItemID, true);
return;
}
addCertifiedEntityOnServer(entity);
}
// check to see if we need to simulate this entity..
@ -764,13 +745,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
theEntity->die();
if (getIsServer()) {
{
QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock);
QString certID = theEntity->getCertificateID();
if (theEntity->getEntityItemID() == _entityCertificateIDMap.value(certID)) {
_entityCertificateIDMap.remove(certID);
}
}
removeCertifiedEntityOnServer(theEntity);
// set up the deleted entities ID
QWriteLocker recentlyDeletedEntitiesLocker(&_recentlyDeletedEntitiesLock);
@ -1421,11 +1396,123 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) {
return false;
}
void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) {
QString certID(entity->getCertificateID());
EntityItemID existingEntityItemID;
if (!certID.isEmpty()) {
EntityItemID entityItemID = entity->getEntityItemID();
QWriteLocker locker(&_entityCertificateIDMapLock);
QList<EntityItemID>& entityList = _entityCertificateIDMap[certID]; // inserts it if needed.
if (!entityList.isEmpty() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) {
existingEntityItemID = entityList.first(); // we will only care about the first, if any, below.
entityList.removeOne(existingEntityItemID);
}
entityList << entityItemID; // adds to list within hash because entityList is a reference.
qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID << "total" << entityList.size() << "entities.";
}
// Delete an already-existing entity from the tree if it has the same
// CertificateID as the entity we're trying to add.
if (!existingEntityItemID.isNull()) {
qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID"
<< existingEntityItemID << ". Deleting existing entity.";
withWriteLock([&] {
deleteEntity(existingEntityItemID, true);
});
}
}
void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) {
QString certID = entity->getCertificateID();
if (!certID.isEmpty()) {
QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock);
QList<EntityItemID>& entityList = _entityCertificateIDMap[certID];
entityList.removeOne(entity->getEntityItemID());
if (entityList.isEmpty()) {
// hmmm, do we to make it be a hash instead of a list, so that this is faster if you stamp out 1000 of a domainUnlimited?
_entityCertificateIDMap.remove(certID);
}
}
}
void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove) {
QReadLocker locker(&_entityCertificateIDMapLock);
QHashIterator<QString, QList<EntityItemID>> i(_entityCertificateIDMap);
qCDebug(entities) << _entityCertificateIDMap.size() << "certificates present.";
while (i.hasNext()) {
i.next();
const auto& certificateID = i.key();
const auto& entityIDs = i.value();
if (entityIDs.isEmpty()) {
continue;
}
// Examine each cert:
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/location");
QJsonObject request;
request["certificate_id"] = certificateID;
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, this, [this, entityIDs, networkReply, minimumAgeToRemove, &certificateID] {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
bool failure = networkReply->error() != QNetworkReply::NoError;
auto failureReason = networkReply->error();
networkReply->deleteLater();
if (failure) {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << failureReason
<< "; NOT deleting cert" << certificateID << "More info:" << jsonObject;
return;
}
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
if (jsonObject["domain_id"].toString() == thisDomainID) {
// Entity belongs here. Nothing to do.
return;
}
// Entity does not belong here:
QList<EntityItemID> retained;
for (int i = 0; i < entityIDs.size(); i++) {
EntityItemID entityID = entityIDs.at(i);
EntityItemPointer entity = findEntityByEntityItemID(entityID);
if (!entity) {
qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID;
continue;
}
if (entity->getAge() <= minimumAgeToRemove) {
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID;
retained << entityID;
continue;
}
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID;
withWriteLock([&] {
deleteEntity(entityID, true);
});
}
{
QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock);
if (retained.isEmpty()) {
qCDebug(entities) << "Removed" << certificateID;
_entityCertificateIDMap.remove(certificateID);
} else {
qCDebug(entities) << "Retained" << retained.size() << "young entities for" << certificateID;
_entityCertificateIDMap[certificateID] = retained;
}
}
});
}
}
void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) {
QTimer* _challengeOwnershipTimeoutTimer = new QTimer(this);
connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const QString& certID) {
QReadLocker locker(&_entityCertificateIDMapLock);
EntityItemID id = _entityCertificateIDMap.value(certID);
connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const EntityItemID& id) {
if (entityItemID == id && _challengeOwnershipTimeoutTimer) {
_challengeOwnershipTimeoutTimer->stop();
_challengeOwnershipTimeoutTimer->deleteLater();
@ -1445,26 +1532,21 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID)
_challengeOwnershipTimeoutTimer->start(5000);
}
QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKey) {
QByteArray EntityTree::computeNonce(const EntityItemID& entityID, const QString ownerKey) {
QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-"
QByteArray nonceBytes = nonce.toByteArray();
QWriteLocker locker(&_certNonceMapLock);
_certNonceMap.insert(certID, QPair<QUuid, QString>(nonce, ownerKey));
QWriteLocker locker(&_entityNonceMapLock);
_entityNonceMap.insert(entityID, QPair<QUuid, QString>(nonce, ownerKey));
return nonceBytes;
}
bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id) {
{
QReadLocker certIdMapLocker(&_entityCertificateIDMapLock);
id = _entityCertificateIDMap.value(certID);
}
bool EntityTree::verifyNonce(const EntityItemID& entityID, const QString& nonce) {
QString actualNonce, key;
{
QWriteLocker locker(&_certNonceMapLock);
QPair<QUuid, QString> sent = _certNonceMap.take(certID);
QWriteLocker locker(&_entityNonceMapLock);
QPair<QUuid, QString> sent = _entityNonceMap.take(entityID);
actualNonce = sent.first.toString();
key = sent.second;
}
@ -1474,9 +1556,9 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity
bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), hashedActualNonce, QByteArray::fromBase64(nonce.toUtf8()));
if (verificationSuccess) {
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded.";
qCDebug(entities) << "Ownership challenge for Entity ID" << entityID << "succeeded.";
} else {
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed. Actual nonce:" << actualNonce <<
qCDebug(entities) << "Ownership challenge for Entity ID" << entityID << "failed. Actual nonce:" << actualNonce <<
"\nHashed actual nonce (digest):" << hashedActualNonce << "\nSent nonce (signature)" << nonce << "\nKey" << key;
}
@ -1484,42 +1566,42 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity
}
void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
int certIDByteArraySize;
int idByteArraySize;
int textByteArraySize;
int nodeToChallengeByteArraySize;
message.readPrimitive(&certIDByteArraySize);
message.readPrimitive(&idByteArraySize);
message.readPrimitive(&textByteArraySize);
message.readPrimitive(&nodeToChallengeByteArraySize);
QByteArray certID(message.read(certIDByteArraySize));
QByteArray id(message.read(idByteArraySize));
QByteArray text(message.read(textByteArraySize));
QByteArray nodeToChallenge(message.read(nodeToChallengeByteArraySize));
sendChallengeOwnershipRequestPacket(certID, text, nodeToChallenge, sourceNode);
sendChallengeOwnershipRequestPacket(id, text, nodeToChallenge, sourceNode);
}
void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
auto nodeList = DependencyManager::get<NodeList>();
int certIDByteArraySize;
int idByteArraySize;
int textByteArraySize;
int challengingNodeUUIDByteArraySize;
message.readPrimitive(&certIDByteArraySize);
message.readPrimitive(&idByteArraySize);
message.readPrimitive(&textByteArraySize);
message.readPrimitive(&challengingNodeUUIDByteArraySize);
QByteArray certID(message.read(certIDByteArraySize));
QByteArray id(message.read(idByteArraySize));
QByteArray text(message.read(textByteArraySize));
QUuid challengingNode = QUuid::fromRfc4122(message.read(challengingNodeUUIDByteArraySize));
auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
certIDByteArraySize + text.length() + 2 * sizeof(int),
idByteArraySize + text.length() + 2 * sizeof(int),
true);
challengeOwnershipReplyPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipReplyPacket->writePrimitive(idByteArraySize);
challengeOwnershipReplyPacket->writePrimitive(text.length());
challengeOwnershipReplyPacket->write(certID);
challengeOwnershipReplyPacket->write(id);
challengeOwnershipReplyPacket->write(text);
nodeList->sendPacket(std::move(challengeOwnershipReplyPacket), *(nodeList->nodeWithUUID(challengingNode)));
@ -1529,7 +1611,7 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
// 1. Obtain a nonce
auto nodeList = DependencyManager::get<NodeList>();
QByteArray text = computeNonce(certID, ownerKey);
QByteArray text = computeNonce(entityItemID, ownerKey);
if (text == "") {
qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. Deleting entity...";
@ -1539,14 +1621,14 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
} else {
qCDebug(entities) << "Challenging ownership of Cert ID" << certID;
// 2. Send the nonce to the rezzing avatar's node
QByteArray certIDByteArray = certID.toUtf8();
int certIDByteArraySize = certIDByteArray.size();
QByteArray idByteArray = entityItemID.toByteArray();
int idByteArraySize = idByteArray.size();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership,
certIDByteArraySize + text.length() + 2 * sizeof(int),
idByteArraySize + text.length() + 2 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(idByteArraySize);
challengeOwnershipPacket->writePrimitive(text.length());
challengeOwnershipPacket->write(certIDByteArray);
challengeOwnershipPacket->write(idByteArray);
challengeOwnershipPacket->write(text);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode);
@ -1560,24 +1642,24 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
}
}
void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) {
void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& id, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) {
auto nodeList = DependencyManager::get<NodeList>();
// In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants
// to make sure belongs to Avatar B.
QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122();
int certIDByteArraySize = certID.length();
int idByteArraySize = id.length();
int TextByteArraySize = text.length();
int senderNodeUUIDSize = senderNodeUUID.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int),
idByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(idByteArraySize);
challengeOwnershipPacket->writePrimitive(TextByteArraySize);
challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(id);
challengeOwnershipPacket->write(text);
challengeOwnershipPacket->write(senderNodeUUID);
@ -1636,22 +1718,21 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt
}
void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
int certIDByteArraySize;
int idByteArraySize;
int textByteArraySize;
message.readPrimitive(&certIDByteArraySize);
message.readPrimitive(&idByteArraySize);
message.readPrimitive(&textByteArraySize);
QString certID(message.read(certIDByteArraySize));
EntityItemID id(message.read(idByteArraySize));
QString text(message.read(textByteArraySize));
emit killChallengeOwnershipTimeoutTimer(certID);
emit killChallengeOwnershipTimeoutTimer(id);
EntityItemID id;
if (!verifyNonce(certID, text, id)) {
if (!id.isNull()) {
if (!verifyNonce(id, text)) {
withWriteLock([&] {
deleteEntity(id, true);
}
});
}
}

View file

@ -157,11 +157,6 @@ public:
return _recentlyDeletedEntityItemIDs;
}
QHash<QString, EntityItemID> getEntityCertificateIDMap() const {
QReadLocker locker(&_entityCertificateIDMapLock);
return _entityCertificateIDMap;
}
void forgetEntitiesDeletedBefore(quint64 sinceTime);
int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode);
@ -252,8 +247,8 @@ public:
static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME;
QByteArray computeNonce(const QString& certID, const QString ownerKey);
bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id);
QByteArray computeNonce(const EntityItemID& entityID, const QString ownerKey);
bool verifyNonce(const EntityItemID& entityID, const QString& nonce);
QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); }
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
@ -279,6 +274,7 @@ public:
void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
bool force, bool tellServer);
void startDynamicDomainVerificationOnServer(float minimumAgeToRemove);
signals:
void deletingEntity(const EntityItemID& entityID);
@ -290,7 +286,7 @@ signals:
void entityServerScriptChanging(const EntityItemID& entityItemID, const bool reload);
void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID);
void clearingEntities();
void killChallengeOwnershipTimeoutTimer(const QString& certID);
void killChallengeOwnershipTimeoutTimer(const EntityItemID& certID);
protected:
@ -327,10 +323,10 @@ protected:
QHash<EntityItemID, EntityItemPointer> _entityMap;
mutable QReadWriteLock _entityCertificateIDMapLock;
QHash<QString, EntityItemID> _entityCertificateIDMap;
QHash<QString, QList<EntityItemID>> _entityCertificateIDMap;
mutable QReadWriteLock _certNonceMapLock;
QHash<QString, QPair<QUuid, QString>> _certNonceMap;
mutable QReadWriteLock _entityNonceMapLock;
QHash<EntityItemID, QPair<QUuid, QString>> _entityNonceMap;
EntitySimulationPointer _simulation;
@ -377,8 +373,10 @@ protected:
Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID);
private:
void addCertifiedEntityOnServer(EntityItemPointer entity);
void removeCertifiedEntityOnServer(EntityItemPointer entity);
void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode);
void sendChallengeOwnershipRequestPacket(const QByteArray& id, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode);
void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
std::shared_ptr<AvatarData> _myAvatar{ nullptr };

View file

@ -49,8 +49,6 @@ class LineEntityItem : public EntityItem {
QVector<glm::vec3> getLinePoints() const;
virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; }
// never have a ray intersection pick a LineEntityItem.
virtual bool supportsDetailedIntersection() const override { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,

View file

@ -175,7 +175,7 @@ protected:
QString _textures;
ShapeType _shapeType = SHAPE_TYPE_NONE;
ShapeType _shapeType { SHAPE_TYPE_NONE };
private:
uint64_t _lastAnimated{ 0 };

View file

@ -410,6 +410,7 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropert
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
withReadLock([&] {
@ -464,6 +465,7 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
withWriteLock([&] {
@ -540,6 +542,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL);
READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor);
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
withWriteLock([&] {
@ -598,6 +601,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_SHAPE_TYPE;
requestedProperties += PROP_COMPOUND_SHAPE_URL;
requestedProperties += PROP_COLOR;
requestedProperties += PROP_ALPHA;
requestedProperties += _pulseProperties.getEntityProperties(params);
@ -656,6 +660,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType());
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL());
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
withReadLock([&] {
@ -718,11 +723,42 @@ void ParticleEffectEntityItem::debugDump() const {
}
void ParticleEffectEntityItem::setShapeType(ShapeType type) {
switch (type) {
case SHAPE_TYPE_NONE:
case SHAPE_TYPE_CAPSULE_X:
case SHAPE_TYPE_CAPSULE_Y:
case SHAPE_TYPE_CAPSULE_Z:
case SHAPE_TYPE_HULL:
case SHAPE_TYPE_SIMPLE_HULL:
case SHAPE_TYPE_SIMPLE_COMPOUND:
case SHAPE_TYPE_STATIC_MESH:
// these types are unsupported for ParticleEffectEntity
type = particle::DEFAULT_SHAPE_TYPE;
break;
default:
break;
}
withWriteLock([&] {
if (type != _shapeType) {
_shapeType = type;
_flags |= Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS;
}
_shapeType = type;
});
}
ShapeType ParticleEffectEntityItem::getShapeType() const {
return resultWithReadLock<ShapeType>([&] {
return _shapeType;
});
}
void ParticleEffectEntityItem::setCompoundShapeURL(const QString& compoundShapeURL) {
withWriteLock([&] {
_compoundShapeURL = compoundShapeURL;
});
}
QString ParticleEffectEntityItem::getCompoundShapeURL() const {
return resultWithReadLock<QString>([&] {
return _compoundShapeURL;
});
}

View file

@ -79,6 +79,7 @@ namespace particle {
static const QString DEFAULT_TEXTURES = "";
static const bool DEFAULT_EMITTER_SHOULD_TRAIL = false;
static const bool DEFAULT_ROTATE_WITH_ENTITY = false;
static const ShapeType DEFAULT_SHAPE_TYPE = ShapeType::SHAPE_TYPE_ELLIPSOID;
template <typename T>
struct Range {
@ -255,7 +256,10 @@ public:
float getAlphaSpread() const { return _particleProperties.alpha.gradient.spread; }
void setShapeType(ShapeType type) override;
virtual ShapeType getShapeType() const override { return _shapeType; }
virtual ShapeType getShapeType() const override;
QString getCompoundShapeURL() const;
virtual void setCompoundShapeURL(const QString& url);
virtual void debugDump() const override;
@ -349,7 +353,8 @@ protected:
PulsePropertyGroup _pulseProperties;
bool _isEmitting { true };
ShapeType _shapeType { SHAPE_TYPE_NONE };
ShapeType _shapeType{ particle::DEFAULT_SHAPE_TYPE };
QString _compoundShapeURL { "" };
};
#endif // hifi_ParticleEffectEntityItem_h

View file

@ -112,7 +112,7 @@ protected:
//! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain
//! prior functionality where new or unsupported shapes are treated as
//! ellipsoids.
ShapeType _collisionShapeType{ ShapeType::SHAPE_TYPE_ELLIPSOID };
ShapeType _collisionShapeType { ShapeType::SHAPE_TYPE_ELLIPSOID };
};
#endif // hifi_ShapeEntityItem_h

View file

@ -13,7 +13,6 @@
#include <glm/gtx/transform.hpp>
#include <QDebug>
#include <QUrlQuery>
#include <ByteCountCoding.h>
@ -353,7 +352,7 @@ bool ZoneEntityItem::contains(const glm::vec3& point) const {
Extents meshExtents = hfmModel.getMeshExtents();
glm::vec3 meshExtentsDiagonal = meshExtents.maximum - meshExtents.minimum;
glm::vec3 offset = -meshExtents.minimum- (meshExtentsDiagonal * getRegistrationPoint());
glm::vec3 offset = -meshExtents.minimum - (meshExtentsDiagonal * getRegistrationPoint());
glm::vec3 scale(getScaledDimensions() / meshExtentsDiagonal);
glm::mat4 hfmToEntityMatrix = glm::scale(scale) * glm::translate(offset);
@ -463,9 +462,6 @@ void ZoneEntityItem::fetchCollisionGeometryResource() {
if (hullURL.isEmpty()) {
_shapeResource.reset();
} else {
QUrlQuery queryArgs(hullURL);
queryArgs.addQueryItem("collision-hull", "");
hullURL.setQuery(queryArgs);
_shapeResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(hullURL);
}
}

View file

@ -133,7 +133,7 @@ protected:
KeyLightPropertyGroup _keyLightProperties;
AmbientLightPropertyGroup _ambientLightProperties;
ShapeType _shapeType = DEFAULT_SHAPE_TYPE;
ShapeType _shapeType { DEFAULT_SHAPE_TYPE };
QString _compoundShapeURL;
// The following 3 values are the defaults for zone creation

View file

@ -711,19 +711,19 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) {
node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]);
} else {
if (node.defined["rotation"] && node.rotation.size() == 4) {
//quat(x,y,z,w) to quat(w,x,y,z)
glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]);
tmat = glm::mat4_cast(rotquat) * tmat;
}
if (node.defined["scale"] && node.scale.size() == 3) {
glm::vec3 scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]);
glm::mat4 s = glm::mat4(1.0);
s = glm::scale(s, scale);
tmat = s * tmat;
}
if (node.defined["rotation"] && node.rotation.size() == 4) {
//quat(x,y,z,w) to quat(w,x,y,z)
glm::quat rotquat = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]);
tmat = glm::mat4_cast(rotquat) * tmat;
}
if (node.defined["translation"] && node.translation.size() == 3) {
glm::vec3 trans = glm::vec3(node.translation[0], node.translation[1], node.translation[2]);
glm::mat4 t = glm::mat4(1.0);
@ -734,15 +734,54 @@ glm::mat4 GLTFSerializer::getModelTransform(const GLTFNode& node) {
return tmat;
}
void GLTFSerializer::getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues) {
for (auto &skin : _file.skins) {
GLTFAccessor& indicesAccessor = _file.accessors[skin.inverseBindMatrices];
GLTFBufferView& indicesBufferview = _file.bufferviews[indicesAccessor.bufferView];
GLTFBuffer& indicesBuffer = _file.buffers[indicesBufferview.buffer];
int accBoffset = indicesAccessor.defined["byteOffset"] ? indicesAccessor.byteOffset : 0;
QVector<float> matrices;
addArrayOfType(indicesBuffer.blob,
indicesBufferview.byteOffset + accBoffset,
indicesAccessor.count,
matrices,
indicesAccessor.type,
indicesAccessor.componentType);
inverseBindMatrixValues.push_back(matrices.toStdVector());
}
}
void GLTFSerializer::getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, std::vector<int>& result) {
int startingIndex = 0;
int finalIndex = (int)children.size();
if (stride == -1) {
startingIndex = (int)children.size() - 1;
finalIndex = -1;
}
for (int index = startingIndex; index != finalIndex; index += stride) {
int c = children[index];
result.push_back(c);
std::vector<int> nested = _file.nodes[c].children.toStdVector();
if (nested.size() != 0) {
std::sort(nested.begin(), nested.end());
for (int r : nested) {
if (result.end() == std::find(result.begin(), result.end(), r)) {
getNodeQueueByDepthFirstChildren(nested, stride, result);
}
}
}
}
}
bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
int numNodes = _file.nodes.size();
//Build dependencies
QVector<QVector<int>> nodeDependencies(_file.nodes.size());
QVector<QVector<int>> nodeDependencies(numNodes);
int nodecount = 0;
bool hasChildren = false;
foreach(auto &node, _file.nodes) {
//nodes_transforms.push_back(getModelTransform(node));
hasChildren |= !node.children.isEmpty();
foreach(int child, node.children) nodeDependencies[child].push_back(nodecount);
nodecount++;
}
@ -764,26 +803,98 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
nodecount++;
}
HFMJoint joint;
joint.isSkeletonJoint = true;
joint.bindTransformFoundInCluster = false;
joint.distanceToParent = 0;
joint.parentIndex = -1;
hfmModel.joints.resize(_file.nodes.size());
hfmModel.jointIndices["x"] = _file.nodes.size();
int jointInd = 0;
for (auto& node : _file.nodes) {
int size = node.transforms.size();
if (hasChildren) { size--; }
joint.preTransform = glm::mat4(1);
for (int i = 0; i < size; i++) {
joint.preTransform = node.transforms[i] * joint.preTransform;
}
joint.name = node.name;
hfmModel.joints[jointInd] = joint;
jointInd++;
// initialize order in which nodes will be parsed
std::vector<int> nodeQueue;
nodeQueue.reserve(numNodes);
int rootNode = 0;
int finalNode = numNodes;
if (!_file.scenes[_file.scene].nodes.contains(0)) {
rootNode = numNodes - 1;
finalNode = -1;
}
bool rootAtStartOfList = rootNode < finalNode;
int nodeListStride = 1;
if (!rootAtStartOfList) { nodeListStride = -1; }
QVector<int> initialSceneNodes = _file.scenes[_file.scene].nodes;
std::sort(initialSceneNodes.begin(), initialSceneNodes.end());
int sceneRootNode = 0;
int sceneFinalNode = initialSceneNodes.size();
if (!rootAtStartOfList) {
sceneRootNode = initialSceneNodes.size() - 1;
sceneFinalNode = -1;
}
for (int index = sceneRootNode; index != sceneFinalNode; index += nodeListStride) {
int i = initialSceneNodes[index];
nodeQueue.push_back(i);
std::vector<int> children = _file.nodes[i].children.toStdVector();
std::sort(children.begin(), children.end());
getNodeQueueByDepthFirstChildren(children, nodeListStride, nodeQueue);
}
// Build joints
HFMJoint joint;
joint.distanceToParent = 0;
hfmModel.jointIndices["x"] = numNodes;
hfmModel.hasSkeletonJoints = false;
for (int nodeIndex : nodeQueue) {
auto& node = _file.nodes[nodeIndex];
joint.parentIndex = -1;
if (!_file.scenes[_file.scene].nodes.contains(nodeIndex)) {
joint.parentIndex = std::distance(nodeQueue.begin(), std::find(nodeQueue.begin(), nodeQueue.end(), nodeDependencies[nodeIndex][0]));
}
joint.transform = node.transforms.first();
joint.translation = extractTranslation(joint.transform);
joint.rotation = glmExtractRotation(joint.transform);
glm::vec3 scale = extractScale(joint.transform);
joint.postTransform = glm::scale(glm::mat4(), scale);
joint.name = node.name;
joint.isSkeletonJoint = false;
hfmModel.joints.push_back(joint);
}
// Build skeleton
std::vector<glm::mat4> jointInverseBindTransforms;
jointInverseBindTransforms.resize(numNodes);
if (!_file.skins.isEmpty()) {
std::vector<std::vector<float>> inverseBindValues;
getSkinInverseBindMatrices(inverseBindValues);
int jointIndex = finalNode;
while (jointIndex != rootNode) {
rootAtStartOfList ? jointIndex-- : jointIndex++;
int jOffset = nodeQueue[jointIndex];
auto joint = hfmModel.joints[jointIndex];
hfmModel.hasSkeletonJoints = true;
for (int s = 0; s < _file.skins.size(); s++) {
auto skin = _file.skins[s];
joint.isSkeletonJoint = skin.joints.contains(jOffset);
if (joint.isSkeletonJoint) {
std::vector<float> value = inverseBindValues[s];
int matrixCount = 16 * skin.joints.indexOf(jOffset);
jointInverseBindTransforms[jointIndex] =
glm::mat4(value[matrixCount], value[matrixCount + 1], value[matrixCount + 2], value[matrixCount + 3],
value[matrixCount + 4], value[matrixCount + 5], value[matrixCount + 6], value[matrixCount + 7],
value[matrixCount + 8], value[matrixCount + 9], value[matrixCount + 10], value[matrixCount + 11],
value[matrixCount + 12], value[matrixCount + 13], value[matrixCount + 14], value[matrixCount + 15]);
} else {
jointInverseBindTransforms[jointIndex] = glm::mat4();
}
glm::vec3 bindTranslation = extractTranslation(hfmModel.offset * glm::inverse(jointInverseBindTransforms[jointIndex]));
hfmModel.bindExtents.addPoint(bindTranslation);
}
hfmModel.joints[jointIndex] = joint;
}
}
//Build materials
QVector<QString> materialIDs;
@ -803,23 +914,39 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
}
nodecount = 0;
// Build meshes
foreach(auto &node, _file.nodes) {
nodecount = 0;
for (int nodeIndex = rootNode; nodeIndex != finalNode; nodeIndex += nodeListStride) {
auto& node = _file.nodes[nodeIndex];
if (node.defined["mesh"]) {
qCDebug(modelformat) << "node_transforms" << node.transforms;
foreach(auto &primitive, _file.meshes[node.mesh].primitives) {
hfmModel.meshes.append(HFMMesh());
HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1];
HFMCluster cluster;
cluster.jointIndex = nodecount;
cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
mesh.clusters.append(cluster);
if (!hfmModel.hasSkeletonJoints) {
HFMCluster cluster;
cluster.jointIndex = nodecount;
cluster.inverseBindMatrix = glm::mat4();
cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
mesh.clusters.append(cluster);
} else {
for (int j = rootNode; j != finalNode; j += nodeListStride) {
HFMCluster cluster;
cluster.jointIndex = j;
cluster.inverseBindMatrix = jointInverseBindTransforms[j];
cluster.inverseBindTransform = Transform(cluster.inverseBindMatrix);
mesh.clusters.append(cluster);
}
}
HFMCluster root;
root.jointIndex = rootNode;
if (root.jointIndex == -1) {
root.jointIndex = 0;
}
root.inverseBindMatrix = jointInverseBindTransforms[root.jointIndex];
root.inverseBindTransform = Transform(root.inverseBindMatrix);
mesh.clusters.append(root);
HFMMeshPart part = HFMMeshPart();
@ -848,6 +975,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
}
QList<QString> keys = primitive.attributes.values.keys();
QVector<uint16_t> clusterJoints;
QVector<float> clusterWeights;
foreach(auto &key, keys) {
int accessorIdx = primitive.attributes.values[key];
@ -949,8 +1078,69 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
for (int n = 0; n < texcoords.size(); n = n + 2) {
mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1]));
}
} else if (key == "JOINTS_0") {
QVector<uint16_t> joints;
success = addArrayOfType(buffer.blob,
bufferview.byteOffset + accBoffset,
accessor.count,
joints,
accessor.type,
accessor.componentType);
if (!success) {
qWarning(modelformat) << "There was a problem reading glTF JOINTS_0 data for model " << _url;
continue;
}
for (int n = 0; n < joints.size(); n++) {
clusterJoints.push_back(joints[n]);
}
} else if (key == "WEIGHTS_0") {
QVector<float> weights;
success = addArrayOfType(buffer.blob,
bufferview.byteOffset + accBoffset,
accessor.count,
weights,
accessor.type,
accessor.componentType);
if (!success) {
qWarning(modelformat) << "There was a problem reading glTF WEIGHTS_0 data for model " << _url;
continue;
}
for (int n = 0; n < weights.size(); n++) {
clusterWeights.push_back(weights[n]);
}
}
}
// adapted from FBXSerializer.cpp
if (hfmModel.hasSkeletonJoints) {
int numClusterIndices = clusterJoints.size();
const int WEIGHTS_PER_VERTEX = 4;
const float ALMOST_HALF = 0.499f;
int numVertices = mesh.vertices.size();
mesh.clusterIndices.fill(mesh.clusters.size() - 1, numClusterIndices);
mesh.clusterWeights.fill(0, numClusterIndices);
for (int c = 0; c < clusterJoints.size(); c++) {
mesh.clusterIndices[c] = _file.skins[node.skin].joints[clusterJoints[c]];
}
// normalize and compress to 16-bits
for (int i = 0; i < numVertices; ++i) {
int j = i * WEIGHTS_PER_VERTEX;
float totalWeight = 0.0f;
for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
totalWeight += clusterWeights[k];
}
if (totalWeight > 0.0f) {
float weightScalingFactor = (float)(UINT16_MAX) / totalWeight;
for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) {
mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * clusterWeights[k] + ALMOST_HALF);
}
} else {
mesh.clusterWeights[j] = (uint16_t)((float)(UINT16_MAX) + ALMOST_HALF);
}
}
}
if (primitive.defined["material"]) {
@ -959,8 +1149,8 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) {
mesh.parts.push_back(part);
// populate the texture coordinates if they don't exist
if (mesh.texCoords.size() == 0) {
for (int i = 0; i < part.triangleIndices.size(); i++) mesh.texCoords.push_back(glm::vec2(0.0, 1.0));
if (mesh.texCoords.size() == 0 && !hfmModel.hasSkeletonJoints) {
for (int i = 0; i < part.triangleIndices.size(); i++) { mesh.texCoords.push_back(glm::vec2(0.0, 1.0)); }
}
mesh.meshExtents.reset();
foreach(const glm::vec3& vertex, mesh.vertices) {

View file

@ -712,6 +712,8 @@ private:
hifi::ByteArray _glbBinary;
glm::mat4 getModelTransform(const GLTFNode& node);
void getSkinInverseBindMatrices(std::vector<std::vector<float>>& inverseBindMatrixValues);
void getNodeQueueByDepthFirstChildren(std::vector<int>& children, int stride, std::vector<int>& result);
bool buildGeometry(HFMModel& hfmModel, const hifi::URL& url);
bool parseGLTF(const hifi::ByteArray& data);

View file

@ -15,7 +15,7 @@
#include "GraphicsScriptingUtil.h"
#include "ScriptableMesh.h"
#include "graphics/Material.h"
#include "image/Image.h"
#include "image/TextureProcessing.h"
// #define SCRIPTABLE_MESH_DEBUG 1

View file

@ -362,7 +362,6 @@ MeshPointer Mesh::createIndexedTriangles_P3F(uint32_t numVertices, uint32_t numI
mesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(numIndices * sizeof(uint32_t), (gpu::Byte*) indices), gpu::Element::INDEX_INT32));
}
std::vector<graphics::Mesh::Part> parts;
parts.push_back(graphics::Mesh::Part(0, numIndices, 0, graphics::Mesh::TRIANGLES));
mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));

View file

@ -3,6 +3,7 @@ setup_hifi_library()
link_hifi_libraries(shared gpu)
target_nvtt()
target_etc2comp()
target_openexr()
if (UNIX AND NOT APPLE)
set(THREADS_PREFER_PTHREAD_FLAG ON)

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,10 @@
#pragma once
//
// Image.h
// image/src/image
// image/src/Image
//
// Created by Clement Brisset on 4/5/2017.
// Copyright 2017 High Fidelity, Inc.
// Created by Olivier Prat on 29/3/2019.
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -12,80 +13,85 @@
#ifndef hifi_image_Image_h
#define hifi_image_Image_h
#include <QVariant>
#include <gpu/Texture.h>
#include <QImage>
#include "ColorChannel.h"
class QByteArray;
class QImage;
#include <glm/fwd.hpp>
#include <glm/vec2.hpp>
#include <GLMHelpers.h>
namespace image {
namespace TextureUsage {
class Image {
public:
enum Type {
DEFAULT_TEXTURE,
STRICT_TEXTURE,
ALBEDO_TEXTURE,
NORMAL_TEXTURE,
BUMP_TEXTURE,
SPECULAR_TEXTURE,
METALLIC_TEXTURE = SPECULAR_TEXTURE, // for now spec and metallic texture are the same, converted to grey
ROUGHNESS_TEXTURE,
GLOSS_TEXTURE,
EMISSIVE_TEXTURE,
CUBE_TEXTURE,
OCCLUSION_TEXTURE,
SCATTERING_TEXTURE = OCCLUSION_TEXTURE,
LIGHTMAP_TEXTURE,
UNUSED_TEXTURE
};
enum Format {
Format_Invalid = QImage::Format_Invalid,
Format_Mono = QImage::Format_Mono,
Format_MonoLSB = QImage::Format_MonoLSB,
Format_Indexed8 = QImage::Format_Indexed8,
Format_RGB32 = QImage::Format_RGB32,
Format_ARGB32 = QImage::Format_ARGB32,
Format_ARGB32_Premultiplied = QImage::Format_ARGB32_Premultiplied,
Format_RGB16 = QImage::Format_RGB16,
Format_ARGB8565_Premultiplied = QImage::Format_ARGB8565_Premultiplied,
Format_RGB666 = QImage::Format_RGB666,
Format_ARGB6666_Premultiplied = QImage::Format_ARGB6666_Premultiplied,
Format_RGB555 = QImage::Format_RGB555,
Format_ARGB8555_Premultiplied = QImage::Format_ARGB8555_Premultiplied,
Format_RGB888 = QImage::Format_RGB888,
Format_RGB444 = QImage::Format_RGB444,
Format_ARGB4444_Premultiplied = QImage::Format_ARGB4444_Premultiplied,
Format_RGBX8888 = QImage::Format_RGBX8888,
Format_RGBA8888 = QImage::Format_RGBA8888,
Format_RGBA8888_Premultiplied = QImage::Format_RGBA8888_Premultiplied,
Format_Grayscale8 = QImage::Format_Grayscale8,
Format_R11G11B10F = QImage::Format_RGB30,
Format_PACKED_FLOAT = Format_R11G11B10F
};
using TextureLoader = std::function<gpu::TexturePointer(QImage&&, const std::string&, bool, gpu::BackendTarget, const std::atomic<bool>&)>;
TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap());
using AspectRatioMode = Qt::AspectRatioMode;
using TransformationMode = Qt::TransformationMode;
gpu::TexturePointer create2DTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createStrict2DTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createAlbedoTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createEmissiveTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createNormalTextureFromNormalImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createNormalTextureFromBumpImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createRoughnessTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createRoughnessTextureFromGlossImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createMetallicTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createCubeTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createLightmapTextureFromImage(QImage&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer process2DTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
gpu::BackendTarget target, bool isStrict, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer process2DTextureNormalMapFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
gpu::BackendTarget target, bool isBumpMap, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer process2DTextureGrayscaleFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
gpu::BackendTarget target, bool isInvertedPixels, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer processCubeTextureColorFromImage(QImage&& srcImage, const std::string& srcImageName, bool compress,
gpu::BackendTarget target, bool generateIrradiance, const std::atomic<bool>& abortProcessing);
Image() {}
Image(int width, int height, Format format) : _data(width, height, (QImage::Format)format) {}
Image(const QImage& data) : _data(data) {}
void operator=(const QImage& image) {
_data = image;
}
} // namespace TextureUsage
bool isNull() const { return _data.isNull(); }
const QStringList getSupportedFormats();
Format getFormat() const { return (Format)_data.format(); }
bool hasAlphaChannel() const { return _data.hasAlphaChannel(); }
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& url, ColorChannel sourceChannel,
int maxNumPixels, TextureUsage::Type textureType,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
glm::uint32 getWidth() const { return (glm::uint32)_data.width(); }
glm::uint32 getHeight() const { return (glm::uint32)_data.height(); }
glm::uvec2 getSize() const { return toGlm(_data.size()); }
size_t getByteCount() const { return _data.byteCount(); }
QRgb getPixel(int x, int y) const { return _data.pixel(x, y); }
void setPixel(int x, int y, QRgb value) {
_data.setPixel(x, y, value);
}
glm::uint8* editScanLine(int y) { return _data.scanLine(y); }
const glm::uint8* getScanLine(int y) const { return _data.scanLine(y); }
const glm::uint8* getBits() const { return _data.constBits(); }
Image getScaled(glm::uvec2 newSize, AspectRatioMode ratioMode, TransformationMode transformationMode = Qt::SmoothTransformation) const;
Image getConvertedToFormat(Format newFormat) const;
Image getSubImage(QRect rect) const;
Image getMirrored(bool horizontal, bool vertical) const;
// Inplace transformations
void invertPixels();
private:
QImage _data;
};
} // namespace image

View file

@ -0,0 +1,99 @@
//
// OpenEXRReader.cpp
// image/src/image
//
// Created by Olivier Prat
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OpenEXRReader.h"
#include "TextureProcessing.h"
#include "ImageLogging.h"
#include <QIODevice>
#include <QDebug>
#if !defined(Q_OS_ANDROID)
#include <OpenEXR/ImfIO.h>
#include <OpenEXR/ImfRgbaFile.h>
#include <OpenEXR/ImfArray.h>
#include <OpenEXR/ImfTestFile.h>
class QIODeviceImfStream : public Imf::IStream {
public:
QIODeviceImfStream(QIODevice& device, const std::string& filename) :
Imf::IStream(filename.c_str()), _device(device) {
}
bool read(char c[/*n*/], int n) override {
if (_device.read(c, n) <= 0) {
qWarning(imagelogging) << "OpenEXR - in file " << fileName() << " : " << _device.errorString();
return false;
}
return true;
}
Imf::Int64 tellg() override {
return _device.pos();
}
void seekg(Imf::Int64 pos) override {
_device.seek(pos);
}
void clear() override {
// Not much to do
}
private:
QIODevice& _device;
};
#endif
image::Image image::readOpenEXR(QIODevice& content, const std::string& filename) {
#if !defined(Q_OS_ANDROID)
QIODeviceImfStream device(content, filename);
if (Imf::isOpenExrFile(device)) {
Imf::RgbaInputFile file(device);
Imath::Box2i viewport = file.dataWindow();
Imf::Array2D<Imf::Rgba> pixels;
int width = viewport.max.x - viewport.min.x + 1;
int height = viewport.max.y - viewport.min.y + 1;
pixels.resizeErase(height, width);
file.setFrameBuffer(&pixels[0][0] - viewport.min.x - viewport.min.y * width, 1, width);
file.readPixels(viewport.min.y, viewport.max.y);
Image image{ width, height, Image::Format_PACKED_FLOAT };
auto packHDRPixel = getHDRPackingFunction();
for (int y = 0; y < height; y++) {
const auto srcScanline = pixels[y];
gpu::uint32* dstScanline = (gpu::uint32*) image.editScanLine(y);
for (int x = 0; x < width; x++) {
const auto& srcPixel = srcScanline[x];
auto& dstPixel = dstScanline[x];
glm::vec3 floatPixel{ srcPixel.r, srcPixel.g, srcPixel.b };
dstPixel = packHDRPixel(floatPixel);
}
}
return image;
} else {
qWarning(imagelogging) << "OpenEXR - File " << filename.c_str() << " doesn't have the proper format";
}
#endif
return QImage();
}

View file

@ -0,0 +1,24 @@
//
// OpenEXRReader.h
// image/src/image
//
// Created by Olivier Prat
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_image_OpenEXRReader_h
#define hifi_image_OpenEXRReader_h
#include "Image.h"
namespace image {
// TODO Move this into a plugin that QImageReader can use
Image readOpenEXR(QIODevice& contents, const std::string& filename);
}
#endif // hifi_image_OpenEXRReader_h

View file

@ -16,7 +16,7 @@
#include <QIODevice>
#include <QDebug>
QImage image::readTGA(QIODevice& content) {
image::Image image::readTGA(QIODevice& content) {
enum class TGAImageType : uint8_t {
NoImageData = 0,
UncompressedColorMapped = 1,

View file

@ -12,12 +12,11 @@
#ifndef hifi_image_TGAReader_h
#define hifi_image_TGAReader_h
#include <QImage>
#include "Image.h"
namespace image {
// TODO Move this into a plugin that QImageReader can use
QImage readTGA(QIODevice& contents);
Image readTGA(QIODevice& contents);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,92 @@
//
// TextureProcessing.h
// image/src/TextureProcessing
//
// Created by Clement Brisset on 4/5/2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_image_TextureProcessing_h
#define hifi_image_TextureProcessing_h
#include <QVariant>
#include <gpu/Texture.h>
#include "Image.h"
namespace image {
std::function<gpu::uint32(const glm::vec3&)> getHDRPackingFunction();
std::function<glm::vec3(gpu::uint32)> getHDRUnpackingFunction();
namespace TextureUsage {
enum Type {
DEFAULT_TEXTURE,
STRICT_TEXTURE,
ALBEDO_TEXTURE,
NORMAL_TEXTURE,
BUMP_TEXTURE,
SPECULAR_TEXTURE,
METALLIC_TEXTURE = SPECULAR_TEXTURE, // for now spec and metallic texture are the same, converted to grey
ROUGHNESS_TEXTURE,
GLOSS_TEXTURE,
EMISSIVE_TEXTURE,
CUBE_TEXTURE,
OCCLUSION_TEXTURE,
SCATTERING_TEXTURE = OCCLUSION_TEXTURE,
LIGHTMAP_TEXTURE,
UNUSED_TEXTURE
};
using TextureLoader = std::function<gpu::TexturePointer(Image&&, const std::string&, bool, gpu::BackendTarget, const std::atomic<bool>&)>;
TextureLoader getTextureLoaderForType(Type type, const QVariantMap& options = QVariantMap());
gpu::TexturePointer create2DTextureFromImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createStrict2DTextureFromImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createAlbedoTextureFromImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createEmissiveTextureFromImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createNormalTextureFromNormalImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createNormalTextureFromBumpImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createRoughnessTextureFromImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createRoughnessTextureFromGlossImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createMetallicTextureFromImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createCubeTextureFromImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createCubeTextureFromImageWithoutIrradiance(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer createLightmapTextureFromImage(Image&& image, const std::string& srcImageName,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer process2DTextureColorFromImage(Image&& srcImage, const std::string& srcImageName, bool compress,
gpu::BackendTarget target, bool isStrict, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer process2DTextureNormalMapFromImage(Image&& srcImage, const std::string& srcImageName, bool compress,
gpu::BackendTarget target, bool isBumpMap, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer process2DTextureGrayscaleFromImage(Image&& srcImage, const std::string& srcImageName, bool compress,
gpu::BackendTarget target, bool isInvertedPixels, const std::atomic<bool>& abortProcessing);
gpu::TexturePointer processCubeTextureColorFromImage(Image&& srcImage, const std::string& srcImageName, bool compress,
gpu::BackendTarget target, bool generateIrradiance, const std::atomic<bool>& abortProcessing);
} // namespace TextureUsage
const QStringList getSupportedFormats();
gpu::TexturePointer processImage(std::shared_ptr<QIODevice> content, const std::string& url, ColorChannel sourceChannel,
int maxNumPixels, TextureUsage::Type textureType,
bool compress, gpu::BackendTarget target, const std::atomic<bool>& abortProcessing = false);
} // namespace image
#endif // hifi_image_TextureProcessing_h

View file

@ -34,7 +34,7 @@
#include <gl/GLHelpers.h>
#include <gpu/Batch.h>
#include <image/Image.h>
#include <image/TextureProcessing.h>
#include <NumericalConstants.h>
#include <shared/NsightHelpers.h>

View file

@ -23,7 +23,7 @@
#include <ResourceCache.h>
#include <graphics/TextureMap.h>
#include <image/ColorChannel.h>
#include <image/Image.h>
#include <image/TextureProcessing.h>
#include <ktx/KTX.h>
#include <TextureMeta.h>

View file

@ -267,6 +267,7 @@ enum class EntityVersion : PacketVersion {
ReOrderParentIDProperties,
CertificateTypeProperty,
DisableWebMedia,
ParticleShapeType,
// Add new versions above here
NUM_PACKET_TYPE,

View file

@ -97,7 +97,7 @@ bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) {
_errorString = "Duplicate Id entries";
return false;
}
gotId = true;
if (nextToken() != '"') {
_errorString = "Invalid Id value";
return false;
@ -107,14 +107,21 @@ bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) {
_errorString = "Invalid Id string";
return false;
}
QUuid idValue = QUuid::fromString(QLatin1String(idString.c_str()) );
if (idValue.isNull()) {
_errorString = "Id value invalid UUID string: " + idString;
return false;
}
parsedEntities["Id"] = idValue;
gotId = true;
// some older archives may have a null string id, so
// return success without setting parsedEntities,
// which will result in a new uuid for the restored
// archive. (not parsing and using isNull as parsing
// results in null if there is a corrupt string)
if (idString != "{00000000-0000-0000-0000-000000000000}") {
QUuid idValue = QUuid::fromString(QLatin1String(idString.c_str()) );
if (idValue.isNull()) {
_errorString = "Id value invalid UUID string: " + idString;
return false;
}
parsedEntities["Id"] = idValue;
}
} else if (key == "Version") {
if (gotVersion) {
_errorString = "Duplicate Version entries";

View file

@ -245,7 +245,7 @@ void OculusMobileDisplayPlugin::updatePresentPose() {
});
}
void OculusMobileDisplayPlugin::internalPresent(const gpu::FramebufferPointer& compsiteFramebuffer) {
void OculusMobileDisplayPlugin::internalPresent() {
VrHandler::pollTask();
if (!vrActive()) {
@ -253,12 +253,8 @@ void OculusMobileDisplayPlugin::internalPresent(const gpu::FramebufferPointer& c
return;
}
GLuint sourceTexture = 0;
glm::uvec2 sourceSize;
if (compsiteFramebuffer) {
sourceTexture = getGLBackend()->getTextureID(compsiteFramebuffer->getRenderBuffer(0));
sourceSize = { compsiteFramebuffer->getWidth(), compsiteFramebuffer->getHeight() };
}
auto sourceTexture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0));
glm::uvec2 sourceSize{ _compositeFramebuffer->getWidth(), _compositeFramebuffer->getHeight() };
VrHandler::presentFrame(sourceTexture, sourceSize, presentTracking);
_presentRate.increment();
}

View file

@ -54,8 +54,8 @@ protected:
void uncustomizeContext() override;
void updatePresentPose() override;
void internalPresent(const gpu::FramebufferPointer&) override;
void hmdPresent(const gpu::FramebufferPointer&) override { throw std::runtime_error("Unused"); }
void internalPresent() override;
void hmdPresent() override { throw std::runtime_error("Unused"); }
bool isHmdMounted() const override;
bool alwaysPresent() const override { return true; }

View file

@ -217,9 +217,8 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const {
}
return MOTION_TYPE_DYNAMIC;
}
if (_entity->isMovingRelativeToParent() ||
_entity->hasActions() ||
_entity->hasGrabs() ||
if (_entity->hasActions() ||
_entity->isMovingRelativeToParent() ||
_entity->hasAncestorOfType(NestableType::Avatar)) {
return MOTION_TYPE_KINEMATIC;
}

View file

@ -2,12 +2,6 @@
#include <NumericalConstants.h>
const DisplayPlugin::HUDOperator DisplayPlugin::DEFAULT_HUD_OPERATOR{ std::function<void(gpu::Batch&, const gpu::TexturePointer&, const gpu::FramebufferPointer&, bool mirror)>() };
DisplayPlugin::DisplayPlugin() : _hudOperator{ DEFAULT_HUD_OPERATOR } {
}
int64_t DisplayPlugin::getPaintDelayUsecs() const {
std::lock_guard<std::mutex> lock(_paintDelayMutex);
return _paintDelayTimer.isValid() ? _paintDelayTimer.nsecsElapsed() / NSECS_PER_USEC : 0;
@ -41,8 +35,8 @@ void DisplayPlugin::waitForPresent() {
}
}
std::function<void(gpu::Batch&, const gpu::TexturePointer&, const gpu::FramebufferPointer& compositeFramebuffer, bool mirror)> DisplayPlugin::getHUDOperator() {
HUDOperator hudOperator;
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> DisplayPlugin::getHUDOperator() {
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> hudOperator;
{
QMutexLocker locker(&_presentMutex);
hudOperator = _hudOperator;
@ -54,5 +48,3 @@ glm::mat4 HmdDisplay::getEyeToHeadTransform(Eye eye) const {
static const glm::mat4 xform;
return xform;
}

View file

@ -27,6 +27,7 @@
#include <SimpleMovingAverage.h>
#include <gpu/Forward.h>
#include "Plugin.h"
#include "StencilMode.h"
class QOpenGLFramebufferObject;
@ -121,8 +122,6 @@ class DisplayPlugin : public Plugin, public HmdDisplay {
Q_OBJECT
using Parent = Plugin;
public:
DisplayPlugin();
virtual int getRequiredThreadCount() const { return 0; }
virtual bool isHmd() const { return false; }
virtual int getHmdScreen() const { return -1; }
@ -216,14 +215,17 @@ public:
void waitForPresent();
float getAveragePresentTime() { return _movingAveragePresent.average / (float)USECS_PER_MSEC; } // in msec
using HUDOperator = std::function<void(gpu::Batch&, const gpu::TexturePointer&, const gpu::FramebufferPointer&, bool mirror)>;
virtual HUDOperator getHUDOperator() final;
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> getHUDOperator();
static const QString& MENU_PATH();
// for updating plugin-related commands. Mimics the input plugin.
virtual void pluginUpdate() = 0;
virtual StencilMode getStencilMaskMode() const { return StencilMode::NONE; }
using StencilMaskMeshOperator = std::function<void(gpu::Batch&)>;
virtual StencilMaskMeshOperator getStencilMaskMeshOperator() { return nullptr; }
signals:
void recommendedFramebufferSizeChanged(const QSize& size);
void resetSensorsRequested();
@ -234,8 +236,7 @@ protected:
gpu::ContextPointer _gpuContext;
static const HUDOperator DEFAULT_HUD_OPERATOR;
HUDOperator _hudOperator;
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> _hudOperator { std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)>() };
MovingAverage<float, 10> _movingAveragePresent;

View file

@ -126,8 +126,8 @@ void CompositeHUD::run(const RenderContextPointer& renderContext, const gpu::Fra
if (inputs) {
batch.setFramebuffer(inputs);
}
if (renderContext->args->_hudOperator && renderContext->args->_blitFramebuffer) {
renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_blitFramebuffer, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE);
if (renderContext->args->_hudOperator) {
renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE);
}
});
#endif

View file

@ -19,7 +19,6 @@ using namespace render;
void PrepareStencil::configure(const Config& config) {
_maskMode = config.maskMode;
_forceDraw = config.forceDraw;
}
graphics::MeshPointer PrepareStencil::getMesh() {
@ -43,6 +42,7 @@ gpu::PipelinePointer PrepareStencil::getMeshStencilPipeline() {
auto state = std::make_shared<gpu::State>();
drawMask(*state);
state->setColorWriteMask(gpu::State::WRITE_NONE);
state->setCullMode(gpu::State::CullMode::CULL_NONE);
_meshStencilPipeline = gpu::Pipeline::create(program, state);
}
@ -64,30 +64,37 @@ gpu::PipelinePointer PrepareStencil::getPaintStencilPipeline() {
void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) {
RenderArgs* args = renderContext->args;
// Only draw the stencil mask if in HMD mode or not forced.
if (!_forceDraw && (args->_displayMode != RenderArgs::STEREO_HMD)) {
StencilMode maskMode = _maskMode;
std::function<void(gpu::Batch&)> maskOperator = [this](gpu::Batch& batch) {
auto mesh = getMesh();
batch.setIndexBuffer(mesh->getIndexBuffer());
batch.setInputFormat((mesh->getVertexFormat()));
batch.setInputStream(0, mesh->getVertexStream());
// Draw
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
};
if (maskMode == StencilMode::NONE) {
maskMode = args->_stencilMode;
maskOperator = args->_stencilMaskOperator;
}
if (maskMode == StencilMode::NONE || (maskMode == StencilMode::MESH && !maskOperator)) {
return;
}
doInBatch("PrepareStencil::run", args->_context, [&](gpu::Batch& batch) {
batch.enableStereo(false);
batch.setViewportTransform(args->_viewport);
if (_maskMode < 0) {
batch.setPipeline(getMeshStencilPipeline());
auto mesh = getMesh();
batch.setIndexBuffer(mesh->getIndexBuffer());
batch.setInputFormat((mesh->getVertexFormat()));
batch.setInputStream(0, mesh->getVertexStream());
// Draw
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
} else {
if (maskMode == StencilMode::PAINT) {
batch.setPipeline(getPaintStencilPipeline());
batch.draw(gpu::TRIANGLE_STRIP, 4);
} else if (maskMode == StencilMode::MESH) {
batch.setPipeline(getMeshStencilPipeline());
maskOperator(batch);
}
});
}

View file

@ -15,17 +15,19 @@
#include <render/Engine.h>
#include <gpu/Pipeline.h>
#include <graphics/Geometry.h>
#include <StencilMode.h>
class PrepareStencilConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(int maskMode MEMBER maskMode NOTIFY dirty)
Q_PROPERTY(bool forceDraw MEMBER forceDraw NOTIFY dirty)
Q_PROPERTY(StencilMode maskMode MEMBER maskMode NOTIFY dirty)
public:
PrepareStencilConfig(bool enabled = true) : JobConfig(enabled) {}
int maskMode { 0 };
bool forceDraw { false };
// -1 -> don't force drawing (fallback to render args mode)
// 0 -> force draw without mesh
// 1 -> force draw with mesh
StencilMode maskMode { StencilMode::NONE };
signals:
void dirty();
@ -66,8 +68,7 @@ private:
graphics::MeshPointer _mesh;
graphics::MeshPointer getMesh();
int _maskMode { 0 };
bool _forceDraw { false };
StencilMode _maskMode { StencilMode::NONE };
};

View file

@ -16,11 +16,11 @@
#include <GLMHelpers.h>
#include <ViewFrustum.h>
#include <StencilMode.h>
#include <gpu/Forward.h>
#include "Forward.h"
class AABox;
namespace render {
@ -131,8 +131,11 @@ namespace render {
render::ScenePointer _scene;
int8_t _cameraMode { -1 };
std::function<void(gpu::Batch&, const gpu::TexturePointer&, const gpu::FramebufferPointer&, bool mirror)> _hudOperator;
std::function<void(gpu::Batch&, const gpu::TexturePointer&, bool mirror)> _hudOperator;
gpu::TexturePointer _hudTexture;
StencilMode _stencilMode { StencilMode::NONE };
std::function<void(gpu::Batch&)> _stencilMaskOperator;
};
}

View file

@ -358,6 +358,12 @@ glm::vec3 Triangle::getNormal() const {
return glm::normalize(glm::cross(edge1, edge2));
}
float Triangle::getArea() const {
glm::vec3 edge1 = v1 - v0;
glm::vec3 edge2 = v2 - v0;
return 0.5f * glm::length(glm::cross(edge1, edge2));
}
Triangle Triangle::operator*(const glm::mat4& transform) const {
return {
glm::vec3(transform * glm::vec4(v0, 1.0f)),

View file

@ -125,6 +125,7 @@ public:
glm::vec3 v1;
glm::vec3 v2;
glm::vec3 getNormal() const;
float getArea() const;
Triangle operator*(const glm::mat4& transform) const;
};

View file

@ -40,6 +40,7 @@ int qMapURLStringMetaTypeId = qRegisterMetaType<QMap<QUrl,QString>>();
int socketErrorMetaTypeId = qRegisterMetaType<QAbstractSocket::SocketError>();
int voidLambdaType = qRegisterMetaType<std::function<void()>>();
int variantLambdaType = qRegisterMetaType<std::function<QVariant()>>();
int stencilModeMetaTypeId = qRegisterMetaType<StencilMode>();
void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, vec2ToScriptValue, vec2FromScriptValue);
@ -64,6 +65,8 @@ void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue);
qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue);
qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue);
qScriptRegisterMetaType(engine, stencilModeToScriptValue, stencilModeFromScriptValue);
}
QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2) {
@ -1283,4 +1286,12 @@ QVariantMap parseTexturesToMap(QString newTextures, const QVariantMap& defaultTe
}
return toReturn;
}
QScriptValue stencilModeToScriptValue(QScriptEngine* engine, const StencilMode& stencilMode) {
return engine->newVariant((int)stencilMode);
}
void stencilModeFromScriptValue(const QScriptValue& object, StencilMode& stencilMode) {
stencilMode = StencilMode(object.toVariant().toInt());
}

View file

@ -25,6 +25,7 @@
#include "shared/Bilateral.h"
#include "Transform.h"
#include "PhysicsCollisionGroups.h"
#include "StencilMode.h"
class QColor;
class QUrl;
@ -729,5 +730,8 @@ void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>
QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures);
Q_DECLARE_METATYPE(StencilMode)
QScriptValue stencilModeToScriptValue(QScriptEngine* engine, const StencilMode& stencilMode);
void stencilModeFromScriptValue(const QScriptValue &object, StencilMode& stencilMode);
#endif // hifi_RegisteredMetaTypes_h

View file

@ -0,0 +1,19 @@
//
// Created by Sam Gondelman on 3/26/19.
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_StencilMode_h
#define hifi_StencilMode_h
enum class StencilMode {
NONE = -1, // for legacy reasons, this is -1
PAINT = 0,
MESH = 1
};
#endif // hifi_StencilMode_h

View file

@ -18,7 +18,7 @@ if (WIN32 AND (NOT USE_GLES))
link_hifi_libraries(
shared task gl shaders gpu ${PLATFORM_GL_BACKEND} controllers ui qml
plugins ui-plugins display-plugins input-plugins
audio-client networking render-utils
audio-client networking render-utils graphics
${PLATFORM_GL_BACKEND}
)
include_hifi_library_headers(octree)

View file

@ -227,3 +227,66 @@ QVector<glm::vec3> OculusBaseDisplayPlugin::getSensorPositions() {
return result;
}
DisplayPlugin::StencilMaskMeshOperator OculusBaseDisplayPlugin::getStencilMaskMeshOperator() {
if (_session) {
if (!_stencilMeshesInitialized) {
_stencilMeshesInitialized = true;
ovr::for_each_eye([&](ovrEyeType eye) {
ovrFovStencilDesc stencilDesc = {
ovrFovStencil_HiddenArea, 0, eye,
_eyeRenderDescs[eye].Fov, _eyeRenderDescs[eye].HmdToEyePose.Orientation
};
// First we get the size of the buffer we need
ovrFovStencilMeshBuffer buffer = { 0, 0, nullptr, 0, 0, nullptr };
ovrResult result = ovr_GetFovStencil(_session, &stencilDesc, &buffer);
if (!OVR_SUCCESS(result)) {
_stencilMeshesInitialized = false;
return;
}
std::vector<ovrVector2f> ovrVertices(buffer.UsedVertexCount);
std::vector<uint16_t> ovrIndices(buffer.UsedIndexCount);
// Now we populate the actual buffer
buffer = { (int)ovrVertices.size(), 0, ovrVertices.data(), (int)ovrIndices.size(), 0, ovrIndices.data() };
result = ovr_GetFovStencil(_session, &stencilDesc, &buffer);
if (!OVR_SUCCESS(result)) {
_stencilMeshesInitialized = false;
return;
}
std::vector<glm::vec3> vertices;
vertices.reserve(ovrVertices.size());
for (auto& ovrVertex : ovrVertices) {
// We need the vertices in clip space
vertices.emplace_back(ovrVertex.x - (1.0f - (float)eye), 2.0f * ovrVertex.y - 1.0f, 0.0f);
}
std::vector<uint32_t> indices;
indices.reserve(ovrIndices.size());
for (auto& ovrIndex : ovrIndices) {
indices.push_back(ovrIndex);
}
_stencilMeshes[eye] = graphics::Mesh::createIndexedTriangles_P3F((uint32_t)vertices.size(), (uint32_t)indices.size(), vertices.data(), indices.data());
});
}
if (_stencilMeshesInitialized) {
return [&](gpu::Batch& batch) {
for (auto& mesh : _stencilMeshes) {
batch.setIndexBuffer(mesh->getIndexBuffer());
batch.setInputFormat((mesh->getVertexFormat()));
batch.setInputStream(0, mesh->getVertexStream());
// Draw
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
}
};
}
}
return nullptr;
}

View file

@ -16,6 +16,8 @@
#define OVRPL_DISABLED
#include <OVR_Platform.h>
#include <graphics/Geometry.h>
class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
using Parent = HmdDisplayPlugin;
public:
@ -34,6 +36,9 @@ public:
QRectF getPlayAreaRect() override;
QVector<glm::vec3> getSensorPositions() override;
virtual StencilMode getStencilMaskMode() const override { return StencilMode::MESH; }
virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override;
protected:
void customizeContext() override;
void uncustomizeContext() override;
@ -52,4 +57,7 @@ protected:
// ovrLayerEyeFovDepth _depthLayer;
bool _hmdMounted { false };
bool _visible { true };
std::array<graphics::MeshPointer, 2> _stencilMeshes;
bool _stencilMeshesInitialized { false };
};

View file

@ -16,7 +16,7 @@ public:
bool isSupported() const override;
protected:
void hmdPresent(const gpu::FramebufferPointer&) override {}
void hmdPresent() override {}
bool isHmdMounted() const override { return true; }
private:

View file

@ -108,16 +108,13 @@ void OculusDisplayPlugin::customizeContext() {
}
void OculusDisplayPlugin::uncustomizeContext() {
#if 0
if (_currentFrame && _currentFrame->framebuffer) {
// Present a final black frame to the HMD
_currentFrame->framebuffer->Bound(FramebufferTarget::Draw, [] {
Context::ClearColor(0, 0, 0, 1);
Context::Clear().ColorBuffer();
});
hmdPresent();
}
// Present a final black frame to the HMD
_compositeFramebuffer->Bound(FramebufferTarget::Draw, [] {
Context::ClearColor(0, 0, 0, 1);
Context::Clear().ColorBuffer();
});
hmdPresent();
#endif
ovr_DestroyTextureSwapChain(_session, _textureSwapChain);
@ -130,7 +127,7 @@ void OculusDisplayPlugin::uncustomizeContext() {
static const uint64_t FRAME_BUDGET = (11 * USECS_PER_MSEC);
static const uint64_t FRAME_OVER_BUDGET = (15 * USECS_PER_MSEC);
void OculusDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
void OculusDisplayPlugin::hmdPresent() {
static uint64_t lastSubmitEnd = 0;
if (!_customized) {
@ -160,8 +157,15 @@ void OculusDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFra
auto fbo = getGLBackend()->getFramebufferID(_outputFramebuffer);
glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, curTexId, 0);
render([&](gpu::Batch& batch) {
auto viewport = ivec4(uvec2(), _outputFramebuffer->getSize());
renderFromTexture(batch, compositeFramebuffer->getRenderBuffer(0), viewport, viewport, _outputFramebuffer);
batch.enableStereo(false);
batch.setFramebuffer(_outputFramebuffer);
batch.setViewportTransform(ivec4(uvec2(), _outputFramebuffer->getSize()));
batch.setStateScissorRect(ivec4(uvec2(), _outputFramebuffer->getSize()));
batch.resetViewTransform();
batch.setProjectionTransform(mat4());
batch.setPipeline(_presentPipeline);
batch.setResourceTexture(0, _compositeFramebuffer->getRenderBuffer(0));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0, 0, 0);
}

View file

@ -28,7 +28,7 @@ protected:
QThread::Priority getPresentPriority() override { return QThread::TimeCriticalPriority; }
bool internalActivate() override;
void hmdPresent(const gpu::FramebufferPointer&) override;
void hmdPresent() override;
bool isHmdMounted() const override;
void customizeContext() override;
void uncustomizeContext() override;

View file

@ -237,7 +237,7 @@ void OculusLegacyDisplayPlugin::uncustomizeContext() {
Parent::uncustomizeContext();
}
void OculusLegacyDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
void OculusLegacyDisplayPlugin::hmdPresent() {
if (!_hswDismissed) {
ovrHSWDisplayState hswState;
ovrHmd_GetHSWDisplayState(_hmd, &hswState);
@ -252,7 +252,7 @@ void OculusLegacyDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compos
memset(eyePoses, 0, sizeof(ovrPosef) * 2);
eyePoses[0].Orientation = eyePoses[1].Orientation = ovrRotation;
GLint texture = getGLBackend()->getTextureID(compositeFramebuffer->getRenderBuffer(0));
GLint texture = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0));
auto sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
if (_hmdWindow->makeCurrent()) {

View file

@ -39,7 +39,7 @@ protected:
void customizeContext() override;
void uncustomizeContext() override;
void hmdPresent(const gpu::FramebufferPointer&) override;
void hmdPresent() override;
bool isHmdMounted() const override { return true; }
private:

View file

@ -511,13 +511,13 @@ void OpenVrDisplayPlugin::customizeContext() {
Parent::customizeContext();
if (_threadedSubmit) {
// _compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0);
_compositeInfos[0].texture = _compositeFramebuffer->getRenderBuffer(0);
for (size_t i = 0; i < COMPOSITING_BUFFER_SIZE; ++i) {
// if (0 != i) {
if (0 != i) {
_compositeInfos[i].texture = gpu::Texture::createRenderBuffer(gpu::Element::COLOR_RGBA_32, _renderTargetSize.x,
_renderTargetSize.y, gpu::Texture::SINGLE_MIP,
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT));
// }
}
_compositeInfos[i].textureID = getGLBackend()->getTextureID(_compositeInfos[i].texture);
}
_submitThread->_canvas = _submitCanvas;
@ -613,17 +613,17 @@ bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
return Parent::beginFrameRender(frameIndex);
}
void OpenVrDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& compositeFramebuffer) {
void OpenVrDisplayPlugin::compositeLayers() {
if (_threadedSubmit) {
++_renderingIndex;
_renderingIndex %= COMPOSITING_BUFFER_SIZE;
auto& newComposite = _compositeInfos[_renderingIndex];
newComposite.pose = _currentPresentFrameInfo.presentPose;
compositeFramebuffer->setRenderBuffer(0, newComposite.texture);
_compositeFramebuffer->setRenderBuffer(0, newComposite.texture);
}
Parent::compositeLayers(compositeFramebuffer);
Parent::compositeLayers();
if (_threadedSubmit) {
auto& newComposite = _compositeInfos[_renderingIndex];
@ -645,13 +645,13 @@ void OpenVrDisplayPlugin::compositeLayers(const gpu::FramebufferPointer& composi
}
}
void OpenVrDisplayPlugin::hmdPresent(const gpu::FramebufferPointer& compositeFramebuffer) {
void OpenVrDisplayPlugin::hmdPresent() {
PROFILE_RANGE_EX(render, __FUNCTION__, 0xff00ff00, (uint64_t)_currentFrame->frameIndex)
if (_threadedSubmit) {
_submitThread->waitForPresent();
} else {
GLuint glTexId = getGLBackend()->getTextureID(compositeFramebuffer->getRenderBuffer(0));
GLuint glTexId = getGLBackend()->getTextureID(_compositeFramebuffer->getRenderBuffer(0));
vr::Texture_t vrTexture{ (void*)(uintptr_t)glTexId, vr::TextureType_OpenGL, vr::ColorSpace_Auto };
vr::VRCompositor()->Submit(vr::Eye_Left, &vrTexture, &OPENVR_TEXTURE_BOUNDS_LEFT);
vr::VRCompositor()->Submit(vr::Eye_Right, &vrTexture, &OPENVR_TEXTURE_BOUNDS_RIGHT);
@ -784,3 +784,49 @@ QRectF OpenVrDisplayPlugin::getPlayAreaRect() {
return QRectF(center.x, center.y, dimensions.x, dimensions.y);
}
DisplayPlugin::StencilMaskMeshOperator OpenVrDisplayPlugin::getStencilMaskMeshOperator() {
if (_system) {
if (!_stencilMeshesInitialized) {
_stencilMeshesInitialized = true;
for (auto eye : VR_EYES) {
vr::HiddenAreaMesh_t stencilMesh = _system->GetHiddenAreaMesh(eye);
if (stencilMesh.pVertexData && stencilMesh.unTriangleCount > 0) {
std::vector<glm::vec3> vertices;
std::vector<uint32_t> indices;
const int NUM_INDICES_PER_TRIANGLE = 3;
int numIndices = stencilMesh.unTriangleCount * NUM_INDICES_PER_TRIANGLE;
vertices.reserve(numIndices);
indices.reserve(numIndices);
for (int i = 0; i < numIndices; i++) {
vr::HmdVector2_t vertex2D = stencilMesh.pVertexData[i];
// We need the vertices in clip space
vertices.emplace_back(vertex2D.v[0] - (1.0f - (float)eye), 2.0f * vertex2D.v[1] - 1.0f, 0.0f);
indices.push_back(i);
}
_stencilMeshes[eye] = graphics::Mesh::createIndexedTriangles_P3F((uint32_t)vertices.size(), (uint32_t)indices.size(), vertices.data(), indices.data());
} else {
_stencilMeshesInitialized = false;
}
}
}
if (_stencilMeshesInitialized) {
return [&](gpu::Batch& batch) {
for (auto& mesh : _stencilMeshes) {
batch.setIndexBuffer(mesh->getIndexBuffer());
batch.setInputFormat((mesh->getVertexFormat()));
batch.setInputStream(0, mesh->getVertexStream());
// Draw
auto part = mesh->getPartBuffer().get<graphics::Mesh::Part>(0);
batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex);
}
};
}
}
return nullptr;
}

View file

@ -13,6 +13,8 @@
#include <display-plugins/hmd/HmdDisplayPlugin.h>
#include <graphics/Geometry.h>
const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only.
namespace gl {
@ -67,13 +69,16 @@ public:
QRectF getPlayAreaRect() override;
virtual StencilMode getStencilMaskMode() const override{ return StencilMode::MESH; }
virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override;
protected:
bool internalActivate() override;
void internalDeactivate() override;
void updatePresentPose() override;
void compositeLayers(const gpu::FramebufferPointer&) override;
void hmdPresent(const gpu::FramebufferPointer&) override;
void compositeLayers() override;
void hmdPresent() override;
bool isHmdMounted() const override;
void postPreview() override;
@ -94,4 +99,7 @@ private:
bool _asyncReprojectionActive { false };
bool _hmdMounted { false };
std::array<graphics::MeshPointer, 2> _stencilMeshes;
bool _stencilMeshesInitialized { false };
};

View file

@ -227,6 +227,14 @@
"speedSpread": {
"tooltip": "The spread in speeds at which particles are emitted at, resulting in a variety of speeds."
},
"particleShapeType": {
"tooltip": "The shape of the surface from which to emit particles.",
"jsPropertyName": "shapeType"
},
"particleCompoundShapeURL": {
"tooltip": "The model file to use for the particle emitter if Shape Type is \"Use Compound Shape URL\".",
"jsPropertyName": "compoundShapeURL"
},
"emitDimensions": {
"tooltip": "The outer limit radius in dimensions that the particles can be emitted from."
},

View file

@ -807,6 +807,21 @@ const GROUPS = [
decimals: 2,
propertyID: "speedSpread",
},
{
label: "Shape Type",
type: "dropdown",
options: { "box": "Box", "ellipsoid": "Ellipsoid",
"cylinder-y": "Cylinder", "circle": "Circle", "plane": "Plane",
"compound": "Use Compound Shape URL" },
propertyID: "particleShapeType",
propertyName: "shapeType",
},
{
label: "Compound Shape URL",
type: "string",
propertyID: "particleCompoundShapeURL",
propertyName: "compoundShapeURL",
},
{
label: "Emit Dimensions",
type: "vec3",

View file

@ -480,7 +480,7 @@ void AWSInterface::createEntry(const int index, const QString& testResult, QText
if (differenceFileFound) {
stream << "\t\t\t\t<td><img src=\"./" << folder << "/" << resultName << "/" << _comparisonImageFilename << "\" width = \"576\" height = \"324\" ></td>\n";
} else {
stream << "\t\t\t\t<td><h2>No Image Found</h2>\n";
stream << "\t\t\t\t<td><h2>Image size mismatch</h2>\n";
}
stream << "\t\t\t</tr>\n";

View file

@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Nitpick.h"
#include "Platform.h"
#ifdef Q_OS_WIN
#include <windows.h>
@ -40,9 +41,15 @@ Nitpick::Nitpick(QWidget* parent) : QMainWindow(parent) {
setWindowTitle("Nitpick - " + nitpickVersion);
clientProfiles << "VR-High" << "Desktop-High" << "Desktop-Low" << "Mobile-Touch" << "VR-Standalone";
_ui.clientProfileComboBox->insertItems(0, clientProfiles);
_GPUVendors << "Nvidia" << "AMD";
_ui.gpuVendorComboBox->insertItems(0, _GPUVendors);
QString gpuVendor = Platform::getGraphicsCardType().toUpper();
if (gpuVendor.contains("NVIDIA")) {
_ui.gpuVendorComboBox->setCurrentIndex(0);
} else {
_ui.gpuVendorComboBox->setCurrentIndex(1);
}
}
Nitpick::~Nitpick() {
@ -126,13 +133,14 @@ void Nitpick::setup() {
);
}
void Nitpick::startTestsEvaluation(const bool isRunningFromCommandLine,
const bool isRunningInAutomaticTestRun,
const QString& snapshotDirectory,
const QString& branch,
const QString& user
void Nitpick::startTestsEvaluation(
const bool isRunningFromCommandLine,
const bool isRunningInAutomaticTestRun,
const QString& snapshotDirectory,
const QString& branch,
const QString& user
) {
_testCreator->startTestsEvaluation(isRunningFromCommandLine, isRunningInAutomaticTestRun, snapshotDirectory, branch, user);
_testCreator->startTestsEvaluation(_ui.gpuVendorComboBox, isRunningFromCommandLine, isRunningInAutomaticTestRun, snapshotDirectory, branch, user);
}
void Nitpick::on_tabWidget_currentChanged(int index) {
@ -144,9 +152,11 @@ void Nitpick::on_tabWidget_currentChanged(int index) {
#endif
_ui.userLineEdit->setDisabled(false);
_ui.branchLineEdit->setDisabled(false);
_ui.gpuVendorComboBox->setDisabled(false);
} else {
_ui.userLineEdit->setDisabled(true);
_ui.branchLineEdit->setDisabled(true);
_ui.gpuVendorComboBox->setDisabled(true);
}
}
@ -159,7 +169,7 @@ void Nitpick::on_createAllRecursiveScriptsPushbutton_clicked() {
}
void Nitpick::on_createTestsPushbutton_clicked() {
_testCreator->createTests(_ui.clientProfileComboBox->currentText());
_testCreator->createTests(_ui.gpuVendorComboBox->currentText());
}
void Nitpick::on_createMDFilePushbutton_clicked() {
@ -250,7 +260,7 @@ void Nitpick::on_showTaskbarPushbutton_clicked() {
}
void Nitpick::on_evaluateTestsPushbutton_clicked() {
_testCreator->startTestsEvaluation(false, false);
_testCreator->startTestsEvaluation(_ui.gpuVendorComboBox, false, false);
}
void Nitpick::on_closePushbutton_clicked() {

View file

@ -28,11 +28,13 @@ public:
void setup();
void startTestsEvaluation(const bool isRunningFromCommandLine,
const bool isRunningInAutomaticTestRun,
const QString& snapshotDirectory,
const QString& branch,
const QString& user);
void startTestsEvaluation(
const bool isRunningFromCommandLine,
const bool isRunningInAutomaticTestRun,
const QString& snapshotDirectory,
const QString& branch,
const QString& user
);
void automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures);
@ -111,7 +113,7 @@ private:
bool _isRunningFromCommandline{ false };
QStringList clientProfiles;
QStringList _GPUVendors;
};
#endif // hifi_Nitpick_h

View file

@ -0,0 +1,121 @@
//
// Platform.h
//
// Created by Nissim Hadar on 2 Apr 2019.
// Copyright 2013 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 "Platform.h"
#include <vector>
#ifdef Q_OS_WIN
#include <dxgi1_3.h>
#pragma comment(lib, "dxgi.lib")
#elif defined Q_OS_MAC
#include <OpenGL/OpenGL.h>
#include <sstream>
#include <QStringList>
#endif
QString Platform::getGraphicsCardType() {
#ifdef Q_OS_WIN
IDXGIFactory1* pFactory = nullptr;
HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory));
if (hr != S_OK || pFactory == nullptr) {
return "Unable to create DXGI";
}
std::vector<int> validAdapterList;
using AdapterEntry = std::pair<std::pair<DXGI_ADAPTER_DESC1, LARGE_INTEGER>, std::vector<DXGI_OUTPUT_DESC>>;
std::vector<AdapterEntry> adapterToOutputs;
// Enumerate adapters and outputs
{
UINT adapterNum = 0;
IDXGIAdapter1* pAdapter = nullptr;
while (pFactory->EnumAdapters1(adapterNum, &pAdapter) != DXGI_ERROR_NOT_FOUND) {
// Found an adapter, get descriptor
DXGI_ADAPTER_DESC1 adapterDesc;
pAdapter->GetDesc1(&adapterDesc);
LARGE_INTEGER version;
hr = pAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &version);
std::wstring wDescription (adapterDesc.Description);
std::string description(wDescription.begin(), wDescription.end());
AdapterEntry adapterEntry;
adapterEntry.first.first = adapterDesc;
adapterEntry.first.second = version;
UINT outputNum = 0;
IDXGIOutput * pOutput;
bool hasOutputConnectedToDesktop = false;
while (pAdapter->EnumOutputs(outputNum, &pOutput) != DXGI_ERROR_NOT_FOUND) {
// FOund an output attached to the adapter, get descriptor
DXGI_OUTPUT_DESC outputDesc;
pOutput->GetDesc(&outputDesc);
adapterEntry.second.push_back(outputDesc);
std::wstring wDeviceName(outputDesc.DeviceName);
std::string deviceName(wDeviceName.begin(), wDeviceName.end());
hasOutputConnectedToDesktop |= (bool)outputDesc.AttachedToDesktop;
pOutput->Release();
outputNum++;
}
adapterToOutputs.push_back(adapterEntry);
// add this adapter to the valid list if has output
if (hasOutputConnectedToDesktop && !adapterEntry.second.empty()) {
validAdapterList.push_back(adapterNum);
}
pAdapter->Release();
adapterNum++;
}
}
pFactory->Release();
if (!validAdapterList.empty()) {
auto& adapterEntry = adapterToOutputs[validAdapterList.front()];
std::wstring wDescription(adapterEntry.first.first.Description);
std::string description(wDescription.begin(), wDescription.end());
return QString(description.c_str());
}
#elif defined Q_OS_MAC
FILE* stream = popen("system_profiler SPDisplaysDataType | grep Chipset", "r");
std::ostringstream hostStream;
while (!feof(stream) && !ferror(stream)) {
char buf[128];
int bytesRead = fread(buf, 1, 128, stream);
hostStream.write(buf, bytesRead);
}
QString result = QString::fromStdString(hostStream.str());
QStringList parts = result.split('\n');
for (int i = 0; i < parts.size(); ++i) {
if (parts[i].toLower().contains("radeon") || parts[i].toLower().contains("nvidia")) {
return parts[i];
}
}
// unkown graphics card
return "UNKNOWN";
#else
return QString("NO IMPLEMENTED");
#endif
return "";
}

View file

@ -0,0 +1,20 @@
//
// Platform.h
//
// Created by Nissim Hadar on 2 Apr 2019.
// Copyright 2013 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_Platform_h
#define hifi_Platform_h
#include <QString>
class Platform {
public:
static QString getGraphicsCardType();
};
#endif

View file

@ -67,7 +67,7 @@ QString TestCreator::zipAndDeleteTestResultsFolder() {
return zippedResultsFileName;
}
int TestCreator::compareImageLists() {
int TestCreator::compareImageLists(const QString& gpuVendor) {
_progressBar->setMinimum(0);
_progressBar->setMaximum(_expectedImagesFullFilenames.length() - 1);
_progressBar->setValue(0);
@ -108,8 +108,16 @@ int TestCreator::compareImageLists() {
};
_mismatchWindow.setTestResult(testResult);
if (similarityIndex < THRESHOLD_GLOBAL || worstTileValue < THRESHOLD_LOCAL) {
// Lower threshold for non-Nvidia GPUs
double thresholdGlobal{ 0.9995 };
double thresholdLocal{ 0.6 };
if (gpuVendor != "Nvidia") {
thresholdGlobal = 0.97;
thresholdLocal = 0.2;
}
if (similarityIndex < thresholdGlobal || worstTileValue < thresholdLocal) {
if (!isInteractiveMode) {
++numberOfFailures;
appendTestResultsToFile(testResult, _mismatchWindow.getComparisonImage(), _mismatchWindow.getSSIMResultsImage(testResult._ssimResults), true);
@ -258,11 +266,13 @@ void::TestCreator::appendTestResultsToFile(QString testResultFilename, bool hasF
}
}
void TestCreator::startTestsEvaluation(const bool isRunningFromCommandLine,
const bool isRunningInAutomaticTestRun,
const QString& snapshotDirectory,
const QString& branchFromCommandLine,
const QString& userFromCommandLine
void TestCreator::startTestsEvaluation(
QComboBox *gpuVendor,
const bool isRunningFromCommandLine,
const bool isRunningInAutomaticTestRun,
const QString& snapshotDirectory,
const QString& branchFromCommandLine,
const QString& userFromCommandLine
) {
_isRunningFromCommandLine = isRunningFromCommandLine;
_isRunningInAutomaticTestRun = isRunningInAutomaticTestRun;
@ -332,12 +342,12 @@ void TestCreator::startTestsEvaluation(const bool isRunningFromCommandLine,
}
_downloader->downloadFiles(expectedImagesURLs, _snapshotDirectory, _expectedImagesFilenames, (void *)this);
finishTestsEvaluation();
finishTestsEvaluation(gpuVendor->currentText());
}
void TestCreator::finishTestsEvaluation() {
void TestCreator::finishTestsEvaluation(const QString& gpuVendor) {
// First - compare the pairs of images
int numberOfFailures = compareImageLists();
int numberOfFailures = compareImageLists(gpuVendor);
// Next - check text results
numberOfFailures += checkTextResults();
@ -430,8 +440,9 @@ void TestCreator::createTests(const QString& clientProfile) {
parent += "/";
}
_testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select test root folder", parent,
QFileDialog::ShowDirsOnly);
if (!createAllFilesSetup()) {
return;
}
// If user canceled then restore previous selection and return
if (_testsRootDirectory == "") {
@ -881,23 +892,12 @@ void TestCreator::createRecursiveScript(const QString& directory, bool interacti
}
void TestCreator::createTestsOutline() {
QString previousSelection = _testDirectory;
QString parent = previousSelection.left(previousSelection.lastIndexOf('/'));
if (!parent.isNull() && parent.right(1) != "/") {
parent += "/";
}
_testDirectory =
QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", parent, QFileDialog::ShowDirsOnly);
// If user canceled then restore previous selection and return
if (_testDirectory == "") {
_testDirectory = previousSelection;
if (!createAllFilesSetup()) {
return;
}
const QString testsOutlineFilename { "testsOutline.md" };
QString mdFilename(_testDirectory + "/" + testsOutlineFilename);
QString mdFilename(_testsRootDirectory + "/" + testsOutlineFilename);
QFile mdFile(mdFilename);
if (!mdFile.open(QIODevice::WriteOnly)) {
QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename);
@ -911,10 +911,10 @@ void TestCreator::createTestsOutline() {
stream << "Directories with an appended (*) have an automatic test\n\n";
// We need to know our current depth, as this isn't given by QDirIterator
int rootDepth { _testDirectory.count('/') };
int rootDepth { _testsRootDirectory.count('/') };
// Each test is shown as the folder name linking to the matching GitHub URL, and the path to the associated test.md file
QDirIterator it(_testDirectory, QDirIterator::Subdirectories);
QDirIterator it(_testsRootDirectory, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString directory = it.next();

View file

@ -45,13 +45,16 @@ class TestCreator {
public:
TestCreator(QProgressBar* progressBar, QCheckBox* checkBoxInteractiveMode);
void startTestsEvaluation(const bool isRunningFromCommandLine,
const bool isRunningInAutomaticTestRun,
const QString& snapshotDirectory = QString(),
const QString& branchFromCommandLine = QString(),
const QString& userFromCommandLine = QString());
void startTestsEvaluation(
QComboBox *gpuVendor,
const bool isRunningFromCommandLine,
const bool isRunningInAutomaticTestRun,
const QString& snapshotDirectory = QString(),
const QString& branchFromCommandLine = QString(),
const QString& userFromCommandLine = QString()
);
void finishTestsEvaluation();
void finishTestsEvaluation(const QString& gpuVendor);
void createTests(const QString& clientProfile);
@ -79,7 +82,7 @@ public:
void createRecursiveScript();
void createRecursiveScript(const QString& directory, bool interactiveMode);
int compareImageLists();
int compareImageLists(const QString& gpuVendor);
int checkTextResults();
QStringList createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory);
@ -124,9 +127,6 @@ private:
const QString TEST_RESULTS_FOLDER { "TestResults" };
const QString TEST_RESULTS_FILENAME { "TestResults.txt" };
const double THRESHOLD_GLOBAL{ 0.9995 };
const double THRESHOLD_LOCAL { 0.6 };
QDir _imageDirectory;
MismatchWindow _mismatchWindow;

View file

@ -60,5 +60,5 @@ const double R_Y = 0.212655f;
const double G_Y = 0.715158f;
const double B_Y = 0.072187f;
const QString nitpickVersion{ "v3.1.5" };
const QString nitpickVersion{ "v3.2.0" };
#endif // hifi_common_h

View file

@ -56,7 +56,7 @@
<property name="geometry">
<rect>
<x>70</x>
<y>40</y>
<y>120</y>
<width>220</width>
<height>40</height>
</rect>
@ -69,7 +69,7 @@
<property name="geometry">
<rect>
<x>70</x>
<y>180</y>
<y>200</y>
<width>220</width>
<height>40</height>
</rect>
@ -82,7 +82,7 @@
<property name="geometry">
<rect>
<x>320</x>
<y>180</y>
<y>200</y>
<width>220</width>
<height>40</height>
</rect>
@ -94,7 +94,7 @@
<widget class="QPushButton" name="createTestsOutlinePushbutton">
<property name="geometry">
<rect>
<x>210</x>
<x>320</x>
<y>120</y>
<width>220</width>
<height>40</height>
@ -108,7 +108,7 @@
<property name="geometry">
<rect>
<x>70</x>
<y>300</y>
<y>360</y>
<width>220</width>
<height>40</height>
</rect>
@ -121,7 +121,7 @@
<property name="geometry">
<rect>
<x>320</x>
<y>300</y>
<y>360</y>
<width>220</width>
<height>40</height>
</rect>
@ -134,7 +134,7 @@
<property name="geometry">
<rect>
<x>70</x>
<y>240</y>
<y>280</y>
<width>220</width>
<height>40</height>
</rect>
@ -147,7 +147,7 @@
<property name="geometry">
<rect>
<x>320</x>
<y>240</y>
<y>280</y>
<width>220</width>
<height>40</height>
</rect>
@ -156,16 +156,6 @@
<string>Create all testAuto scripts</string>
</property>
</widget>
<widget class="QComboBox" name="clientProfileComboBox">
<property name="geometry">
<rect>
<x>320</x>
<y>40</y>
<width>120</width>
<height>40</height>
</rect>
</property>
</widget>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
@ -927,8 +917,8 @@
<rect>
<x>330</x>
<y>190</y>
<width>181</width>
<height>51</height>
<width>180</width>
<height>50</height>
</rect>
</property>
<property name="text">
@ -1186,6 +1176,39 @@
</rect>
</property>
</widget>
<widget class="QComboBox" name="gpuVendorComboBox">
<property name="geometry">
<rect>
<x>450</x>
<y>60</y>
<width>180</width>
<height>40</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>505</x>
<y>40</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>GPU Vendor</string>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">

View file

@ -14,7 +14,7 @@
#include <QtCore/QDebug>
#include <QtCore/QThread>
#include <image/Image.h>
#include <image/TextureProcessing.h>
#include <DependencyManager.h>
#include <StatTracker.h>

View file

@ -14,7 +14,7 @@
#include <QtCore/QCommandLineParser>
#include <QtCore/QUrl>
#include <image/Image.h>
#include <image/TextureProcessing.h>
#include <TextureBaker.h>
#include "BakerCLI.h"