mirror of
https://github.com/lubosz/overte.git
synced 2025-04-19 13:44:05 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi
This commit is contained in:
commit
3aa387ddb9
142 changed files with 6508 additions and 1946 deletions
1
BUILD.md
1
BUILD.md
|
@ -18,6 +18,7 @@
|
|||
* [oglplus](http://oglplus.org/) ~> 0.63
|
||||
* [OpenVR](https://github.com/ValveSoftware/openvr) ~> 0.91 (Win32 only)
|
||||
* [Polyvox](http://www.volumesoffun.com/) ~> 0.2.1
|
||||
* [QuaZip](http://sourceforge.net/projects/quazip/files/quazip/) ~> 0.7.1
|
||||
* [SDL2](https://www.libsdl.org/download-2.0.php) ~> 2.0.3
|
||||
* [soxr](http://soxr.sourceforge.net) ~> 0.1.1
|
||||
* [Intel Threading Building Blocks](https://www.threadingbuildingblocks.org/) ~> 4.3
|
||||
|
|
|
@ -204,6 +204,7 @@ set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX})
|
|||
setup_externals_binary_dir()
|
||||
|
||||
option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
|
||||
option(GET_QUAZIP "Get QuaZip library automatically as external project" 1)
|
||||
|
||||
|
||||
if (WIN32)
|
||||
|
|
|
@ -392,7 +392,6 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) {
|
|||
const int16_t* nextSoundOutput = NULL;
|
||||
|
||||
if (_avatarSound) {
|
||||
|
||||
const QByteArray& soundByteArray = _avatarSound->getByteArray();
|
||||
nextSoundOutput = reinterpret_cast<const int16_t*>(soundByteArray.data()
|
||||
+ _numAvatarSoundSentBytes);
|
||||
|
@ -442,6 +441,10 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) {
|
|||
audioPacket->writePrimitive(headOrientation);
|
||||
|
||||
} else if (nextSoundOutput) {
|
||||
// write the codec
|
||||
QString codecName;
|
||||
audioPacket->writeString(codecName);
|
||||
|
||||
// assume scripted avatar audio is mono and set channel flag to zero
|
||||
audioPacket->writePrimitive((quint8)0);
|
||||
|
||||
|
|
55
cmake/externals/quazip/CMakeLists.txt
vendored
Normal file
55
cmake/externals/quazip/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
set(EXTERNAL_NAME quazip)
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
cmake_policy(SET CMP0046 OLD)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
if (WIN32)
|
||||
# windows shell does not like backslashes expanded on the command line,
|
||||
# so convert all backslashes in the QT path to forward slashes
|
||||
string(REPLACE \\ / QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
|
||||
elseif ($ENV{QT_CMAKE_PREFIX_PATH})
|
||||
set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
|
||||
endif ()
|
||||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://s3-us-west-1.amazonaws.com/hifi-production/dependencies/quazip-0.7.2.zip
|
||||
URL_MD5 2955176048a31262c09259ca8d309d19
|
||||
BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> -DCMAKE_PREFIX_PATH=${QT_CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_NAME_DIR:PATH=<INSTALL_DIR>/lib -DZLIB_ROOT=${ZLIB_ROOT} -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
LOG_DOWNLOAD 1
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
|
||||
add_dependencies(quazip zlib)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES
|
||||
FOLDER "hidden/externals"
|
||||
INSTALL_NAME_DIR ${INSTALL_DIR}/lib
|
||||
BUILD_WITH_INSTALL_RPATH True)
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include CACHE PATH "List of QuaZip include directories")
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${${EXTERNAL_NAME_UPPER}_INCLUDE_DIR} CACHE PATH "List of QuaZip include directories")
|
||||
set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${INSTALL_DIR}/lib CACHE FILEPATH "Location of QuaZip DLL")
|
||||
|
||||
if (APPLE)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.1.0.0.dylib CACHE FILEPATH "Location of QuaZip release library")
|
||||
elseif (WIN32)
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/quazip5.lib CACHE FILEPATH "Location of QuaZip release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/quazip5d.lib CACHE FILEPATH "Location of QuaZip release library")
|
||||
else ()
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libquazip5.so CACHE FILEPATH "Location of QuaZip release library")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libquazip5d.so CACHE FILEPATH "Location of QuaZip release library")
|
||||
endif ()
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
select_library_configurations(${EXTERNAL_NAME_UPPER})
|
||||
|
||||
# Force selected libraries into the cache
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE FILEPATH "Location of QuaZip libraries")
|
||||
set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${${EXTERNAL_NAME_UPPER}_LIBRARIES} CACHE FILEPATH "Location of QuaZip libraries")
|
16
cmake/macros/TargetQuazip.cmake
Normal file
16
cmake/macros/TargetQuazip.cmake
Normal file
|
@ -0,0 +1,16 @@
|
|||
#
|
||||
# Copyright 2015 High Fidelity, Inc.
|
||||
# Created by Leonardo Murillo on 2015/11/20
|
||||
#
|
||||
# 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_QUAZIP)
|
||||
add_dependency_external_projects(quazip)
|
||||
find_package(QuaZip REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${QUAZIP_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES})
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs(${QUAZIP_DLL_PATH})
|
||||
endif ()
|
||||
endmacro()
|
29
cmake/modules/FindQuaZip.cmake
Normal file
29
cmake/modules/FindQuaZip.cmake
Normal file
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# FindQuaZip.h
|
||||
# StackManagerQt/cmake/modules
|
||||
#
|
||||
# Created by Mohammed Nafees.
|
||||
# Copyright (c) 2014 High Fidelity. All rights reserved.
|
||||
#
|
||||
|
||||
# QUAZIP_FOUND - QuaZip library was found
|
||||
# QUAZIP_INCLUDE_DIR - Path to QuaZip include dir
|
||||
# QUAZIP_INCLUDE_DIRS - Path to QuaZip and zlib include dir (combined from QUAZIP_INCLUDE_DIR + ZLIB_INCLUDE_DIR)
|
||||
# QUAZIP_LIBRARIES - List of QuaZip libraries
|
||||
# QUAZIP_ZLIB_INCLUDE_DIR - The include dir of zlib headers
|
||||
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("quazip")
|
||||
|
||||
if (WIN32)
|
||||
find_path(QUAZIP_INCLUDE_DIRS quazip.h PATH_SUFFIXES include/quazip HINTS ${QUAZIP_SEARCH_DIRS})
|
||||
elseif (APPLE)
|
||||
find_path(QUAZIP_INCLUDE_DIRS quazip.h PATH_SUFFIXES include/quazip HINTS ${QUAZIP_SEARCH_DIRS})
|
||||
else ()
|
||||
find_path(QUAZIP_INCLUDE_DIRS quazip.h PATH_SUFFIXES quazip HINTS ${QUAZIP_SEARCH_DIRS})
|
||||
endif ()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(QUAZIP DEFAULT_MSG QUAZIP_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(QUAZIP_INCLUDE_DIRS QUAZIP_SEARCH_DIRS)
|
|
@ -218,16 +218,22 @@ ScrollingWindow {
|
|||
onIconChanged: {
|
||||
console.log("New icon: " + icon)
|
||||
}
|
||||
onNewViewRequested:{
|
||||
onNewViewRequested: {
|
||||
var component = Qt.createComponent("Browser.qml");
|
||||
var newWindow = component.createObject(desktop);
|
||||
request.openIn(newWindow.webView)
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
desktop.initWebviewProfileHandlers(webview.profile)
|
||||
}
|
||||
|
||||
|
||||
//profile: desktop.browserProfile
|
||||
}
|
||||
|
||||
} // item
|
||||
|
||||
|
||||
|
||||
Keys.onPressed: {
|
||||
switch(event.key) {
|
||||
case Qt.Key_L:
|
||||
|
|
103
interface/resources/qml/MarketplaceComboBox.qml
Normal file
103
interface/resources/qml/MarketplaceComboBox.qml
Normal file
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// MarketplaceComboBox.qml
|
||||
//
|
||||
// Created by Elisa Lupin-Jimenez on 3 Aug 2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtWebChannel 1.0
|
||||
import QtWebEngine 1.1
|
||||
import QtWebSockets 1.0
|
||||
import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel
|
||||
|
||||
import "controls"
|
||||
import "controls-uit" as Controls
|
||||
import "styles"
|
||||
import "styles-uit"
|
||||
|
||||
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi }
|
||||
id: marketplaceComboBox
|
||||
anchors.fill: parent
|
||||
color: hifi.colors.baseGrayShadow
|
||||
property var currentUrl: "https://metaverse.highfidelity.com/marketplace"
|
||||
|
||||
Controls.WebView {
|
||||
id: webview
|
||||
url: currentUrl
|
||||
anchors.top: switchMarketView.bottom
|
||||
width: parent.width
|
||||
height: parent.height - 40
|
||||
focus: true
|
||||
|
||||
Timer {
|
||||
id: zipTimer
|
||||
running: false
|
||||
repeat: false
|
||||
interval: 1500
|
||||
property var handler;
|
||||
onTriggered: handler();
|
||||
}
|
||||
|
||||
property var autoCancel: 'var element = $("a.btn.cancel");
|
||||
element.click();'
|
||||
|
||||
onNewViewRequested: {
|
||||
var component = Qt.createComponent("Browser.qml");
|
||||
var newWindow = component.createObject(desktop);
|
||||
request.openIn(newWindow.webView);
|
||||
if (File.isZippedFbx(desktop.currentUrl)) {
|
||||
zipTimer.handler = function() {
|
||||
newWindow.destroy();
|
||||
runJavaScript(autoCancel);
|
||||
}
|
||||
zipTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
property var simpleDownload: 'var element = $("a.download-file");
|
||||
element.removeClass("download-file");
|
||||
element.removeAttr("download");'
|
||||
|
||||
onLinkHovered: {
|
||||
desktop.currentUrl = hoveredUrl;
|
||||
// add an error message for non-fbx files
|
||||
if (File.isZippedFbx(desktop.currentUrl)) {
|
||||
runJavaScript(simpleDownload, function(){console.log("ran the JS");});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Controls.ComboBox {
|
||||
id: switchMarketView
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
width: 200
|
||||
height: 40
|
||||
visible: true
|
||||
model: ["Marketplace", "Clara.io"]
|
||||
onCurrentIndexChanged: {
|
||||
if (currentIndex === 0) { webview.url = "https://metaverse.highfidelity.com/marketplace"; }
|
||||
if (currentIndex === 1) { webview.url = "https://clara.io/library"; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
id: switchMarketLabel
|
||||
anchors.verticalCenter: switchMarketView.verticalCenter
|
||||
anchors.right: switchMarketView.left
|
||||
color: hifi.colors.white
|
||||
text: "Explore interesting content from: "
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls 1.2
|
||||
import QtQuick.Controls 1.4
|
||||
import QtWebChannel 1.0
|
||||
import QtWebEngine 1.1
|
||||
import QtWebSockets 1.0
|
||||
import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel
|
||||
|
||||
import "windows" as Windows
|
||||
import "controls"
|
||||
import "controls-uit" as Controls
|
||||
import "styles"
|
||||
import "styles-uit"
|
||||
|
||||
Windows.Window {
|
||||
id: root
|
||||
|
@ -23,6 +26,8 @@ Windows.Window {
|
|||
property var eventBridge;
|
||||
property var component;
|
||||
property var dynamicContent;
|
||||
|
||||
|
||||
onSourceChanged: {
|
||||
if (dynamicContent) {
|
||||
dynamicContent.destroy();
|
||||
|
|
|
@ -18,6 +18,7 @@ import "." as VrControls
|
|||
|
||||
FocusScope {
|
||||
id: root
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
property alias model: comboBox.model;
|
||||
property alias comboBox: comboBox
|
||||
|
|
|
@ -13,6 +13,7 @@ import QtQuick 2.5
|
|||
import "../styles-uit"
|
||||
|
||||
RalewaySemiBold {
|
||||
HifiConstants { id: hifi }
|
||||
property int colorScheme: hifi.colorSchemes.light
|
||||
|
||||
size: hifi.fontSizes.inputLabel
|
||||
|
|
|
@ -25,6 +25,8 @@ WebEngineView {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
|
||||
Timer {
|
||||
id: urlReplacementTimer
|
||||
|
@ -59,11 +61,6 @@ WebEngineView {
|
|||
}
|
||||
}
|
||||
|
||||
onNewViewRequested:{
|
||||
var component = Qt.createComponent("../Browser.qml");
|
||||
var newWindow = component.createObject(desktop);
|
||||
request.openIn(newWindow.webView)
|
||||
}
|
||||
|
||||
// This breaks the webchannel used for passing messages. Fixed in Qt 5.6
|
||||
// See https://bugreports.qt.io/browse/QTBUG-49521
|
||||
|
|
|
@ -55,9 +55,11 @@ WebEngineView {
|
|||
}
|
||||
|
||||
onNewViewRequested:{
|
||||
if (desktop) {
|
||||
var component = Qt.createComponent("../Browser.qml");
|
||||
var newWindow = component.createObject(desktop);
|
||||
request.openIn(newWindow.webView)
|
||||
request.openIn(newWindow.webView);
|
||||
}
|
||||
}
|
||||
|
||||
// This breaks the webchannel used for passing messages. Fixed in Qt 5.6
|
||||
|
|
|
@ -20,6 +20,8 @@ OriginalDesktop.Desktop {
|
|||
onEntered: ApplicationCompositor.reticleOverDesktop = true
|
||||
onExited: ApplicationCompositor.reticleOverDesktop = false
|
||||
acceptedButtons: Qt.NoButton
|
||||
|
||||
|
||||
}
|
||||
|
||||
// The tool window, one instance
|
||||
|
@ -71,6 +73,39 @@ OriginalDesktop.Desktop {
|
|||
});
|
||||
}
|
||||
|
||||
// Accept a download through the webview
|
||||
property bool webViewProfileSetup: false
|
||||
property string currentUrl: ""
|
||||
property string adaptedPath: ""
|
||||
property string tempDir: ""
|
||||
|
||||
function initWebviewProfileHandlers(profile) {
|
||||
console.log("The webview url in desktop is: " + currentUrl);
|
||||
if (webViewProfileSetup) return;
|
||||
webViewProfileSetup = true;
|
||||
|
||||
profile.downloadRequested.connect(function(download){
|
||||
console.log("Download start: " + download.state);
|
||||
adaptedPath = File.convertUrlToPath(currentUrl);
|
||||
tempDir = File.getTempDir();
|
||||
console.log("Temp dir created: " + tempDir);
|
||||
download.path = tempDir + "/" + adaptedPath;
|
||||
console.log("Path where object should download: " + download.path);
|
||||
download.accept();
|
||||
if (download.state === WebEngineDownloadItem.DownloadInterrupted) {
|
||||
console.log("download failed to complete");
|
||||
}
|
||||
})
|
||||
|
||||
profile.downloadFinished.connect(function(download){
|
||||
if (download.state === WebEngineDownloadItem.DownloadCompleted) {
|
||||
File.runUnzip(download.path, currentUrl);
|
||||
} else {
|
||||
console.log("The download was corrupted, state: " + download.state);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Create or fetch a toolbar with the given name
|
||||
function getToolbar(name) {
|
||||
var result = toolbars[name];
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include <display-plugins/DisplayPlugin.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
#include <ErrorDialog.h>
|
||||
#include <FileScriptingInterface.h>
|
||||
#include <Finally.h>
|
||||
#include <FramebufferCache.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
@ -1219,7 +1220,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
_defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", true } });
|
||||
|
||||
_defaultSkybox->setCubemap(_defaultSkyboxTexture);
|
||||
_defaultSkybox->setColor({ 1.0, 1.0, 1.0 });
|
||||
|
||||
EntityItem::setEntitiesShouldFadeFunction([this]() {
|
||||
SharedNodePointer entityServerNode = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::EntityServer);
|
||||
|
@ -1578,6 +1578,9 @@ void Application::initializeUi() {
|
|||
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
|
||||
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||
rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
|
||||
FileScriptingInterface* fileDownload = new FileScriptingInterface(engine);
|
||||
rootContext->setContextProperty("File", fileDownload);
|
||||
connect(fileDownload, &FileScriptingInterface::unzipSuccess, this, &Application::showAssetServerWidget);
|
||||
rootContext->setContextProperty("MyAvatar", getMyAvatar());
|
||||
rootContext->setContextProperty("Messages", DependencyManager::get<MessagesClient>().data());
|
||||
rootContext->setContextProperty("Recording", DependencyManager::get<RecordingScriptingInterface>().data());
|
||||
|
@ -2024,7 +2027,6 @@ bool Application::importJSONFromURL(const QString& urlString) {
|
|||
}
|
||||
|
||||
bool Application::importSVOFromURL(const QString& urlString) {
|
||||
|
||||
emit svoImportRequested(urlString);
|
||||
return true;
|
||||
}
|
||||
|
@ -2149,13 +2151,15 @@ bool Application::event(QEvent* event) {
|
|||
// handle custom URL
|
||||
if (event->type() == QEvent::FileOpen) {
|
||||
|
||||
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
|
||||
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
|
||||
|
||||
QUrl url = fileEvent->url();
|
||||
|
||||
if (!url.isEmpty()) {
|
||||
QString urlString = url.toString();
|
||||
|
||||
if (canAcceptURL(urlString)) {
|
||||
|
||||
return acceptURL(urlString);
|
||||
}
|
||||
}
|
||||
|
@ -4322,34 +4326,42 @@ namespace render {
|
|||
sceneKeyLight->setIntensity(DEFAULT_SKYBOX_INTENSITY);
|
||||
sceneKeyLight->setAmbientIntensity(DEFAULT_SKYBOX_AMBIENT_INTENSITY);
|
||||
sceneKeyLight->setDirection(DEFAULT_SKYBOX_DIRECTION);
|
||||
// fall through: render a skybox, if available
|
||||
// fall through: render a skybox (if available), or the defaults (if requested)
|
||||
}
|
||||
|
||||
case model::SunSkyStage::SKY_BOX: {
|
||||
auto skybox = skyStage->getSkybox();
|
||||
if (skybox) {
|
||||
if (!skybox->empty()) {
|
||||
PerformanceTimer perfTimer("skybox");
|
||||
skybox->render(batch, args->getViewFrustum());
|
||||
break;
|
||||
}
|
||||
// fall through: render defaults, if available
|
||||
// fall through: render defaults (if requested)
|
||||
}
|
||||
|
||||
case model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE: {
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) {
|
||||
auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
|
||||
auto sceneKeyLight = scene->getKeyLight();
|
||||
auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture();
|
||||
// do not set the ambient sphere - it peaks too high, and causes flashing when turning
|
||||
// set the ambient sphere uniformly - the defaultSkyboxAmbientTexture has peaks that cause flashing when turning
|
||||
sceneKeyLight->setAmbientSphere(DependencyManager::get<TextureCache>()->getWhiteTexture()->getIrradiance());
|
||||
sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
|
||||
// fall through: render defaults skybox
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
// fall through: render defaults, if available
|
||||
}
|
||||
|
||||
case model::SunSkyStage::SKY_DEFAULT_TEXTURE:
|
||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) {
|
||||
qApp->getDefaultSkybox()->render(batch, args->getViewFrustum());
|
||||
}
|
||||
break;
|
||||
|
||||
// Any other cases require no extra rendering
|
||||
case model::SunSkyStage::NO_BACKGROUND:
|
||||
default:
|
||||
// this line intentionally left blank
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -5021,7 +5033,6 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {
|
|||
}
|
||||
|
||||
bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
|
||||
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(url);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
|
@ -5119,11 +5130,11 @@ void Application::toggleRunningScriptsWidget() const {
|
|||
//}
|
||||
}
|
||||
|
||||
|
||||
void Application::showAssetServerWidget(QString filePath) {
|
||||
if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const QUrl url { "AssetServer.qml" };
|
||||
|
||||
auto startUpload = [=](QQmlContext* context, QObject* newObject){
|
||||
|
|
|
@ -137,7 +137,7 @@ namespace MenuOption {
|
|||
const QString Overlays = "Overlays";
|
||||
const QString PackageModel = "Package Model...";
|
||||
const QString Pair = "Pair";
|
||||
const QString PhysicsShowHulls = "Draw Collision Hulls";
|
||||
const QString PhysicsShowHulls = "Draw Collision Shapes";
|
||||
const QString PhysicsShowOwned = "Highlight Simulation Ownership";
|
||||
const QString PipelineWarnings = "Log Render Pipeline Warnings";
|
||||
const QString Preferences = "General...";
|
||||
|
|
|
@ -59,6 +59,8 @@ const float DISPLAYNAME_ALPHA = 1.0f;
|
|||
const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;
|
||||
const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f);
|
||||
|
||||
const int SENSOR_TO_WORLD_MATRIX_INDEX = 65534;
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {
|
||||
return ItemKey::Builder::opaqueShape();
|
||||
|
@ -851,15 +853,33 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const {
|
|||
}
|
||||
|
||||
glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
|
||||
glm::quat rotation;
|
||||
_skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation);
|
||||
return Quaternions::Y_180 * rotation;
|
||||
if (index == SENSOR_TO_WORLD_MATRIX_INDEX) {
|
||||
glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
|
||||
bool success;
|
||||
Transform avatarTransform;
|
||||
Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
|
||||
glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
|
||||
return glmExtractRotation(invAvatarMat * sensorToWorldMatrix);
|
||||
} else {
|
||||
glm::quat rotation;
|
||||
_skeletonModel->getAbsoluteJointRotationInRigFrame(index, rotation);
|
||||
return Quaternions::Y_180 * rotation;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
|
||||
glm::vec3 translation;
|
||||
_skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation);
|
||||
return Quaternions::Y_180 * translation;
|
||||
if (index == SENSOR_TO_WORLD_MATRIX_INDEX) {
|
||||
glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
|
||||
bool success;
|
||||
Transform avatarTransform;
|
||||
Transform::mult(avatarTransform, getParentTransform(success), getLocalTransform());
|
||||
glm::mat4 invAvatarMat = avatarTransform.getInverseMatrix();
|
||||
return extractTranslation(invAvatarMat * sensorToWorldMatrix);
|
||||
} else {
|
||||
glm::vec3 translation;
|
||||
_skeletonModel->getAbsoluteJointTranslationInRigFrame(index, translation);
|
||||
return Quaternions::Y_180 * translation;
|
||||
}
|
||||
}
|
||||
|
||||
int Avatar::getJointIndex(const QString& name) const {
|
||||
|
|
|
@ -367,7 +367,7 @@ void AvatarManager::addAvatarToSimulation(Avatar* avatar) {
|
|||
|
||||
ShapeInfo shapeInfo;
|
||||
avatar->computeShapeInfo(shapeInfo);
|
||||
btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
// we don't add to the simulation now, we put it on a list to be added later
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "AvatarMotionState.h"
|
||||
#include "BulletUtil.h"
|
||||
|
||||
AvatarMotionState::AvatarMotionState(Avatar* avatar, btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
||||
AvatarMotionState::AvatarMotionState(Avatar* avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) {
|
||||
assert(_avatar);
|
||||
_type = MOTIONSTATE_TYPE_AVATAR;
|
||||
if (_shape) {
|
||||
|
@ -47,7 +47,7 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const {
|
|||
}
|
||||
|
||||
// virtual and protected
|
||||
btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||
const btCollisionShape* AvatarMotionState::computeNewShape() {
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
|
|
|
@ -20,7 +20,7 @@ class Avatar;
|
|||
|
||||
class AvatarMotionState : public ObjectMotionState {
|
||||
public:
|
||||
AvatarMotionState(Avatar* avatar, btCollisionShape* shape);
|
||||
AvatarMotionState(Avatar* avatar, const btCollisionShape* shape);
|
||||
|
||||
virtual PhysicsMotionType getMotionType() const override { return _motionType; }
|
||||
|
||||
|
@ -72,7 +72,7 @@ protected:
|
|||
~AvatarMotionState();
|
||||
|
||||
virtual bool isReadyToComputeShape() const override { return true; }
|
||||
virtual btCollisionShape* computeNewShape() override;
|
||||
virtual const btCollisionShape* computeNewShape() override;
|
||||
|
||||
// The AvatarMotionState keeps a RAW backpointer to its Avatar because all AvatarMotionState
|
||||
// instances are "owned" by their corresponding Avatar instance and are deleted in the Avatar dtor.
|
||||
|
|
|
@ -107,7 +107,6 @@ MyAvatar::MyAvatar(RigPointer rig) :
|
|||
_hmdSensorOrientation(),
|
||||
_hmdSensorPosition(),
|
||||
_bodySensorMatrix(),
|
||||
_sensorToWorldMatrix(),
|
||||
_goToPending(false),
|
||||
_goToPosition(),
|
||||
_goToOrientation(),
|
||||
|
@ -511,13 +510,9 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
updateAvatarEntities();
|
||||
}
|
||||
|
||||
// thread-safe
|
||||
glm::mat4 MyAvatar::getSensorToWorldMatrix() const {
|
||||
return _sensorToWorldMatrixCache.get();
|
||||
}
|
||||
|
||||
// As far as I know no HMD system supports a play area of a kilometer in radius.
|
||||
// As far as I know no HMD system supports a play area of a kilometer in radius.
|
||||
static const float MAX_HMD_ORIGIN_DISTANCE = 1000.0f;
|
||||
|
||||
// Pass a recent sample of the HMD to the avatar.
|
||||
// This can also update the avatar's position to follow the HMD
|
||||
// as it moves through the world.
|
||||
|
@ -526,7 +521,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) {
|
|||
_hmdSensorMatrix = hmdSensorMatrix;
|
||||
auto newHmdSensorPosition = extractTranslation(hmdSensorMatrix);
|
||||
|
||||
if (newHmdSensorPosition != _hmdSensorPosition &&
|
||||
if (newHmdSensorPosition != _hmdSensorPosition &&
|
||||
glm::length(newHmdSensorPosition) > MAX_HMD_ORIGIN_DISTANCE) {
|
||||
qWarning() << "Invalid HMD sensor position " << newHmdSensorPosition;
|
||||
// Ignore unreasonable HMD sensor data
|
||||
|
|
|
@ -79,8 +79,6 @@ class MyAvatar : public Avatar {
|
|||
Q_PROPERTY(controller::Pose leftHandTipPose READ getLeftHandTipPose)
|
||||
Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose)
|
||||
|
||||
Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix)
|
||||
|
||||
Q_PROPERTY(float energy READ getEnergy WRITE setEnergy)
|
||||
|
||||
Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled)
|
||||
|
@ -110,9 +108,6 @@ public:
|
|||
const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; }
|
||||
const glm::vec2& getHMDSensorFacingMovingAverage() const { return _hmdSensorFacingMovingAverage; }
|
||||
|
||||
// thread safe
|
||||
Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const;
|
||||
|
||||
Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar);
|
||||
Q_INVOKABLE QVariant getOrientationVar() const;
|
||||
|
||||
|
@ -415,6 +410,10 @@ private:
|
|||
bool _useSnapTurn { true };
|
||||
bool _clearOverlayWhenMoving { true };
|
||||
|
||||
// working copy of sensorToWorldMatrix.
|
||||
// See AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access
|
||||
glm::mat4 _sensorToWorldMatrix;
|
||||
|
||||
// cache of the current HMD sensor position and orientation
|
||||
// in sensor space.
|
||||
glm::mat4 _hmdSensorMatrix;
|
||||
|
@ -427,10 +426,6 @@ private:
|
|||
// in sensor space.
|
||||
glm::mat4 _bodySensorMatrix;
|
||||
|
||||
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
|
||||
glm::mat4 _sensorToWorldMatrix;
|
||||
ThreadSafeValueCache<glm::mat4> _sensorToWorldMatrixCache { glm::mat4() };
|
||||
|
||||
struct FollowHelper {
|
||||
FollowHelper();
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
static const float DPI = 30.47f;
|
||||
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
|
||||
static float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||
|
||||
QString const Web3DOverlay::TYPE = "web3d";
|
||||
|
||||
|
@ -106,7 +107,11 @@ void Web3DOverlay::render(RenderArgs* args) {
|
|||
|
||||
batch.setModelTransform(transform);
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
geometryCache->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch);
|
||||
if (color.a < OPAQUE_ALPHA_THRESHOLD) {
|
||||
geometryCache->bindTransparentWebBrowserProgram(batch);
|
||||
} else {
|
||||
geometryCache->bindOpaqueWebBrowserProgram(batch);
|
||||
}
|
||||
geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color);
|
||||
batch.setResourceTexture(0, args->_whiteTexture); // restore default white color after me
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
set(TARGET_NAME animation)
|
||||
setup_hifi_library(Network Script)
|
||||
link_hifi_libraries(shared model fbx)
|
||||
|
||||
target_nsight()
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <shared/NsightHelpers.h>
|
||||
|
||||
#include "ElbowConstraint.h"
|
||||
#include "SwingTwistConstraint.h"
|
||||
|
@ -144,9 +145,11 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
accumulator.clearAndClean();
|
||||
}
|
||||
|
||||
float maxError = FLT_MAX;
|
||||
int numLoops = 0;
|
||||
const int MAX_IK_LOOPS = 4;
|
||||
while (numLoops < MAX_IK_LOOPS) {
|
||||
const int MAX_IK_LOOPS = 16;
|
||||
const float MAX_ERROR_TOLERANCE = 0.1f; // cm
|
||||
while (maxError > MAX_ERROR_TOLERANCE && numLoops < MAX_IK_LOOPS) {
|
||||
++numLoops;
|
||||
|
||||
// solve all targets
|
||||
|
@ -173,6 +176,18 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector<I
|
|||
absolutePoses[i] = absolutePoses[parentIndex] * _relativePoses[i];
|
||||
}
|
||||
}
|
||||
|
||||
// compute maxError
|
||||
maxError = 0.0f;
|
||||
for (size_t i = 0; i < targets.size(); i++) {
|
||||
if (targets[i].getType() == IKTarget::Type::RotationAndPosition || targets[i].getType() == IKTarget::Type::HmdHead ||
|
||||
targets[i].getType() == IKTarget::Type::HipsRelativeRotationAndPosition) {
|
||||
float error = glm::length(absolutePoses[targets[i].getIndex()].trans - targets[i].getTranslation());
|
||||
if (error > maxError) {
|
||||
maxError = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finally set the relative rotation of each tip to agree with absolute target rotation
|
||||
|
@ -285,8 +300,8 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
|
|||
const float MIN_ADJUSTMENT_ANGLE = 1.0e-4f;
|
||||
if (angle > MIN_ADJUSTMENT_ANGLE) {
|
||||
// reduce angle by a fraction (for stability)
|
||||
const float fraction = 0.5f;
|
||||
angle *= fraction;
|
||||
const float FRACTION = 0.5f;
|
||||
angle *= FRACTION;
|
||||
deltaRotation = glm::angleAxis(angle, axis);
|
||||
|
||||
// The swing will re-orient the tip but there will tend to be be a non-zero delta between the tip's
|
||||
|
@ -308,7 +323,7 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe
|
|||
glm::vec3 axis = glm::normalize(deltaRotation * leverArm);
|
||||
swingTwistDecomposition(missingRotation, axis, swingPart, twistPart);
|
||||
float dotSign = copysignf(1.0f, twistPart.w);
|
||||
deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * twistPart, fraction)) * deltaRotation;
|
||||
deltaRotation = glm::normalize(glm::lerp(glm::quat(), dotSign * twistPart, FRACTION)) * deltaRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -369,6 +384,7 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
|
|||
|
||||
//virtual
|
||||
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
||||
|
||||
const float MAX_OVERLAY_DT = 1.0f / 30.0f; // what to clamp delta-time to in AnimInverseKinematics::overlay
|
||||
if (dt > MAX_OVERLAY_DT) {
|
||||
dt = MAX_OVERLAY_DT;
|
||||
|
@ -377,6 +393,9 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
if (_relativePoses.size() != underPoses.size()) {
|
||||
loadPoses(underPoses);
|
||||
} else {
|
||||
|
||||
PROFILE_RANGE_EX("ik/relax", 0xffff00ff, 0);
|
||||
|
||||
// relax toward underPoses
|
||||
// HACK: this relaxation needs to be constant per-frame rather than per-realtime
|
||||
// in order to prevent IK "flutter" for bad FPS. The bad news is that the good parts
|
||||
|
@ -410,9 +429,13 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
}
|
||||
|
||||
if (!_relativePoses.empty()) {
|
||||
|
||||
// build a list of targets from _targetVarVec
|
||||
std::vector<IKTarget> targets;
|
||||
computeTargets(animVars, targets, underPoses);
|
||||
{
|
||||
PROFILE_RANGE_EX("ik/computeTargets", 0xffff00ff, 0);
|
||||
computeTargets(animVars, targets, underPoses);
|
||||
}
|
||||
|
||||
if (targets.empty()) {
|
||||
// no IK targets but still need to enforce constraints
|
||||
|
@ -425,64 +448,76 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars
|
|||
++constraintItr;
|
||||
}
|
||||
} else {
|
||||
// shift hips according to the _hipsOffset from the previous frame
|
||||
float offsetLength = glm::length(_hipsOffset);
|
||||
const float MIN_HIPS_OFFSET_LENGTH = 0.03f;
|
||||
if (offsetLength > MIN_HIPS_OFFSET_LENGTH && _hipsIndex >= 0) {
|
||||
// but only if offset is long enough
|
||||
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
|
||||
if (_hipsParentIndex == -1) {
|
||||
// the hips are the root so _hipsOffset is in the correct frame
|
||||
_relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans + scaleFactor * _hipsOffset;
|
||||
} else {
|
||||
// the hips are NOT the root so we need to transform _hipsOffset into hips local-frame
|
||||
glm::quat hipsFrameRotation = _relativePoses[_hipsParentIndex].rot;
|
||||
int index = _skeleton->getParentIndex(_hipsParentIndex);
|
||||
while (index != -1) {
|
||||
hipsFrameRotation *= _relativePoses[index].rot;
|
||||
index = _skeleton->getParentIndex(index);
|
||||
|
||||
{
|
||||
PROFILE_RANGE_EX("ik/shiftHips", 0xffff00ff, 0);
|
||||
|
||||
// shift hips according to the _hipsOffset from the previous frame
|
||||
float offsetLength = glm::length(_hipsOffset);
|
||||
const float MIN_HIPS_OFFSET_LENGTH = 0.03f;
|
||||
if (offsetLength > MIN_HIPS_OFFSET_LENGTH && _hipsIndex >= 0) {
|
||||
// but only if offset is long enough
|
||||
float scaleFactor = ((offsetLength - MIN_HIPS_OFFSET_LENGTH) / offsetLength);
|
||||
if (_hipsParentIndex == -1) {
|
||||
// the hips are the root so _hipsOffset is in the correct frame
|
||||
_relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans + scaleFactor * _hipsOffset;
|
||||
} else {
|
||||
// the hips are NOT the root so we need to transform _hipsOffset into hips local-frame
|
||||
glm::quat hipsFrameRotation = _relativePoses[_hipsParentIndex].rot;
|
||||
int index = _skeleton->getParentIndex(_hipsParentIndex);
|
||||
while (index != -1) {
|
||||
hipsFrameRotation *= _relativePoses[index].rot;
|
||||
index = _skeleton->getParentIndex(index);
|
||||
}
|
||||
_relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans
|
||||
+ glm::inverse(glm::normalize(hipsFrameRotation)) * (scaleFactor * _hipsOffset);
|
||||
}
|
||||
_relativePoses[_hipsIndex].trans = underPoses[_hipsIndex].trans
|
||||
+ glm::inverse(glm::normalize(hipsFrameRotation)) * (scaleFactor * _hipsOffset);
|
||||
}
|
||||
}
|
||||
|
||||
solveWithCyclicCoordinateDescent(targets);
|
||||
|
||||
// measure new _hipsOffset for next frame
|
||||
// by looking for discrepancies between where a targeted endEffector is
|
||||
// and where it wants to be (after IK solutions are done)
|
||||
glm::vec3 newHipsOffset = Vectors::ZERO;
|
||||
for (auto& target: targets) {
|
||||
int targetIndex = target.getIndex();
|
||||
if (targetIndex == _headIndex && _headIndex != -1) {
|
||||
// special handling for headTarget
|
||||
if (target.getType() == IKTarget::Type::RotationOnly) {
|
||||
// we want to shift the hips to bring the underPose closer
|
||||
// to where the head happens to be (overpose)
|
||||
glm::vec3 under = _skeleton->getAbsolutePose(_headIndex, underPoses).trans;
|
||||
glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans;
|
||||
const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f;
|
||||
newHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * (actual - under);
|
||||
} else if (target.getType() == IKTarget::Type::HmdHead) {
|
||||
// we want to shift the hips to bring the head to its designated position
|
||||
glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans;
|
||||
_hipsOffset += target.getTranslation() - actual;
|
||||
// and ignore all other targets
|
||||
newHipsOffset = _hipsOffset;
|
||||
break;
|
||||
}
|
||||
} else if (target.getType() == IKTarget::Type::RotationAndPosition) {
|
||||
glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans;
|
||||
glm::vec3 targetPosition = target.getTranslation();
|
||||
newHipsOffset += targetPosition - actualPosition;
|
||||
}
|
||||
{
|
||||
PROFILE_RANGE_EX("ik/ccd", 0xffff00ff, 0);
|
||||
solveWithCyclicCoordinateDescent(targets);
|
||||
}
|
||||
|
||||
// smooth transitions by relaxing _hipsOffset toward the new value
|
||||
const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f;
|
||||
float tau = dt < HIPS_OFFSET_SLAVE_TIMESCALE ? dt / HIPS_OFFSET_SLAVE_TIMESCALE : 1.0f;
|
||||
_hipsOffset += (newHipsOffset - _hipsOffset) * tau;
|
||||
{
|
||||
PROFILE_RANGE_EX("ik/measureHipsOffset", 0xffff00ff, 0);
|
||||
|
||||
// measure new _hipsOffset for next frame
|
||||
// by looking for discrepancies between where a targeted endEffector is
|
||||
// and where it wants to be (after IK solutions are done)
|
||||
glm::vec3 newHipsOffset = Vectors::ZERO;
|
||||
for (auto& target: targets) {
|
||||
int targetIndex = target.getIndex();
|
||||
if (targetIndex == _headIndex && _headIndex != -1) {
|
||||
// special handling for headTarget
|
||||
if (target.getType() == IKTarget::Type::RotationOnly) {
|
||||
// we want to shift the hips to bring the underPose closer
|
||||
// to where the head happens to be (overpose)
|
||||
glm::vec3 under = _skeleton->getAbsolutePose(_headIndex, underPoses).trans;
|
||||
glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans;
|
||||
const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f;
|
||||
newHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * (actual - under);
|
||||
} else if (target.getType() == IKTarget::Type::HmdHead) {
|
||||
// we want to shift the hips to bring the head to its designated position
|
||||
glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans;
|
||||
_hipsOffset += target.getTranslation() - actual;
|
||||
// and ignore all other targets
|
||||
newHipsOffset = _hipsOffset;
|
||||
break;
|
||||
}
|
||||
} else if (target.getType() == IKTarget::Type::RotationAndPosition) {
|
||||
glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans;
|
||||
glm::vec3 targetPosition = target.getTranslation();
|
||||
newHipsOffset += targetPosition - actualPosition;
|
||||
}
|
||||
}
|
||||
|
||||
// smooth transitions by relaxing _hipsOffset toward the new value
|
||||
const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f;
|
||||
float tau = dt < HIPS_OFFSET_SLAVE_TIMESCALE ? dt / HIPS_OFFSET_SLAVE_TIMESCALE : 1.0f;
|
||||
_hipsOffset += (newHipsOffset - _hipsOffset) * tau;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _relativePoses;
|
||||
|
|
|
@ -53,15 +53,18 @@ namespace AvatarDataPacket {
|
|||
// NOTE: AvatarDataPackets start with a uint16_t sequence number that is not reflected in the Header structure.
|
||||
|
||||
PACKED_BEGIN struct Header {
|
||||
float position[3]; // skeletal model's position
|
||||
float globalPosition[3]; // avatar's position
|
||||
uint16_t localOrientation[3]; // avatar's local euler angles (degrees, compressed) relative to the thing it's attached to
|
||||
uint16_t scale; // (compressed) 'ratio' encoding uses sign bit as flag.
|
||||
float lookAtPosition[3]; // world space position that eyes are focusing on.
|
||||
float audioLoudness; // current loundess of microphone
|
||||
float position[3]; // skeletal model's position
|
||||
float globalPosition[3]; // avatar's position
|
||||
uint16_t localOrientation[3]; // avatar's local euler angles (degrees, compressed) relative to the thing it's attached to
|
||||
uint16_t scale; // (compressed) 'ratio' encoding uses sign bit as flag.
|
||||
float lookAtPosition[3]; // world space position that eyes are focusing on.
|
||||
float audioLoudness; // current loundess of microphone
|
||||
uint8_t sensorToWorldQuat[6]; // 6 byte compressed quaternion part of sensor to world matrix
|
||||
uint16_t sensorToWorldScale; // uniform scale of sensor to world matrix
|
||||
float sensorToWorldTrans[3]; // fourth column of sensor to world matrix
|
||||
uint8_t flags;
|
||||
} PACKED_END;
|
||||
const size_t HEADER_SIZE = 49;
|
||||
const size_t HEADER_SIZE = 69;
|
||||
|
||||
// only present if HAS_REFERENTIAL flag is set in header.flags
|
||||
PACKED_BEGIN struct ParentInfo {
|
||||
|
@ -93,6 +96,9 @@ namespace AvatarDataPacket {
|
|||
*/
|
||||
}
|
||||
|
||||
static const int TRANSLATION_COMPRESSION_RADIX = 12;
|
||||
static const int SENSOR_TO_WORLD_SCALE_RADIX = 10;
|
||||
|
||||
#define ASSERT(COND) do { if (!(COND)) { abort(); } } while(0)
|
||||
|
||||
AvatarData::AvatarData() :
|
||||
|
@ -210,6 +216,14 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
|||
header->lookAtPosition[2] = _headData->_lookAtPosition.z;
|
||||
header->audioLoudness = _headData->_audioLoudness;
|
||||
|
||||
glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
|
||||
packOrientationQuatToSixBytes(header->sensorToWorldQuat, glmExtractRotation(sensorToWorldMatrix));
|
||||
glm::vec3 scale = extractScale(sensorToWorldMatrix);
|
||||
packFloatScalarToSignedTwoByteFixed((uint8_t*)&header->sensorToWorldScale, scale.x, SENSOR_TO_WORLD_SCALE_RADIX);
|
||||
header->sensorToWorldTrans[0] = sensorToWorldMatrix[3][0];
|
||||
header->sensorToWorldTrans[1] = sensorToWorldMatrix[3][1];
|
||||
header->sensorToWorldTrans[2] = sensorToWorldMatrix[3][2];
|
||||
|
||||
setSemiNibbleAt(header->flags, KEY_STATE_START_BIT, _keyState);
|
||||
// hand state
|
||||
bool isFingerPointing = _handState & IS_FINGER_POINTING_FLAG;
|
||||
|
@ -346,8 +360,6 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
|
|||
*destinationBuffer++ = validity;
|
||||
}
|
||||
|
||||
const int TRANSLATION_COMPRESSION_RADIX = 12;
|
||||
|
||||
validityBit = 0;
|
||||
validity = *validityPosition++;
|
||||
for (int i = 0; i < _jointData.size(); i ++) {
|
||||
|
@ -500,6 +512,15 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
}
|
||||
_headData->_audioLoudness = audioLoudness;
|
||||
|
||||
glm::quat sensorToWorldQuat;
|
||||
unpackOrientationQuatFromSixBytes(header->sensorToWorldQuat, sensorToWorldQuat);
|
||||
float sensorToWorldScale;
|
||||
unpackFloatScalarFromSignedTwoByteFixed((int16_t*)&header->sensorToWorldScale, &sensorToWorldScale, SENSOR_TO_WORLD_SCALE_RADIX);
|
||||
glm::vec3 sensorToWorldTrans(header->sensorToWorldTrans[0], header->sensorToWorldTrans[1], header->sensorToWorldTrans[2]);
|
||||
glm::mat4 sensorToWorldMatrix = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), sensorToWorldQuat, sensorToWorldTrans);
|
||||
|
||||
_sensorToWorldMatrixCache.set(sensorToWorldMatrix);
|
||||
|
||||
{ // bitFlags and face data
|
||||
uint8_t bitItems = header->flags;
|
||||
|
||||
|
@ -616,7 +637,6 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
|||
// each joint translation component is stored in 6 bytes.
|
||||
const int COMPRESSED_TRANSLATION_SIZE = 6;
|
||||
PACKET_READ_CHECK(JointTranslation, numValidJointTranslations * COMPRESSED_TRANSLATION_SIZE);
|
||||
const int TRANSLATION_COMPRESSION_RADIX = 12;
|
||||
|
||||
for (int i = 0; i < numJoints; i++) {
|
||||
JointData& data = _jointData[i];
|
||||
|
@ -1718,6 +1738,11 @@ AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() {
|
|||
return result;
|
||||
}
|
||||
|
||||
// thread-safe
|
||||
glm::mat4 AvatarData::getSensorToWorldMatrix() const {
|
||||
return _sensorToWorldMatrixCache.get();
|
||||
}
|
||||
|
||||
QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& value) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("intersects", value.intersects);
|
||||
|
|
|
@ -54,6 +54,7 @@ typedef unsigned long long quint64;
|
|||
#include <SpatiallyNestable.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <Packed.h>
|
||||
#include <ThreadSafeValueCache.h>
|
||||
|
||||
#include "AABox.h"
|
||||
#include "HeadData.h"
|
||||
|
@ -171,6 +172,8 @@ class AvatarData : public QObject, public SpatiallyNestable {
|
|||
|
||||
Q_PROPERTY(QUuid sessionUUID READ getSessionUUID)
|
||||
|
||||
Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix)
|
||||
|
||||
public:
|
||||
|
||||
static const QString FRAME_NAME;
|
||||
|
@ -351,6 +354,9 @@ public:
|
|||
void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; }
|
||||
AvatarEntityIDs getAndClearRecentlyDetachedIDs();
|
||||
|
||||
// thread safe
|
||||
Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const;
|
||||
|
||||
public slots:
|
||||
void sendAvatarDataPacket();
|
||||
void sendIdentityPacket();
|
||||
|
@ -425,6 +431,9 @@ protected:
|
|||
bool _avatarEntityDataLocallyEdited { false };
|
||||
bool _avatarEntityDataChanged { false };
|
||||
|
||||
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
|
||||
ThreadSafeValueCache<glm::mat4> _sensorToWorldMatrixCache { glm::mat4() };
|
||||
|
||||
private:
|
||||
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);
|
||||
static QUrl _defaultFullAvatarModelUrl;
|
||||
|
|
|
@ -113,17 +113,19 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() {
|
|||
void EntityTreeRenderer::clear() {
|
||||
leaveAllEntities();
|
||||
|
||||
// unload and stop the engine
|
||||
if (_entitiesScriptEngine) {
|
||||
// Unload and stop the engine here (instead of in its deleter) to
|
||||
// avoid marshalling unload signals back to this thread
|
||||
// do this here (instead of in deleter) to avoid marshalling unload signals back to this thread
|
||||
_entitiesScriptEngine->unloadAllEntityScripts();
|
||||
_entitiesScriptEngine->stop();
|
||||
}
|
||||
|
||||
// reset the engine
|
||||
if (_wantScripts && !_shuttingDown) {
|
||||
resetEntitiesScriptEngine();
|
||||
}
|
||||
|
||||
// remove all entities from the scene
|
||||
auto scene = _viewState->getMain3DScene();
|
||||
render::PendingChanges pendingChanges;
|
||||
foreach(auto entity, _entitiesInScene) {
|
||||
|
@ -132,6 +134,10 @@ void EntityTreeRenderer::clear() {
|
|||
scene->enqueuePendingChanges(pendingChanges);
|
||||
_entitiesInScene.clear();
|
||||
|
||||
// reset the zone to the default (while we load the next scene)
|
||||
_bestZone = nullptr;
|
||||
applyZonePropertiesToScene(_bestZone);
|
||||
|
||||
OctreeRenderer::clear();
|
||||
}
|
||||
|
||||
|
@ -499,35 +505,18 @@ ModelPointer EntityTreeRenderer::getModelForEntityItem(EntityItemPointer entityI
|
|||
return result;
|
||||
}
|
||||
|
||||
const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(EntityItemPointer entityItem) {
|
||||
const FBXGeometry* result = NULL;
|
||||
|
||||
if (entityItem->getType() == EntityTypes::Model) {
|
||||
std::shared_ptr<RenderableModelEntityItem> modelEntityItem =
|
||||
std::dynamic_pointer_cast<RenderableModelEntityItem>(entityItem);
|
||||
if (modelEntityItem->hasCompoundShapeURL()) {
|
||||
ModelPointer model = modelEntityItem->getModel(this);
|
||||
if (model && model->isCollisionLoaded()) {
|
||||
result = &model->getCollisionFBXGeometry();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
||||
std::static_pointer_cast<EntityTree>(_tree)->processEraseMessage(message, sourceNode);
|
||||
}
|
||||
|
||||
ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority) {
|
||||
ModelPointer EntityTreeRenderer::allocateModel(const QString& url, float loadingPriority) {
|
||||
ModelPointer model = nullptr;
|
||||
|
||||
// Only create and delete models on the thread that owns the EntityTreeRenderer
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "allocateModel", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(ModelPointer, model),
|
||||
Q_ARG(const QString&, url),
|
||||
Q_ARG(const QString&, collisionUrl));
|
||||
Q_ARG(const QString&, url));
|
||||
|
||||
return model;
|
||||
}
|
||||
|
@ -536,7 +525,6 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString
|
|||
model->setLoadingPriority(loadingPriority);
|
||||
model->init();
|
||||
model->setURL(QUrl(url));
|
||||
model->setCollisionModelURL(QUrl(collisionUrl));
|
||||
return model;
|
||||
}
|
||||
|
||||
|
@ -553,7 +541,6 @@ ModelPointer EntityTreeRenderer::updateModel(ModelPointer model, const QString&
|
|||
}
|
||||
|
||||
model->setURL(QUrl(newUrl));
|
||||
model->setCollisionModelURL(QUrl(collisionUrl));
|
||||
return model;
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,6 @@ public:
|
|||
|
||||
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) override;
|
||||
virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) override;
|
||||
virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) override;
|
||||
|
||||
/// clears the tree
|
||||
virtual void clear() override;
|
||||
|
@ -74,7 +73,7 @@ public:
|
|||
void reloadEntityScripts();
|
||||
|
||||
/// if a renderable entity item needs a model, we will allocate it for them
|
||||
Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority = 0.0f);
|
||||
Q_INVOKABLE ModelPointer allocateModel(const QString& url, float loadingPriority = 0.0f);
|
||||
|
||||
/// if a renderable entity item needs to update the URL of a model, we will handle that for the entity
|
||||
Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <AbstractViewStateInterface.h>
|
||||
#include <CollisionRenderMeshCache.h>
|
||||
#include <Model.h>
|
||||
#include <PerfStat.h>
|
||||
#include <render/Scene.h>
|
||||
|
@ -28,6 +29,9 @@
|
|||
#include "RenderableModelEntityItem.h"
|
||||
#include "RenderableEntityItem.h"
|
||||
|
||||
static CollisionRenderMeshCache collisionMeshCache;
|
||||
|
||||
|
||||
EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer entity{ new RenderableModelEntityItem(entityID, properties.getDimensionsInitialized()) };
|
||||
entity->setProperties(properties);
|
||||
|
@ -214,21 +218,21 @@ namespace render {
|
|||
bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges) {
|
||||
_myMetaItem = scene->allocateID();
|
||||
|
||||
|
||||
auto renderData = std::make_shared<RenderableModelEntityItemMeta>(self);
|
||||
auto renderPayload = std::make_shared<RenderableModelEntityItemMeta::Payload>(renderData);
|
||||
|
||||
|
||||
pendingChanges.resetItem(_myMetaItem, renderPayload);
|
||||
|
||||
|
||||
if (_model) {
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
|
||||
|
||||
// note: we don't care if the model fails to add items, we always added our meta item and therefore we return
|
||||
// true so that the system knows our meta item is in the scene!
|
||||
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
|
||||
|
||||
// note: we don't mind if the model fails to add, we'll retry (in render()) until it succeeds
|
||||
_model->addToScene(scene, pendingChanges, statusGetters);
|
||||
}
|
||||
|
||||
// we've successfully added _myMetaItem so we always return true
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -415,19 +419,35 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
|
|||
// Remap textures for the next frame to avoid flicker
|
||||
remapTextures();
|
||||
|
||||
// check to see if when we added our models to the scene they were ready, if they were not ready, then
|
||||
// fix them up in the scene
|
||||
bool shouldShowCollisionHull = (args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS) > 0
|
||||
&& getShapeType() == SHAPE_TYPE_COMPOUND;
|
||||
if (_model->needsFixupInScene() || _showCollisionHull != shouldShowCollisionHull) {
|
||||
_showCollisionHull = shouldShowCollisionHull;
|
||||
// update whether the model should be showing collision mesh (this may flag for fixupInScene)
|
||||
bool showingCollisionGeometry = (bool)(args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS);
|
||||
if (showingCollisionGeometry != _showCollisionGeometry) {
|
||||
ShapeType type = getShapeType();
|
||||
_showCollisionGeometry = showingCollisionGeometry;
|
||||
if (_showCollisionGeometry && type != SHAPE_TYPE_STATIC_MESH && type != SHAPE_TYPE_NONE) {
|
||||
// NOTE: it is OK if _collisionMeshKey is nullptr
|
||||
model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey);
|
||||
// NOTE: the model will render the collisionGeometry if it has one
|
||||
_model->setCollisionMesh(mesh);
|
||||
} else {
|
||||
// release mesh
|
||||
if (_collisionMeshKey) {
|
||||
collisionMeshCache.releaseMesh(_collisionMeshKey);
|
||||
}
|
||||
// clear model's collision geometry
|
||||
model::MeshPointer mesh = nullptr;
|
||||
_model->setCollisionMesh(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
if (_model->needsFixupInScene()) {
|
||||
render::PendingChanges pendingChanges;
|
||||
|
||||
_model->removeFromScene(scene, pendingChanges);
|
||||
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
|
||||
_model->addToScene(scene, pendingChanges, statusGetters, _showCollisionHull);
|
||||
_model->addToScene(scene, pendingChanges, statusGetters);
|
||||
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
|
@ -471,14 +491,13 @@ ModelPointer RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
|
|||
if (!getModelURL().isEmpty()) {
|
||||
// If we don't have a model, allocate one *immediately*
|
||||
if (!_model) {
|
||||
_model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL(), renderer->getEntityLoadingPriority(*this));
|
||||
_model = _myRenderer->allocateModel(getModelURL(), renderer->getEntityLoadingPriority(*this));
|
||||
_needsInitialSimulation = true;
|
||||
// If we need to change URLs, update it *after rendering* (to avoid access violations)
|
||||
} else if ((QUrl(getModelURL()) != _model->getURL() || QUrl(getCompoundShapeURL()) != _model->getCollisionURL())) {
|
||||
} else if (QUrl(getModelURL()) != _model->getURL()) {
|
||||
QMetaObject::invokeMethod(_myRenderer, "updateModel", Qt::QueuedConnection,
|
||||
Q_ARG(ModelPointer, _model),
|
||||
Q_ARG(const QString&, getModelURL()),
|
||||
Q_ARG(const QString&, getCompoundShapeURL()));
|
||||
Q_ARG(const QString&, getModelURL()));
|
||||
_needsInitialSimulation = true;
|
||||
}
|
||||
// Else we can just return the _model
|
||||
|
@ -546,6 +565,18 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
|
|||
face, surfaceNormal, extraInfo, precisionPicking);
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setShapeType(ShapeType type) {
|
||||
ModelEntityItem::setShapeType(type);
|
||||
if (_shapeType == SHAPE_TYPE_COMPOUND) {
|
||||
if (!_compoundShapeResource && !_compoundShapeURL.isEmpty()) {
|
||||
_compoundShapeResource = DependencyManager::get<ModelCache>()->getGeometryResource(getCompoundShapeURL());
|
||||
}
|
||||
} else if (_compoundShapeResource && !_compoundShapeURL.isEmpty()) {
|
||||
// the compoundURL has been set but the shapeType does not agree
|
||||
_compoundShapeResource.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
|
||||
auto currentCompoundShapeURL = getCompoundShapeURL();
|
||||
ModelEntityItem::setCompoundShapeURL(url);
|
||||
|
@ -555,6 +586,9 @@ void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
|
|||
if (tree) {
|
||||
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
|
||||
}
|
||||
if (_shapeType == SHAPE_TYPE_COMPOUND) {
|
||||
_compoundShapeResource = DependencyManager::get<ModelCache>()->getGeometryResource(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -562,7 +596,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
|
|||
ShapeType type = getShapeType();
|
||||
|
||||
if (type == SHAPE_TYPE_COMPOUND) {
|
||||
if (!_model || _model->getCollisionURL().isEmpty()) {
|
||||
if (!_model || _compoundShapeURL.isEmpty()) {
|
||||
EntityTreePointer tree = getTree();
|
||||
if (tree) {
|
||||
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
|
||||
|
@ -575,15 +609,18 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (_model->isLoaded() && _model->isCollisionLoaded()) {
|
||||
// we have both URLs AND both geometries AND they are both fully loaded.
|
||||
if (_needsInitialSimulation) {
|
||||
// the _model's offset will be wrong until _needsInitialSimulation is false
|
||||
PerformanceTimer perfTimer("_model->simulate");
|
||||
doInitialModelSimulation();
|
||||
if (_model->isLoaded()) {
|
||||
if (_compoundShapeResource && _compoundShapeResource->isLoaded()) {
|
||||
// we have both URLs AND both geometries AND they are both fully loaded.
|
||||
if (_needsInitialSimulation) {
|
||||
// the _model's offset will be wrong until _needsInitialSimulation is false
|
||||
PerformanceTimer perfTimer("_model->simulate");
|
||||
doInitialModelSimulation();
|
||||
}
|
||||
return true;
|
||||
} else if (!_compoundShapeURL.isEmpty()) {
|
||||
_compoundShapeResource = DependencyManager::get<ModelCache>()->getGeometryResource(_compoundShapeURL);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// the model is still being downloaded.
|
||||
|
@ -594,7 +631,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
||||
void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||
const uint32_t TRIANGLE_STRIDE = 3;
|
||||
const uint32_t QUAD_STRIDE = 4;
|
||||
|
||||
|
@ -605,10 +642,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
|
||||
// should never fall in here when collision model not fully loaded
|
||||
// hence we assert that all geometries exist and are loaded
|
||||
assert(_model && _model->isLoaded() && _model->isCollisionLoaded());
|
||||
const FBXGeometry& collisionGeometry = _model->getCollisionFBXGeometry();
|
||||
assert(_model && _model->isLoaded() && _compoundShapeResource && _compoundShapeResource->isLoaded());
|
||||
const FBXGeometry& collisionGeometry = _compoundShapeResource->getFBXGeometry();
|
||||
|
||||
ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
|
||||
ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection();
|
||||
pointCollection.clear();
|
||||
uint32_t i = 0;
|
||||
|
||||
|
@ -684,15 +721,14 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
glm::vec3 scaleToFit = dimensions / _model->getFBXGeometry().getUnscaledMeshExtents().size();
|
||||
// multiply each point by scale before handing the point-set off to the physics engine.
|
||||
// also determine the extents of the collision model.
|
||||
glm::vec3 registrationOffset = dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint());
|
||||
for (int32_t i = 0; i < pointCollection.size(); i++) {
|
||||
for (int32_t j = 0; j < pointCollection[i].size(); j++) {
|
||||
// compensate for registration
|
||||
pointCollection[i][j] += _model->getOffset();
|
||||
// scale so the collision points match the model points
|
||||
pointCollection[i][j] *= scaleToFit;
|
||||
// back compensate for registration so we can apply that offset to the shapeInfo later
|
||||
pointCollection[i][j] = scaleToFit * (pointCollection[i][j] + _model->getOffset()) - registrationOffset;
|
||||
}
|
||||
}
|
||||
info.setParams(type, dimensions, _compoundShapeURL);
|
||||
shapeInfo.setParams(type, dimensions, _compoundShapeURL);
|
||||
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
|
||||
// should never fall in here when model not fully loaded
|
||||
assert(_model && _model->isLoaded());
|
||||
|
@ -705,29 +741,31 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
const FBXGeometry& fbxGeometry = _model->getFBXGeometry();
|
||||
int numFbxMeshes = fbxGeometry.meshes.size();
|
||||
int totalNumVertices = 0;
|
||||
glm::mat4 invRegistraionOffset = glm::translate(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT));
|
||||
for (int i = 0; i < numFbxMeshes; i++) {
|
||||
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
|
||||
if (mesh.clusters.size() > 0) {
|
||||
const FBXCluster& cluster = mesh.clusters.at(0);
|
||||
auto jointMatrix = _model->getRig()->getJointTransform(cluster.jointIndex);
|
||||
localTransforms.push_back(jointMatrix * cluster.inverseBindMatrix);
|
||||
// we backtranslate by the registration offset so we can apply that offset to the shapeInfo later
|
||||
localTransforms.push_back(invRegistraionOffset * jointMatrix * cluster.inverseBindMatrix);
|
||||
} else {
|
||||
glm::mat4 identity;
|
||||
localTransforms.push_back(identity);
|
||||
localTransforms.push_back(invRegistraionOffset);
|
||||
}
|
||||
totalNumVertices += mesh.vertices.size();
|
||||
}
|
||||
const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6;
|
||||
if (totalNumVertices > MAX_VERTICES_PER_STATIC_MESH) {
|
||||
qWarning() << "model" << getModelURL() << "has too many vertices" << totalNumVertices << "and will collide as a box.";
|
||||
info.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions);
|
||||
shapeInfo.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& meshes = _model->getGeometry()->getMeshes();
|
||||
int32_t numMeshes = (int32_t)(meshes.size());
|
||||
|
||||
ShapeInfo::PointCollection& pointCollection = info.getPointCollection();
|
||||
ShapeInfo::PointCollection& pointCollection = shapeInfo.getPointCollection();
|
||||
pointCollection.clear();
|
||||
if (type == SHAPE_TYPE_SIMPLE_COMPOUND) {
|
||||
pointCollection.resize(numMeshes);
|
||||
|
@ -735,7 +773,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
pointCollection.resize(1);
|
||||
}
|
||||
|
||||
ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices();
|
||||
ShapeInfo::TriangleIndices& triangleIndices = shapeInfo.getTriangleIndices();
|
||||
triangleIndices.clear();
|
||||
|
||||
Extents extents;
|
||||
|
@ -909,17 +947,30 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
}
|
||||
}
|
||||
|
||||
info.setParams(type, 0.5f * dimensions, _modelURL);
|
||||
shapeInfo.setParams(type, 0.5f * dimensions, _modelURL);
|
||||
} else {
|
||||
ModelEntityItem::computeShapeInfo(info);
|
||||
info.setParams(type, 0.5f * dimensions);
|
||||
adjustShapeInfoByRegistration(info);
|
||||
ModelEntityItem::computeShapeInfo(shapeInfo);
|
||||
shapeInfo.setParams(type, 0.5f * dimensions);
|
||||
}
|
||||
// finally apply the registration offset to the shapeInfo
|
||||
adjustShapeInfoByRegistration(shapeInfo);
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::setCollisionShape(const btCollisionShape* shape) {
|
||||
const void* key = static_cast<const void*>(shape);
|
||||
if (_collisionMeshKey != key) {
|
||||
if (_collisionMeshKey) {
|
||||
collisionMeshCache.releaseMesh(_collisionMeshKey);
|
||||
}
|
||||
_collisionMeshKey = key;
|
||||
// toggle _showCollisionGeometry forces re-evaluation later
|
||||
_showCollisionGeometry = !_showCollisionGeometry;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::contains(const glm::vec3& point) const {
|
||||
if (EntityItem::contains(point) && _model && _model->isCollisionLoaded()) {
|
||||
return _model->getCollisionFBXGeometry().convexHullContains(worldToEntity(point));
|
||||
if (EntityItem::contains(point) && _model && _compoundShapeResource && _compoundShapeResource->isLoaded()) {
|
||||
return _compoundShapeResource->getFBXGeometry().convexHullContains(worldToEntity(point));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -56,10 +56,13 @@ public:
|
|||
virtual bool needsToCallUpdate() const override;
|
||||
virtual void update(const quint64& now) override;
|
||||
|
||||
virtual void setShapeType(ShapeType type) override;
|
||||
virtual void setCompoundShapeURL(const QString& url) override;
|
||||
|
||||
virtual bool isReadyToComputeShape() override;
|
||||
virtual void computeShapeInfo(ShapeInfo& info) override;
|
||||
virtual void computeShapeInfo(ShapeInfo& shapeInfo) override;
|
||||
|
||||
void setCollisionShape(const btCollisionShape* shape) override;
|
||||
|
||||
virtual bool contains(const glm::vec3& point) const override;
|
||||
|
||||
|
@ -98,6 +101,7 @@ private:
|
|||
QVariantMap parseTexturesToMap(QString textures);
|
||||
void remapTextures();
|
||||
|
||||
GeometryResource::Pointer _compoundShapeResource;
|
||||
ModelPointer _model = nullptr;
|
||||
bool _needsInitialSimulation = true;
|
||||
bool _needsModelReload = true;
|
||||
|
@ -112,11 +116,11 @@ private:
|
|||
|
||||
render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID };
|
||||
|
||||
bool _showCollisionHull = false;
|
||||
|
||||
bool getAnimationFrame();
|
||||
|
||||
bool _needsJointSimulation { false };
|
||||
bool _showCollisionGeometry { false };
|
||||
const void* _collisionMeshKey { nullptr };
|
||||
};
|
||||
|
||||
#endif // hifi_RenderableModelEntityItem_h
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <QMouseEvent>
|
||||
#include <QQuickItem>
|
||||
#include <QQuickWindow>
|
||||
#include <QQmlContext>
|
||||
#include <QOpenGLContext>
|
||||
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
@ -26,7 +27,6 @@
|
|||
|
||||
#include "EntityTreeRenderer.h"
|
||||
|
||||
const float DPI = 30.47f;
|
||||
const float METERS_TO_INCHES = 39.3701f;
|
||||
static uint32_t _currentWebCount { 0 };
|
||||
// Don't allow more than 100 concurrent web views
|
||||
|
@ -34,6 +34,9 @@ static const uint32_t MAX_CONCURRENT_WEB_VIEWS = 100;
|
|||
// If a web-view hasn't been rendered for 30 seconds, de-allocate the framebuffer
|
||||
static uint64_t MAX_NO_RENDER_INTERVAL = 30 * USECS_PER_SECOND;
|
||||
|
||||
static int MAX_WINDOW_SIZE = 4096;
|
||||
static float OPAQUE_ALPHA_THRESHOLD = 0.99f;
|
||||
|
||||
EntityItemPointer RenderableWebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
EntityItemPointer entity{ new RenderableWebEntityItem(entityID) };
|
||||
entity->setProperties(properties);
|
||||
|
@ -67,6 +70,7 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
|||
_webSurface->load("WebView.qml");
|
||||
_webSurface->resume();
|
||||
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||
_webSurface->getRootContext()->setContextProperty("desktop", QVariant());
|
||||
_connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) {
|
||||
_texture = textureId;
|
||||
});
|
||||
|
@ -87,7 +91,7 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
|||
QTouchEvent::TouchPoint point;
|
||||
point.setId(event.getID());
|
||||
point.setState(Qt::TouchPointReleased);
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI);
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||
point.setPos(windowPoint);
|
||||
QList<QTouchEvent::TouchPoint> touchPoints;
|
||||
|
@ -99,6 +103,19 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) {
|
|||
return true;
|
||||
}
|
||||
|
||||
glm::vec2 RenderableWebEntityItem::getWindowSize() const {
|
||||
glm::vec2 dims = glm::vec2(getDimensions());
|
||||
dims *= METERS_TO_INCHES * _dpi;
|
||||
|
||||
// ensure no side is never larger then MAX_WINDOW_SIZE
|
||||
float max = (dims.x > dims.y) ? dims.x : dims.y;
|
||||
if (max > MAX_WINDOW_SIZE) {
|
||||
dims *= MAX_WINDOW_SIZE / max;
|
||||
}
|
||||
|
||||
return dims;
|
||||
}
|
||||
|
||||
void RenderableWebEntityItem::render(RenderArgs* args) {
|
||||
checkFading();
|
||||
|
||||
|
@ -124,12 +141,13 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
|||
}
|
||||
|
||||
_lastRenderTime = usecTimestampNow();
|
||||
glm::vec2 dims = glm::vec2(getDimensions());
|
||||
dims *= METERS_TO_INCHES * DPI;
|
||||
|
||||
glm::vec2 windowSize = getWindowSize();
|
||||
|
||||
// The offscreen surface is idempotent for resizes (bails early
|
||||
// if it's a no-op), so it's safe to just call resize every frame
|
||||
// if it's a no-op), so it's safe to just call resize every frame
|
||||
// without worrying about excessive overhead.
|
||||
_webSurface->resize(QSize(dims.x, dims.y));
|
||||
_webSurface->resize(QSize(windowSize.x, windowSize.y));
|
||||
|
||||
PerformanceTimer perfTimer("RenderableWebEntityItem::render");
|
||||
Q_ASSERT(getType() == EntityTypes::Web);
|
||||
|
@ -147,9 +165,14 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
|||
}
|
||||
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
|
||||
batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch);
|
||||
if (fadeRatio < OPAQUE_ALPHA_THRESHOLD) {
|
||||
DependencyManager::get<GeometryCache>()->bindTransparentWebBrowserProgram(batch);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->bindOpaqueWebBrowserProgram(batch);
|
||||
}
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio));
|
||||
}
|
||||
|
||||
|
@ -185,7 +208,7 @@ void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) {
|
|||
return;
|
||||
}
|
||||
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI);
|
||||
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
|
||||
QPointF windowPoint(windowPos.x, windowPos.y);
|
||||
|
||||
if (event.getType() == PointerEvent::Move) {
|
||||
|
@ -274,3 +297,9 @@ void RenderableWebEntityItem::update(const quint64& now) {
|
|||
destroyWebSurface();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool RenderableWebEntityItem::isTransparent() {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD;
|
||||
}
|
||||
|
|
|
@ -44,9 +44,12 @@ public:
|
|||
|
||||
SIMPLE_RENDERABLE();
|
||||
|
||||
virtual bool isTransparent() override;
|
||||
|
||||
private:
|
||||
bool buildWebSurface(EntityTreeRenderer* renderer);
|
||||
void destroyWebSurface();
|
||||
glm::vec2 getWindowSize() const;
|
||||
|
||||
OffscreenQmlSurface* _webSurface{ nullptr };
|
||||
QMetaObject::Connection _connection;
|
||||
|
|
|
@ -122,7 +122,7 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
|
|||
_model->removeFromScene(scene, pendingChanges);
|
||||
render::Item::Status::Getters statusGetters;
|
||||
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
|
||||
_model->addToScene(scene, pendingChanges, false);
|
||||
_model->addToScene(scene, pendingChanges);
|
||||
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
|
||||
|
|
|
@ -2213,4 +2213,4 @@ void EntityItem::globalizeProperties(EntityItemProperties& properties, const QSt
|
|||
}
|
||||
QUuid empty;
|
||||
properties.setParentID(empty);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ class EntityTreeElementExtraEncodeData;
|
|||
class EntityActionInterface;
|
||||
class EntityItemProperties;
|
||||
class EntityTree;
|
||||
class btCollisionShape;
|
||||
typedef std::shared_ptr<EntityTree> EntityTreePointer;
|
||||
typedef std::shared_ptr<EntityActionInterface> EntityActionPointer;
|
||||
typedef std::shared_ptr<EntityTreeElement> EntityTreeElementPointer;
|
||||
|
@ -324,6 +325,8 @@ public:
|
|||
/// return preferred shape type (actual physical shape may differ)
|
||||
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
|
||||
|
||||
virtual void setCollisionShape(const btCollisionShape* shape) {}
|
||||
|
||||
// updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags
|
||||
virtual void updateRegistrationPoint(const glm::vec3& value);
|
||||
void updatePosition(const glm::vec3& value);
|
||||
|
|
|
@ -335,6 +335,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_OWNING_AVATAR_ID, owningAvatarID);
|
||||
|
||||
CHECK_PROPERTY_CHANGE(PROP_SHAPE, shape);
|
||||
CHECK_PROPERTY_CHANGE(PROP_DPI, dpi);
|
||||
|
||||
changedProperties += _animation.getChangedProperties();
|
||||
changedProperties += _keyLight.getChangedProperties();
|
||||
|
@ -504,6 +505,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
// Web only
|
||||
if (_type == EntityTypes::Web) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi);
|
||||
}
|
||||
|
||||
// PolyVoxel only
|
||||
|
@ -726,6 +728,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(clientOnly, bool, setClientOnly);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(owningAvatarID, QUuid, setOwningAvatarID);
|
||||
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI);
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
||||
|
@ -903,6 +907,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
|||
ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool);
|
||||
|
||||
ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t);
|
||||
|
||||
// FIXME - these are not yet handled
|
||||
//ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64);
|
||||
|
||||
|
@ -1065,6 +1071,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
|
||||
if (properties.getType() == EntityTypes::Web) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI());
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Text) {
|
||||
|
@ -1364,6 +1371,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
|
||||
if (properties.getType() == EntityTypes::Web) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI);
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Text) {
|
||||
|
@ -1642,6 +1650,8 @@ void EntityItemProperties::markAllChanged() {
|
|||
|
||||
_clientOnlyChanged = true;
|
||||
_owningAvatarIDChanged = true;
|
||||
|
||||
_dpiChanged = true;
|
||||
}
|
||||
|
||||
// The minimum bounding box for the entity.
|
||||
|
@ -1977,6 +1987,10 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
out += "ghostingAllowed";
|
||||
}
|
||||
|
||||
if (dpiChanged()) {
|
||||
out += "dpi";
|
||||
}
|
||||
|
||||
if (shapeChanged()) {
|
||||
out += "shape";
|
||||
}
|
||||
|
|
|
@ -215,6 +215,8 @@ public:
|
|||
DEFINE_PROPERTY(PROP_CLIENT_ONLY, ClientOnly, clientOnly, bool, false);
|
||||
DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID);
|
||||
|
||||
DEFINE_PROPERTY_REF(PROP_DPI, DPI, dpi, uint16_t, ENTITY_ITEM_DEFAULT_DPI);
|
||||
|
||||
static QString getBackgroundModeString(BackgroundMode mode);
|
||||
|
||||
|
||||
|
|
|
@ -73,4 +73,6 @@ const bool ENTITY_ITEM_DEFAULT_BILLBOARDED = false;
|
|||
|
||||
const QString ENTITY_ITEM_DEFAULT_NAME = QString("");
|
||||
|
||||
const uint16_t ENTITY_ITEM_DEFAULT_DPI = 30;
|
||||
|
||||
#endif // hifi_EntityItemPropertiesDefaults_h
|
||||
|
|
|
@ -176,6 +176,7 @@ enum EntityPropertyList {
|
|||
PROP_OWNING_AVATAR_ID, // doesn't go over wire
|
||||
|
||||
PROP_SHAPE,
|
||||
PROP_DPI,
|
||||
|
||||
PROP_LOCAL_VELOCITY, // only used to convert values to and from scripts
|
||||
PROP_LOCAL_ANGULAR_VELOCITY, // only used to convert values to and from scripts
|
||||
|
|
|
@ -40,7 +40,6 @@ class EntityItemFBXService {
|
|||
public:
|
||||
virtual const FBXGeometry* getGeometryForEntity(EntityItemPointer entityItem) = 0;
|
||||
virtual ModelPointer getModelForEntityItem(EntityItemPointer entityItem) = 0;
|
||||
virtual const FBXGeometry* getCollisionGeometryForEntity(EntityItemPointer entityItem) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -282,8 +282,8 @@ ShapeType ModelEntityItem::computeTrueShapeType() const {
|
|||
type = SHAPE_TYPE_COMPOUND;
|
||||
}
|
||||
if (type == SHAPE_TYPE_COMPOUND && !hasCompoundShapeURL()) {
|
||||
// no compoundURL set --> fall back to NONE
|
||||
type = SHAPE_TYPE_NONE;
|
||||
// no compoundURL set --> fall back to SIMPLE_COMPOUND
|
||||
type = SHAPE_TYPE_SIMPLE_COMPOUND;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <ByteCountCoding.h>
|
||||
#include <GeometryUtil.h>
|
||||
|
@ -30,6 +31,7 @@ EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const Ent
|
|||
|
||||
WebEntityItem::WebEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
|
||||
_type = EntityTypes::Web;
|
||||
_dpi = ENTITY_ITEM_DEFAULT_DPI;
|
||||
}
|
||||
|
||||
const float WEB_ENTITY_ITEM_FIXED_DEPTH = 0.01f;
|
||||
|
@ -42,6 +44,7 @@ void WebEntityItem::setDimensions(const glm::vec3& value) {
|
|||
EntityItemProperties WebEntityItem::getProperties(EntityPropertyFlags desiredProperties) const {
|
||||
EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dpi, getDPI);
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
@ -50,6 +53,7 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(sourceUrl, setSourceUrl);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dpi, setDPI);
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
|
@ -74,6 +78,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
|
|||
const unsigned char* dataAt = data;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
READ_ENTITY_PROPERTY(PROP_DPI, uint16_t, setDPI);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
@ -83,6 +88,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
|
|||
EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_SOURCE_URL;
|
||||
requestedProperties += PROP_DPI;
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
|
@ -96,6 +102,7 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst
|
|||
|
||||
bool successPropertyFits = true;
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl);
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, _dpi);
|
||||
}
|
||||
|
||||
bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
|
@ -123,3 +130,11 @@ void WebEntityItem::setSourceUrl(const QString& value) {
|
|||
}
|
||||
|
||||
const QString& WebEntityItem::getSourceUrl() const { return _sourceUrl; }
|
||||
|
||||
void WebEntityItem::setDPI(uint16_t value) {
|
||||
_dpi = value;
|
||||
}
|
||||
|
||||
uint16_t WebEntityItem::getDPI() const {
|
||||
return _dpi;
|
||||
}
|
||||
|
|
|
@ -54,8 +54,14 @@ public:
|
|||
virtual void setSourceUrl(const QString& value);
|
||||
const QString& getSourceUrl() const;
|
||||
|
||||
virtual bool wantsHandControllerPointerEvents() const override { return true; }
|
||||
|
||||
void setDPI(uint16_t value);
|
||||
uint16_t getDPI() const;
|
||||
|
||||
protected:
|
||||
QString _sourceUrl;
|
||||
uint16_t _dpi;
|
||||
};
|
||||
|
||||
#endif // hifi_WebEntityItem_h
|
||||
|
|
|
@ -166,7 +166,7 @@ private:
|
|||
QMyQuickRenderControl* _renderControl{ nullptr };
|
||||
FramebufferPtr _fbo;
|
||||
RenderbufferPtr _depthStencil;
|
||||
TextureRecycler _textures;
|
||||
TextureRecycler _textures { true };
|
||||
GLTextureEscrow _escrow;
|
||||
|
||||
uint64_t _lastRenderTime{ 0 };
|
||||
|
@ -399,6 +399,8 @@ void OffscreenQmlRenderThread::render() {
|
|||
glGetError();
|
||||
}
|
||||
|
||||
Context::Bound(oglplus::Texture::Target::_2D, *texture).GenerateMipmap();
|
||||
|
||||
// FIXME probably unecessary
|
||||
DefaultFramebuffer().Bind(Framebuffer::Target::Draw);
|
||||
_quickWindow->resetOpenGLState();
|
||||
|
|
|
@ -509,16 +509,28 @@ TexturePtr TextureRecycler::getNextTexture() {
|
|||
using namespace oglplus;
|
||||
if (_readyTextures.empty()) {
|
||||
TexturePtr newTexture(new Texture());
|
||||
Context::Bound(oglplus::Texture::Target::_2D, *newTexture)
|
||||
.MinFilter(TextureMinFilter::Linear)
|
||||
.MagFilter(TextureMagFilter::Linear)
|
||||
.WrapS(TextureWrap::ClampToEdge)
|
||||
.WrapT(TextureWrap::ClampToEdge)
|
||||
.Image2D(
|
||||
0, PixelDataInternalFormat::RGBA8,
|
||||
_size.x, _size.y,
|
||||
0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr
|
||||
);
|
||||
|
||||
if (_useMipmaps) {
|
||||
Context::Bound(oglplus::Texture::Target::_2D, *newTexture)
|
||||
.MinFilter(TextureMinFilter::LinearMipmapLinear)
|
||||
.MagFilter(TextureMagFilter::Linear)
|
||||
.WrapS(TextureWrap::ClampToEdge)
|
||||
.WrapT(TextureWrap::ClampToEdge)
|
||||
.Anisotropy(8.0f)
|
||||
.LODBias(-0.2f)
|
||||
.Image2D(0, PixelDataInternalFormat::RGBA8,
|
||||
_size.x, _size.y,
|
||||
0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr);
|
||||
} else {
|
||||
Context::Bound(oglplus::Texture::Target::_2D, *newTexture)
|
||||
.MinFilter(TextureMinFilter::Linear)
|
||||
.MagFilter(TextureMagFilter::Linear)
|
||||
.WrapS(TextureWrap::ClampToEdge)
|
||||
.WrapT(TextureWrap::ClampToEdge)
|
||||
.Image2D(0, PixelDataInternalFormat::RGBA8,
|
||||
_size.x, _size.y,
|
||||
0, PixelDataFormat::RGB, PixelDataType::UnsignedByte, nullptr);
|
||||
}
|
||||
GLuint texId = GetName(*newTexture);
|
||||
_allTextures[texId] = TexInfo{ newTexture, _size };
|
||||
_readyTextures.push(newTexture);
|
||||
|
|
|
@ -190,6 +190,7 @@ using BasicFramebufferWrapperPtr = std::shared_ptr<BasicFramebufferWrapper>;
|
|||
|
||||
class TextureRecycler {
|
||||
public:
|
||||
TextureRecycler(bool useMipmaps) : _useMipmaps(useMipmaps) {}
|
||||
void setSize(const uvec2& size);
|
||||
void clear();
|
||||
TexturePtr getNextTexture();
|
||||
|
@ -212,4 +213,5 @@ private:
|
|||
Map _allTextures;
|
||||
Queue _readyTextures;
|
||||
uvec2 _size{ 1920, 1080 };
|
||||
bool _useMipmaps;
|
||||
};
|
||||
|
|
|
@ -180,10 +180,10 @@ public:
|
|||
using Index = int;
|
||||
|
||||
BufferPointer _buffer;
|
||||
Size _offset;
|
||||
Size _size;
|
||||
Element _element;
|
||||
uint16 _stride;
|
||||
Size _offset { 0 };
|
||||
Size _size { 0 };
|
||||
Element _element { DEFAULT_ELEMENT };
|
||||
uint16 _stride { 0 };
|
||||
|
||||
BufferView(const BufferView& view) = default;
|
||||
BufferView& operator=(const BufferView& view) = default;
|
||||
|
|
|
@ -676,6 +676,10 @@ void sphericalHarmonicsEvaluateDirection(float * result, int order, const glm::
|
|||
}
|
||||
|
||||
bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<glm::vec3> & output, const uint order) {
|
||||
int width = cubeTexture.getWidth();
|
||||
if(width != cubeTexture.getHeight()) {
|
||||
return false;
|
||||
}
|
||||
const uint sqOrder = order*order;
|
||||
|
||||
// allocate memory for calculations
|
||||
|
@ -684,8 +688,6 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
|
|||
std::vector<float> resultG(sqOrder);
|
||||
std::vector<float> resultB(sqOrder);
|
||||
|
||||
int width, height;
|
||||
|
||||
// initialize values
|
||||
float fWt = 0.0f;
|
||||
for(uint i=0; i < sqOrder; i++) {
|
||||
|
@ -696,11 +698,16 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
|
|||
}
|
||||
std::vector<float> shBuff(sqOrder);
|
||||
std::vector<float> shBuffB(sqOrder);
|
||||
// get width and height
|
||||
width = height = cubeTexture.getWidth();
|
||||
if(width != height) {
|
||||
return false;
|
||||
|
||||
// We trade accuracy for speed by breaking the image into 32x32 parts
|
||||
// and approximating the distance for all the pixels in each part to be
|
||||
// the distance to the part's center.
|
||||
int numDivisionsPerSide = 32;
|
||||
if (width < numDivisionsPerSide) {
|
||||
numDivisionsPerSide = width;
|
||||
}
|
||||
int stride = width / numDivisionsPerSide;
|
||||
int halfStride = stride / 2;
|
||||
|
||||
// for each face of cube texture
|
||||
for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
|
||||
|
@ -718,11 +725,11 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
|
|||
// step between two texels for range [-1, 1]
|
||||
float invWidthBy2 = 2.0f / float(width);
|
||||
|
||||
for(int y=0; y < width; y++) {
|
||||
for(int y=halfStride; y < width-halfStride; y += stride) {
|
||||
// texture coordinate V in range [-1 to 1]
|
||||
const float fV = negativeBound + float(y) * invWidthBy2;
|
||||
|
||||
for(int x=0; x < width; x++) {
|
||||
for(int x=halfStride; x < width - halfStride; x += stride) {
|
||||
// texture coordinate U in range [-1 to 1]
|
||||
const float fU = negativeBound + float(x) * invWidthBy2;
|
||||
|
||||
|
@ -785,32 +792,37 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
|
|||
sphericalHarmonicsEvaluateDirection(shBuff.data(), order, dir);
|
||||
|
||||
// index of texel in texture
|
||||
uint pixOffsetIndex = (x + y * width) * numComponents;
|
||||
|
||||
// get color from texture and map to range [0, 1]
|
||||
glm::vec3 clr(ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex]),
|
||||
ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex + 1]),
|
||||
ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex + 2]));
|
||||
float red { 0.0f };
|
||||
float green { 0.0f };
|
||||
float blue { 0.0f };
|
||||
for (int i = 0; i < stride; ++i) {
|
||||
for (int j = 0; j < stride; ++j) {
|
||||
int k = (int)(x + i - halfStride + (y + j - halfStride) * width) * numComponents;
|
||||
red += ColorUtils::sRGB8ToLinearFloat(data[k]);
|
||||
green += ColorUtils::sRGB8ToLinearFloat(data[k + 1]);
|
||||
blue += ColorUtils::sRGB8ToLinearFloat(data[k + 2]);
|
||||
}
|
||||
}
|
||||
glm::vec3 clr(red, green, blue);
|
||||
|
||||
// scale color and add to previously accumulated coefficients
|
||||
sphericalHarmonicsScale(shBuffB.data(), order,
|
||||
shBuff.data(), clr.r * fDiffSolid);
|
||||
sphericalHarmonicsAdd(resultR.data(), order,
|
||||
resultR.data(), shBuffB.data());
|
||||
sphericalHarmonicsScale(shBuffB.data(), order,
|
||||
shBuff.data(), clr.g * fDiffSolid);
|
||||
sphericalHarmonicsAdd(resultG.data(), order,
|
||||
resultG.data(), shBuffB.data());
|
||||
sphericalHarmonicsScale(shBuffB.data(), order,
|
||||
shBuff.data(), clr.b * fDiffSolid);
|
||||
sphericalHarmonicsAdd(resultB.data(), order,
|
||||
resultB.data(), shBuffB.data());
|
||||
// red
|
||||
sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), clr.r * fDiffSolid);
|
||||
sphericalHarmonicsAdd(resultR.data(), order, resultR.data(), shBuffB.data());
|
||||
// green
|
||||
sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), clr.g * fDiffSolid);
|
||||
sphericalHarmonicsAdd(resultG.data(), order, resultG.data(), shBuffB.data());
|
||||
// blue
|
||||
sphericalHarmonicsScale(shBuffB.data(), order, shBuff.data(), clr.b * fDiffSolid);
|
||||
sphericalHarmonicsAdd(resultB.data(), order, resultB.data(), shBuffB.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// final scale for coefficients
|
||||
const float fNormProj = (4.0f * glm::pi<float>()) / fWt;
|
||||
const float fNormProj = (4.0f * glm::pi<float>()) / (fWt * (float)(stride * stride));
|
||||
sphericalHarmonicsScale(resultR.data(), order, resultR.data(), fNormProj);
|
||||
sphericalHarmonicsScale(resultG.data(), order, resultG.data(), fNormProj);
|
||||
sphericalHarmonicsScale(resultB.data(), order, resultB.data(), fNormProj);
|
||||
|
|
|
@ -330,9 +330,22 @@ bool Geometry::areTexturesLoaded() const {
|
|||
if (!_areTexturesLoaded) {
|
||||
for (auto& material : _materials) {
|
||||
// Check if material textures are loaded
|
||||
if (std::any_of(material->_textures.cbegin(), material->_textures.cend(),
|
||||
[](const NetworkMaterial::Textures::value_type& it) { return it.texture && !it.texture->isLoaded(); })) {
|
||||
bool materialMissingTexture = std::any_of(material->_textures.cbegin(), material->_textures.cend(),
|
||||
[](const NetworkMaterial::Textures::value_type& it) {
|
||||
auto texture = it.texture;
|
||||
if (!texture) {
|
||||
return false;
|
||||
}
|
||||
// Failed texture downloads need to be considered as 'loaded'
|
||||
// or the object will never fade in
|
||||
bool finished = texture->isLoaded() || texture->isFailed();
|
||||
if (!finished) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (materialMissingTexture) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -111,13 +111,13 @@ public:
|
|||
|
||||
QUrl getURL() const { return (bool)_resource ? _resource->getURL() : QUrl(); }
|
||||
|
||||
signals:
|
||||
void finished(bool success);
|
||||
|
||||
private:
|
||||
void startWatching();
|
||||
void stopWatching();
|
||||
|
||||
signals:
|
||||
void finished(bool success);
|
||||
|
||||
private slots:
|
||||
void resourceFinished(bool success);
|
||||
void resourceRefreshed();
|
||||
|
|
|
@ -130,7 +130,7 @@ protected:
|
|||
void evalVertexStream();
|
||||
|
||||
};
|
||||
typedef std::shared_ptr< Mesh > MeshPointer;
|
||||
using MeshPointer = std::shared_ptr< Mesh >;
|
||||
|
||||
|
||||
class Geometry {
|
||||
|
|
|
@ -49,6 +49,11 @@ void Skybox::updateSchemaBuffer() const {
|
|||
}
|
||||
}
|
||||
|
||||
void Skybox::clear() {
|
||||
_schemaBuffer.edit<Schema>().color = vec3(0);
|
||||
setCubemap(nullptr);
|
||||
}
|
||||
|
||||
void Skybox::prepare(gpu::Batch& batch, int textureSlot, int bufferSlot) const {
|
||||
if (bufferSlot > -1) {
|
||||
batch.setUniformBuffer(bufferSlot, _schemaBuffer);
|
||||
|
|
|
@ -35,7 +35,8 @@ public:
|
|||
void setCubemap(const gpu::TexturePointer& cubemap);
|
||||
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
|
||||
|
||||
virtual void clear() { setCubemap(nullptr); }
|
||||
virtual bool empty() { return _schemaBuffer.get<Schema>().color == vec3(0) && !_cubemap; }
|
||||
virtual void clear();
|
||||
|
||||
void prepare(gpu::Batch& batch, int textureSlot = SKYBOX_SKYMAP_SLOT, int bufferSlot = SKYBOX_CONSTANTS_SLOT) const;
|
||||
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const;
|
||||
|
@ -50,7 +51,7 @@ protected:
|
|||
|
||||
class Schema {
|
||||
public:
|
||||
glm::vec3 color { 1.0f, 1.0f, 1.0f };
|
||||
glm::vec3 color { 0.0f, 0.0f, 0.0f };
|
||||
float blend { 0.0f };
|
||||
};
|
||||
|
||||
|
|
|
@ -226,8 +226,6 @@ private:
|
|||
void resetResourceCounters();
|
||||
void removeResource(const QUrl& url, qint64 size = 0);
|
||||
|
||||
void getResourceAsynchronously(const QUrl& url);
|
||||
|
||||
static int _requestLimit;
|
||||
static int _requestsActive;
|
||||
|
||||
|
@ -282,6 +280,9 @@ public:
|
|||
/// Checks whether the resource has loaded.
|
||||
virtual bool isLoaded() const { return _loaded; }
|
||||
|
||||
/// Checks whether the resource has failed to download.
|
||||
virtual bool isFailed() const { return _failedToLoad; }
|
||||
|
||||
/// For loading resources, returns the number of bytes received.
|
||||
qint64 getBytesReceived() const { return _bytesReceived; }
|
||||
|
||||
|
|
|
@ -47,12 +47,12 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityAdd:
|
||||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
return VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS;
|
||||
return VERSION_WEB_ENTITIES_SUPPORT_DPI;
|
||||
case PacketType::AvatarIdentity:
|
||||
case PacketType::AvatarData:
|
||||
case PacketType::BulkAvatarData:
|
||||
case PacketType::KillAvatar:
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::AbsoluteSixByteRotations);
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::SensorToWorldMat);
|
||||
case PacketType::ICEServerHeartbeat:
|
||||
return 18; // ICE Server Heartbeat signing
|
||||
case PacketType::AssetGetInfo:
|
||||
|
|
|
@ -186,12 +186,14 @@ const PacketVersion VERSION_ENTITIES_MORE_SHAPES = 59;
|
|||
const PacketVersion VERSION_ENTITIES_PROPERLY_ENCODE_SHAPE_EDITS = 60;
|
||||
const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_STATIC_MESH = 61;
|
||||
const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS = 62;
|
||||
const PacketVersion VERSION_WEB_ENTITIES_SUPPORT_DPI = 63;
|
||||
|
||||
enum class AvatarMixerPacketVersion : PacketVersion {
|
||||
TranslationSupport = 17,
|
||||
SoftAttachmentSupport,
|
||||
AvatarEntities,
|
||||
AbsoluteSixByteRotations
|
||||
AbsoluteSixByteRotations,
|
||||
SensorToWorldMat
|
||||
};
|
||||
|
||||
enum class DomainConnectRequestVersion : PacketVersion {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
set(TARGET_NAME physics)
|
||||
setup_hifi_library()
|
||||
link_hifi_libraries(shared fbx entities)
|
||||
link_hifi_libraries(shared fbx entities model)
|
||||
|
||||
target_bullet()
|
||||
|
|
213
libraries/physics/src/CollisionRenderMeshCache.cpp
Normal file
213
libraries/physics/src/CollisionRenderMeshCache.cpp
Normal file
|
@ -0,0 +1,213 @@
|
|||
//
|
||||
// CollisionRenderMeshCache.cpp
|
||||
// libraries/physcis/src
|
||||
//
|
||||
// Created by Andrew Meadows 2016.07.13
|
||||
// Copyright 2016 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 "CollisionRenderMeshCache.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
#include <BulletCollision/CollisionShapes/btShapeHull.h>
|
||||
|
||||
#include <ShapeInfo.h> // for MAX_HULL_POINTS
|
||||
|
||||
const int32_t MAX_HULL_INDICES = 6 * MAX_HULL_POINTS;
|
||||
const int32_t MAX_HULL_NORMALS = MAX_HULL_INDICES;
|
||||
float tempVertices[MAX_HULL_NORMALS];
|
||||
model::Index tempIndexBuffer[MAX_HULL_INDICES];
|
||||
|
||||
bool copyShapeToMesh(const btTransform& transform, const btConvexShape* shape,
|
||||
gpu::BufferView& vertices, gpu::BufferView& indices, gpu::BufferView& parts,
|
||||
gpu::BufferView& normals) {
|
||||
assert(shape);
|
||||
|
||||
btShapeHull hull(shape);
|
||||
if (!hull.buildHull(shape->getMargin())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t numHullIndices = hull.numIndices();
|
||||
assert(numHullIndices <= MAX_HULL_INDICES);
|
||||
|
||||
int32_t numHullVertices = hull.numVertices();
|
||||
assert(numHullVertices <= MAX_HULL_POINTS);
|
||||
|
||||
{ // new part
|
||||
model::Mesh::Part part;
|
||||
part._startIndex = (model::Index)indices.getNumElements();
|
||||
part._numIndices = (model::Index)numHullIndices;
|
||||
// FIXME: the render code cannot handle the case where part._baseVertex != 0
|
||||
//part._baseVertex = vertices.getNumElements(); // DOES NOT WORK
|
||||
part._baseVertex = 0;
|
||||
|
||||
gpu::BufferView::Size numBytes = sizeof(model::Mesh::Part);
|
||||
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(&part);
|
||||
parts._buffer->append(numBytes, data);
|
||||
parts._size = parts._buffer->getSize();
|
||||
}
|
||||
|
||||
const int32_t SIZE_OF_VEC3 = 3 * sizeof(float);
|
||||
model::Index indexOffset = (model::Index)vertices.getNumElements();
|
||||
|
||||
{ // new indices
|
||||
const uint32_t* hullIndices = hull.getIndexPointer();
|
||||
// FIXME: the render code cannot handle the case where part._baseVertex != 0
|
||||
// so we must add an offset to each index
|
||||
for (int32_t i = 0; i < numHullIndices; ++i) {
|
||||
tempIndexBuffer[i] = hullIndices[i] + indexOffset;
|
||||
}
|
||||
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(tempIndexBuffer);
|
||||
gpu::BufferView::Size numBytes = (gpu::BufferView::Size)(sizeof(model::Index) * numHullIndices);
|
||||
indices._buffer->append(numBytes, data);
|
||||
indices._size = indices._buffer->getSize();
|
||||
}
|
||||
{ // new vertices
|
||||
const btVector3* hullVertices = hull.getVertexPointer();
|
||||
assert(numHullVertices <= MAX_HULL_POINTS);
|
||||
for (int32_t i = 0; i < numHullVertices; ++i) {
|
||||
btVector3 transformedPoint = transform * hullVertices[i];
|
||||
memcpy(tempVertices + 3 * i, transformedPoint.m_floats, SIZE_OF_VEC3);
|
||||
}
|
||||
gpu::BufferView::Size numBytes = sizeof(float) * (3 * numHullVertices);
|
||||
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(tempVertices);
|
||||
vertices._buffer->append(numBytes, data);
|
||||
vertices._size = vertices._buffer->getSize();
|
||||
}
|
||||
{ // new normals
|
||||
// compute average point
|
||||
btVector3 avgVertex(0.0f, 0.0f, 0.0f);
|
||||
const btVector3* hullVertices = hull.getVertexPointer();
|
||||
for (int i = 0; i < numHullVertices; ++i) {
|
||||
avgVertex += hullVertices[i];
|
||||
}
|
||||
avgVertex = transform * (avgVertex * (1.0f / (float)numHullVertices));
|
||||
|
||||
for (int i = 0; i < numHullVertices; ++i) {
|
||||
btVector3 norm = (transform * hullVertices[i] - avgVertex).normalize();
|
||||
memcpy(tempVertices + 3 * i, norm.m_floats, SIZE_OF_VEC3);
|
||||
}
|
||||
gpu::BufferView::Size numBytes = sizeof(float) * (3 * numHullVertices);
|
||||
const gpu::Byte* data = reinterpret_cast<const gpu::Byte*>(tempVertices);
|
||||
normals._buffer->append(numBytes, data);
|
||||
normals._size = vertices._buffer->getSize();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
model::MeshPointer createMeshFromShape(const void* pointer) {
|
||||
model::MeshPointer mesh;
|
||||
if (!pointer) {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
// pointer must be a const btCollisionShape* (cast to void*), but it only
|
||||
// needs to be valid here when its render mesh is created, after this call
|
||||
// the cache doesn't care what happens to the shape behind the pointer
|
||||
const btCollisionShape* shape = static_cast<const btCollisionShape*>(pointer);
|
||||
|
||||
int32_t shapeType = shape->getShapeType();
|
||||
if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE || shape->isConvex()) {
|
||||
// allocate buffers for it
|
||||
gpu::BufferView vertices(new gpu::Buffer(), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
|
||||
gpu::BufferView indices(new gpu::Buffer(), gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX));
|
||||
gpu::BufferView parts(new gpu::Buffer(), gpu::Element(gpu::VEC4, gpu::UINT32, gpu::PART));
|
||||
gpu::BufferView normals(new gpu::Buffer(), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
|
||||
|
||||
int32_t numSuccesses = 0;
|
||||
if (shapeType == (int32_t)COMPOUND_SHAPE_PROXYTYPE) {
|
||||
const btCompoundShape* compoundShape = static_cast<const btCompoundShape*>(shape);
|
||||
int32_t numSubShapes = compoundShape->getNumChildShapes();
|
||||
for (int32_t i = 0; i < numSubShapes; ++i) {
|
||||
const btCollisionShape* childShape = compoundShape->getChildShape(i);
|
||||
if (childShape->isConvex()) {
|
||||
const btConvexShape* convexShape = static_cast<const btConvexShape*>(childShape);
|
||||
if (copyShapeToMesh(compoundShape->getChildTransform(i), convexShape, vertices, indices, parts, normals)) {
|
||||
numSuccesses++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// shape is convex
|
||||
const btConvexShape* convexShape = static_cast<const btConvexShape*>(shape);
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
if (copyShapeToMesh(transform, convexShape, vertices, indices, parts, normals)) {
|
||||
numSuccesses++;
|
||||
}
|
||||
}
|
||||
if (numSuccesses > 0) {
|
||||
mesh = std::make_shared<model::Mesh>();
|
||||
mesh->setVertexBuffer(vertices);
|
||||
mesh->setIndexBuffer(indices);
|
||||
mesh->setPartBuffer(parts);
|
||||
mesh->addAttribute(gpu::Stream::NORMAL, normals);
|
||||
} else {
|
||||
// TODO: log failure message here
|
||||
}
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
CollisionRenderMeshCache::CollisionRenderMeshCache() {
|
||||
}
|
||||
|
||||
CollisionRenderMeshCache::~CollisionRenderMeshCache() {
|
||||
_meshMap.clear();
|
||||
_pendingGarbage.clear();
|
||||
}
|
||||
|
||||
model::MeshPointer CollisionRenderMeshCache::getMesh(CollisionRenderMeshCache::Key key) {
|
||||
model::MeshPointer mesh;
|
||||
if (key) {
|
||||
CollisionMeshMap::const_iterator itr = _meshMap.find(key);
|
||||
if (itr == _meshMap.end()) {
|
||||
// make mesh and add it to map
|
||||
mesh = createMeshFromShape(key);
|
||||
if (mesh) {
|
||||
_meshMap.insert(std::make_pair(key, mesh));
|
||||
}
|
||||
} else {
|
||||
mesh = itr->second;
|
||||
}
|
||||
}
|
||||
const uint32_t MAX_NUM_PENDING_GARBAGE = 20;
|
||||
if (_pendingGarbage.size() > MAX_NUM_PENDING_GARBAGE) {
|
||||
collectGarbage();
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
bool CollisionRenderMeshCache::releaseMesh(CollisionRenderMeshCache::Key key) {
|
||||
if (!key) {
|
||||
return false;
|
||||
}
|
||||
CollisionMeshMap::const_iterator itr = _meshMap.find(key);
|
||||
if (itr != _meshMap.end()) {
|
||||
_pendingGarbage.push_back(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CollisionRenderMeshCache::collectGarbage() {
|
||||
uint32_t numShapes = (uint32_t)_pendingGarbage.size();
|
||||
for (uint32_t i = 0; i < numShapes; ++i) {
|
||||
CollisionRenderMeshCache::Key key = _pendingGarbage[i];
|
||||
CollisionMeshMap::const_iterator itr = _meshMap.find(key);
|
||||
if (itr != _meshMap.end()) {
|
||||
if ((*itr).second.use_count() == 1) {
|
||||
// we hold the only reference
|
||||
_meshMap.erase(itr);
|
||||
}
|
||||
}
|
||||
}
|
||||
_pendingGarbage.clear();
|
||||
}
|
||||
|
48
libraries/physics/src/CollisionRenderMeshCache.h
Normal file
48
libraries/physics/src/CollisionRenderMeshCache.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// CollisionRenderMeshCache.h
|
||||
// libraries/physcis/src
|
||||
//
|
||||
// Created by Andrew Meadows 2016.07.13
|
||||
// Copyright 2016 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_CollisionRenderMeshCache_h
|
||||
#define hifi_CollisionRenderMeshCache_h
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <model/Geometry.h>
|
||||
|
||||
|
||||
class CollisionRenderMeshCache {
|
||||
public:
|
||||
using Key = const void*; // must actually be a const btCollisionShape*
|
||||
|
||||
CollisionRenderMeshCache();
|
||||
~CollisionRenderMeshCache();
|
||||
|
||||
/// \return pointer to geometry
|
||||
model::MeshPointer getMesh(Key key);
|
||||
|
||||
/// \return true if geometry was found and released
|
||||
bool releaseMesh(Key key);
|
||||
|
||||
/// delete geometries that have zero references
|
||||
void collectGarbage();
|
||||
|
||||
// validation methods
|
||||
uint32_t getNumMeshes() const { return (uint32_t)_meshMap.size(); }
|
||||
bool hasMesh(Key key) const { return _meshMap.find(key) == _meshMap.end(); }
|
||||
|
||||
private:
|
||||
using CollisionMeshMap = std::unordered_map<Key, model::MeshPointer>;
|
||||
CollisionMeshMap _meshMap;
|
||||
std::vector<Key> _pendingGarbage;
|
||||
};
|
||||
|
||||
#endif // hifi_CollisionRenderMeshCache_h
|
|
@ -46,7 +46,7 @@ bool entityTreeIsLocked() {
|
|||
|
||||
|
||||
EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer entity) :
|
||||
ObjectMotionState(shape),
|
||||
ObjectMotionState(nullptr),
|
||||
_entityPtr(entity),
|
||||
_entity(entity.get()),
|
||||
_serverPosition(0.0f),
|
||||
|
@ -71,6 +71,9 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
|||
assert(_entity);
|
||||
assert(entityTreeIsLocked());
|
||||
setMass(_entity->computeMass());
|
||||
// we need the side-effects of EntityMotionState::setShape() so we call it explicitly here
|
||||
// rather than pass the legit shape pointer to the ObjectMotionState ctor above.
|
||||
setShape(shape);
|
||||
}
|
||||
|
||||
EntityMotionState::~EntityMotionState() {
|
||||
|
@ -264,13 +267,20 @@ bool EntityMotionState::isReadyToComputeShape() const {
|
|||
}
|
||||
|
||||
// virtual and protected
|
||||
btCollisionShape* EntityMotionState::computeNewShape() {
|
||||
const btCollisionShape* EntityMotionState::computeNewShape() {
|
||||
ShapeInfo shapeInfo;
|
||||
assert(entityTreeIsLocked());
|
||||
_entity->computeShapeInfo(shapeInfo);
|
||||
return getShapeManager()->getShape(shapeInfo);
|
||||
}
|
||||
|
||||
void EntityMotionState::setShape(const btCollisionShape* shape) {
|
||||
if (_shape != shape) {
|
||||
ObjectMotionState::setShape(shape);
|
||||
_entity->setCollisionShape(_shape);
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityMotionState::isCandidateForOwnership() const {
|
||||
assert(_body);
|
||||
assert(_entity);
|
||||
|
|
|
@ -88,9 +88,10 @@ protected:
|
|||
bool entityTreeIsLocked() const;
|
||||
#endif
|
||||
|
||||
virtual bool isReadyToComputeShape() const override;
|
||||
virtual btCollisionShape* computeNewShape() override;
|
||||
virtual void setMotionType(PhysicsMotionType motionType);
|
||||
bool isReadyToComputeShape() const override;
|
||||
const btCollisionShape* computeNewShape() override;
|
||||
void setShape(const btCollisionShape* shape) override;
|
||||
void setMotionType(PhysicsMotionType motionType) override;
|
||||
|
||||
// In the glorious future (when entities lib depends on physics lib) the EntityMotionState will be
|
||||
// properly "owned" by the EntityItem and will be deleted by it in the dtor. In pursuit of that
|
||||
|
|
|
@ -62,7 +62,7 @@ ShapeManager* ObjectMotionState::getShapeManager() {
|
|||
return shapeManager;
|
||||
}
|
||||
|
||||
ObjectMotionState::ObjectMotionState(btCollisionShape* shape) :
|
||||
ObjectMotionState::ObjectMotionState(const btCollisionShape* shape) :
|
||||
_motionType(MOTION_TYPE_STATIC),
|
||||
_shape(shape),
|
||||
_body(nullptr),
|
||||
|
@ -73,7 +73,7 @@ ObjectMotionState::ObjectMotionState(btCollisionShape* shape) :
|
|||
|
||||
ObjectMotionState::~ObjectMotionState() {
|
||||
assert(!_body);
|
||||
releaseShape();
|
||||
setShape(nullptr);
|
||||
_type = MOTIONSTATE_TYPE_INVALID;
|
||||
}
|
||||
|
||||
|
@ -114,13 +114,6 @@ glm::vec3 ObjectMotionState::getBodyAngularVelocity() const {
|
|||
return bulletToGLM(_body->getAngularVelocity());
|
||||
}
|
||||
|
||||
void ObjectMotionState::releaseShape() {
|
||||
if (_shape) {
|
||||
shapeManager->releaseShape(_shape);
|
||||
_shape = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectMotionState::setMotionType(PhysicsMotionType motionType) {
|
||||
_motionType = motionType;
|
||||
}
|
||||
|
@ -160,11 +153,21 @@ void ObjectMotionState::setRigidBody(btRigidBody* body) {
|
|||
_body = body;
|
||||
if (_body) {
|
||||
_body->setUserPointer(this);
|
||||
assert(_body->getCollisionShape() == _shape);
|
||||
}
|
||||
updateCCDConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectMotionState::setShape(const btCollisionShape* shape) {
|
||||
if (_shape != shape) {
|
||||
if (_shape) {
|
||||
getShapeManager()->releaseShape(_shape);
|
||||
}
|
||||
_shape = shape;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectMotionState::handleEasyChanges(uint32_t& flags) {
|
||||
if (flags & Simulation::DIRTY_POSITION) {
|
||||
btTransform worldTrans = _body->getWorldTransform();
|
||||
|
@ -251,7 +254,7 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine*
|
|||
if (!isReadyToComputeShape()) {
|
||||
return false;
|
||||
}
|
||||
btCollisionShape* newShape = computeNewShape();
|
||||
const btCollisionShape* newShape = computeNewShape();
|
||||
if (!newShape) {
|
||||
qCDebug(physics) << "Warning: failed to generate new shape!";
|
||||
// failed to generate new shape! --> keep old shape and remove shape-change flag
|
||||
|
@ -265,15 +268,15 @@ bool ObjectMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine*
|
|||
return true;
|
||||
}
|
||||
}
|
||||
getShapeManager()->releaseShape(_shape);
|
||||
if (_shape != newShape) {
|
||||
_shape = newShape;
|
||||
_body->setCollisionShape(_shape);
|
||||
|
||||
updateCCDConfiguration();
|
||||
} else {
|
||||
// huh... the shape didn't actually change, so we clear the DIRTY_SHAPE flag
|
||||
if (_shape == newShape) {
|
||||
// the shape didn't actually change, so we clear the DIRTY_SHAPE flag
|
||||
flags &= ~Simulation::DIRTY_SHAPE;
|
||||
// and clear the reference we just created
|
||||
getShapeManager()->releaseShape(_shape);
|
||||
} else {
|
||||
_body->setCollisionShape(const_cast<btCollisionShape*>(newShape));
|
||||
setShape(newShape);
|
||||
updateCCDConfiguration();
|
||||
}
|
||||
}
|
||||
if (flags & EASY_DIRTY_PHYSICS_FLAGS) {
|
||||
|
|
|
@ -78,7 +78,7 @@ public:
|
|||
static void setShapeManager(ShapeManager* manager);
|
||||
static ShapeManager* getShapeManager();
|
||||
|
||||
ObjectMotionState(btCollisionShape* shape);
|
||||
ObjectMotionState(const btCollisionShape* shape);
|
||||
~ObjectMotionState();
|
||||
|
||||
virtual void handleEasyChanges(uint32_t& flags);
|
||||
|
@ -110,11 +110,9 @@ public:
|
|||
|
||||
virtual PhysicsMotionType computePhysicsMotionType() const = 0;
|
||||
|
||||
btCollisionShape* getShape() const { return _shape; }
|
||||
const btCollisionShape* getShape() const { return _shape; }
|
||||
btRigidBody* getRigidBody() const { return _body; }
|
||||
|
||||
void releaseShape();
|
||||
|
||||
virtual bool isMoving() const = 0;
|
||||
|
||||
// These pure virtual methods must be implemented for each MotionState type
|
||||
|
@ -152,16 +150,17 @@ public:
|
|||
|
||||
protected:
|
||||
virtual bool isReadyToComputeShape() const = 0;
|
||||
virtual btCollisionShape* computeNewShape() = 0;
|
||||
void setMotionType(PhysicsMotionType motionType);
|
||||
virtual const btCollisionShape* computeNewShape() = 0;
|
||||
virtual void setMotionType(PhysicsMotionType motionType);
|
||||
void updateCCDConfiguration();
|
||||
|
||||
void setRigidBody(btRigidBody* body);
|
||||
virtual void setShape(const btCollisionShape* shape);
|
||||
|
||||
MotionStateType _type = MOTIONSTATE_TYPE_INVALID; // type of MotionState
|
||||
PhysicsMotionType _motionType; // type of motion: KINEMATIC, DYNAMIC, or STATIC
|
||||
|
||||
btCollisionShape* _shape;
|
||||
const btCollisionShape* _shape;
|
||||
btRigidBody* _body;
|
||||
float _mass;
|
||||
|
||||
|
|
|
@ -225,7 +225,7 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re
|
|||
<< "at" << entity->getPosition() << " will be reduced";
|
||||
}
|
||||
}
|
||||
btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
EntityMotionState* motionState = new EntityMotionState(shape, entity);
|
||||
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
||||
|
|
|
@ -76,7 +76,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
switch(motionType) {
|
||||
case MOTION_TYPE_KINEMATIC: {
|
||||
if (!body) {
|
||||
btCollisionShape* shape = motionState->getShape();
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(motionState->getShape());
|
||||
assert(shape);
|
||||
body = new btRigidBody(mass, motionState, shape, inertia);
|
||||
motionState->setRigidBody(body);
|
||||
|
@ -93,7 +93,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
}
|
||||
case MOTION_TYPE_DYNAMIC: {
|
||||
mass = motionState->getMass();
|
||||
btCollisionShape* shape = motionState->getShape();
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(motionState->getShape());
|
||||
assert(shape);
|
||||
shape->calculateLocalInertia(mass, inertia);
|
||||
if (!body) {
|
||||
|
@ -120,7 +120,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
|
|||
default: {
|
||||
if (!body) {
|
||||
assert(motionState->getShape());
|
||||
body = new btRigidBody(mass, motionState, motionState->getShape(), inertia);
|
||||
body = new btRigidBody(mass, motionState, const_cast<btCollisionShape*>(motionState->getShape()), inertia);
|
||||
motionState->setRigidBody(body);
|
||||
} else {
|
||||
body->setMassProps(mass, inertia);
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
//
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <BulletCollision/CollisionShapes/btShapeHull.h>
|
||||
|
||||
#include <SharedUtil.h> // for MILLIMETERS_PER_METER
|
||||
|
||||
|
@ -248,7 +247,7 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) {
|
|||
delete dataArray;
|
||||
}
|
||||
|
||||
btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
||||
const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
||||
btCollisionShape* shape = NULL;
|
||||
int type = info.getType();
|
||||
switch(type) {
|
||||
|
@ -347,23 +346,39 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) {
|
|||
}
|
||||
if (shape) {
|
||||
if (glm::length2(info.getOffset()) > MIN_SHAPE_OFFSET * MIN_SHAPE_OFFSET) {
|
||||
// this shape has an offset, which we support by wrapping the true shape
|
||||
// in a btCompoundShape with a local transform
|
||||
auto compound = new btCompoundShape();
|
||||
btTransform trans;
|
||||
trans.setIdentity();
|
||||
trans.setOrigin(glmToBullet(info.getOffset()));
|
||||
compound->addChildShape(trans, shape);
|
||||
shape = compound;
|
||||
// we need to apply an offset
|
||||
btTransform offset;
|
||||
offset.setIdentity();
|
||||
offset.setOrigin(glmToBullet(info.getOffset()));
|
||||
|
||||
if (shape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) {
|
||||
// this shape is already compound
|
||||
// walk through the child shapes and adjust their transforms
|
||||
btCompoundShape* compound = static_cast<btCompoundShape*>(shape);
|
||||
int32_t numSubShapes = compound->getNumChildShapes();
|
||||
for (int32_t i = 0; i < numSubShapes; ++i) {
|
||||
compound->updateChildTransform(i, offset * compound->getChildTransform(i), false);
|
||||
}
|
||||
compound->recalculateLocalAabb();
|
||||
} else {
|
||||
// wrap this shape in a compound
|
||||
auto compound = new btCompoundShape();
|
||||
compound->addChildShape(offset, shape);
|
||||
shape = compound;
|
||||
}
|
||||
}
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
void ShapeFactory::deleteShape(btCollisionShape* shape) {
|
||||
void ShapeFactory::deleteShape(const btCollisionShape* shape) {
|
||||
assert(shape);
|
||||
if (shape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) {
|
||||
btCompoundShape* compoundShape = static_cast<btCompoundShape*>(shape);
|
||||
// ShapeFactory is responsible for deleting all shapes, even the const ones that are stored
|
||||
// in the ShapeManager, so we must cast to non-const here when deleting.
|
||||
// so we cast to non-const here when deleting memory.
|
||||
btCollisionShape* nonConstShape = const_cast<btCollisionShape*>(shape);
|
||||
if (nonConstShape->getShapeType() == (int)COMPOUND_SHAPE_PROXYTYPE) {
|
||||
btCompoundShape* compoundShape = static_cast<btCompoundShape*>(nonConstShape);
|
||||
const int numChildShapes = compoundShape->getNumChildShapes();
|
||||
for (int i = 0; i < numChildShapes; i ++) {
|
||||
btCollisionShape* childShape = compoundShape->getChildShape(i);
|
||||
|
@ -375,7 +390,7 @@ void ShapeFactory::deleteShape(btCollisionShape* shape) {
|
|||
}
|
||||
}
|
||||
}
|
||||
delete shape;
|
||||
delete nonConstShape;
|
||||
}
|
||||
|
||||
// the dataArray must be created before we create the StaticMeshShape
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
// translates between ShapeInfo and btShape
|
||||
|
||||
namespace ShapeFactory {
|
||||
btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
||||
void deleteShape(btCollisionShape* shape);
|
||||
const btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
|
||||
void deleteShape(const btCollisionShape* shape);
|
||||
|
||||
//btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info);
|
||||
//void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray);
|
||||
|
|
|
@ -28,15 +28,15 @@ ShapeManager::~ShapeManager() {
|
|||
_shapeMap.clear();
|
||||
}
|
||||
|
||||
btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||
const btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
||||
if (info.getType() == SHAPE_TYPE_NONE) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube
|
||||
if (4.0f * glm::length2(info.getHalfExtents()) < MIN_SHAPE_DIAGONAL_SQUARED) {
|
||||
// tiny shapes are not supported
|
||||
// qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal;
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DoubleHashKey key = info.getHash();
|
||||
|
@ -45,7 +45,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) {
|
|||
shapeRef->refCount++;
|
||||
return shapeRef->shape;
|
||||
}
|
||||
btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||
const btCollisionShape* shape = ShapeFactory::createShapeFromInfo(info);
|
||||
if (shape) {
|
||||
ShapeReference newRef;
|
||||
newRef.refCount = 1;
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
~ShapeManager();
|
||||
|
||||
/// \return pointer to shape
|
||||
btCollisionShape* getShape(const ShapeInfo& info);
|
||||
const btCollisionShape* getShape(const ShapeInfo& info);
|
||||
|
||||
/// \return true if shape was found and released
|
||||
bool releaseShape(const btCollisionShape* shape);
|
||||
|
@ -43,11 +43,12 @@ public:
|
|||
private:
|
||||
bool releaseShapeByKey(const DoubleHashKey& key);
|
||||
|
||||
struct ShapeReference {
|
||||
class ShapeReference {
|
||||
public:
|
||||
int refCount;
|
||||
btCollisionShape* shape;
|
||||
const btCollisionShape* shape;
|
||||
DoubleHashKey key;
|
||||
ShapeReference() : refCount(0), shape(NULL) {}
|
||||
ShapeReference() : refCount(0), shape(nullptr) {}
|
||||
};
|
||||
|
||||
btHashMap<DoubleHashKey, ShapeReference> _shapeMap;
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
void parse(const QString& userDataJson);
|
||||
|
||||
bool ready();
|
||||
bool enabled() { return _enabled; }
|
||||
void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation);
|
||||
const gpu::ShaderPointer& getShader() const { return _shader; }
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ ProceduralSkybox::ProceduralSkybox() : model::Skybox() {
|
|||
_procedural._opaqueState->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
}
|
||||
|
||||
bool ProceduralSkybox::empty() {
|
||||
return !_procedural.enabled() && Skybox::empty();
|
||||
}
|
||||
|
||||
void ProceduralSkybox::clear() {
|
||||
// Parse and prepare a procedural with no shaders to release textures
|
||||
parse(QString());
|
||||
|
|
|
@ -20,13 +20,14 @@
|
|||
class ProceduralSkybox: public model::Skybox {
|
||||
public:
|
||||
ProceduralSkybox();
|
||||
virtual ~ProceduralSkybox() {};
|
||||
~ProceduralSkybox() override {};
|
||||
|
||||
void parse(const QString& userData) { _procedural.parse(userData); }
|
||||
|
||||
virtual void clear() override;
|
||||
bool empty() override;
|
||||
void clear() override;
|
||||
|
||||
virtual void render(gpu::Batch& batch, const ViewFrustum& frustum) const override;
|
||||
void render(gpu::Batch& batch, const ViewFrustum& frustum) const override;
|
||||
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const ProceduralSkybox& skybox);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -30,9 +30,88 @@
|
|||
|
||||
#include "ssao_makePyramid_frag.h"
|
||||
#include "ssao_makeOcclusion_frag.h"
|
||||
#include "ssao_debugOcclusion_frag.h"
|
||||
#include "ssao_makeHorizontalBlur_frag.h"
|
||||
#include "ssao_makeVerticalBlur_frag.h"
|
||||
|
||||
|
||||
AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() {
|
||||
}
|
||||
|
||||
void AmbientOcclusionFramebuffer::updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer) {
|
||||
//If the depth buffer or size changed, we need to delete our FBOs
|
||||
bool reset = false;
|
||||
if ((_linearDepthTexture != linearDepthBuffer)) {
|
||||
_linearDepthTexture = linearDepthBuffer;
|
||||
reset = true;
|
||||
}
|
||||
if (_linearDepthTexture) {
|
||||
auto newFrameSize = glm::ivec2(_linearDepthTexture->getDimensions());
|
||||
if (_frameSize != newFrameSize) {
|
||||
_frameSize = newFrameSize;
|
||||
reset = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
void AmbientOcclusionFramebuffer::clear() {
|
||||
_occlusionFramebuffer.reset();
|
||||
_occlusionTexture.reset();
|
||||
_occlusionBlurredFramebuffer.reset();
|
||||
_occlusionBlurredTexture.reset();
|
||||
}
|
||||
|
||||
gpu::TexturePointer AmbientOcclusionFramebuffer::getLinearDepthTexture() {
|
||||
return _linearDepthTexture;
|
||||
}
|
||||
|
||||
void AmbientOcclusionFramebuffer::allocate() {
|
||||
|
||||
auto width = _frameSize.x;
|
||||
auto height = _frameSize.y;
|
||||
|
||||
_occlusionTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||
_occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
_occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture);
|
||||
|
||||
_occlusionBlurredTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||
_occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
_occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture);
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionFramebuffer() {
|
||||
if (!_occlusionFramebuffer) {
|
||||
allocate();
|
||||
}
|
||||
return _occlusionFramebuffer;
|
||||
}
|
||||
|
||||
gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionTexture() {
|
||||
if (!_occlusionTexture) {
|
||||
allocate();
|
||||
}
|
||||
return _occlusionTexture;
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer AmbientOcclusionFramebuffer::getOcclusionBlurredFramebuffer() {
|
||||
if (!_occlusionBlurredFramebuffer) {
|
||||
allocate();
|
||||
}
|
||||
return _occlusionBlurredFramebuffer;
|
||||
}
|
||||
|
||||
gpu::TexturePointer AmbientOcclusionFramebuffer::getOcclusionBlurredTexture() {
|
||||
if (!_occlusionBlurredTexture) {
|
||||
allocate();
|
||||
}
|
||||
return _occlusionBlurredTexture;
|
||||
}
|
||||
|
||||
|
||||
class GaussianDistribution {
|
||||
public:
|
||||
|
||||
|
@ -90,15 +169,11 @@ public:
|
|||
|
||||
const int AmbientOcclusionEffect_FrameTransformSlot = 0;
|
||||
const int AmbientOcclusionEffect_ParamsSlot = 1;
|
||||
const int AmbientOcclusionEffect_DepthMapSlot = 0;
|
||||
const int AmbientOcclusionEffect_PyramidMapSlot = 0;
|
||||
const int AmbientOcclusionEffect_CameraCorrectionSlot = 2;
|
||||
const int AmbientOcclusionEffect_LinearDepthMapSlot = 0;
|
||||
const int AmbientOcclusionEffect_OcclusionMapSlot = 0;
|
||||
|
||||
AmbientOcclusionEffect::AmbientOcclusionEffect() {
|
||||
FrameTransform frameTransform;
|
||||
_frameTransformBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(FrameTransform), (const gpu::Byte*) &frameTransform));
|
||||
Parameters parameters;
|
||||
_parametersBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Parameters), (const gpu::Byte*) ¶meters));
|
||||
}
|
||||
|
||||
void AmbientOcclusionEffect::configure(const Config& config) {
|
||||
|
@ -108,67 +183,75 @@ void AmbientOcclusionEffect::configure(const Config& config) {
|
|||
|
||||
const double RADIUS_POWER = 6.0;
|
||||
const auto& radius = config.radius;
|
||||
if (radius != getRadius()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().radiusInfo;
|
||||
if (radius != _parametersBuffer->getRadius()) {
|
||||
auto& current = _parametersBuffer->radiusInfo;
|
||||
current.x = radius;
|
||||
current.y = radius * radius;
|
||||
current.z = (float)(1.0 / pow((double)radius, RADIUS_POWER));
|
||||
}
|
||||
|
||||
if (config.obscuranceLevel != getObscuranceLevel()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().radiusInfo;
|
||||
if (config.obscuranceLevel != _parametersBuffer->getObscuranceLevel()) {
|
||||
auto& current = _parametersBuffer->radiusInfo;
|
||||
current.w = config.obscuranceLevel;
|
||||
}
|
||||
|
||||
if (config.falloffBias != getFalloffBias()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().ditheringInfo;
|
||||
if (config.falloffBias != _parametersBuffer->getFalloffBias()) {
|
||||
auto& current = _parametersBuffer->ditheringInfo;
|
||||
current.z = config.falloffBias;
|
||||
}
|
||||
|
||||
if (config.edgeSharpness != getEdgeSharpness()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().blurInfo;
|
||||
if (config.edgeSharpness != _parametersBuffer->getEdgeSharpness()) {
|
||||
auto& current = _parametersBuffer->blurInfo;
|
||||
current.x = config.edgeSharpness;
|
||||
}
|
||||
|
||||
if (config.blurDeviation != getBlurDeviation()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().blurInfo;
|
||||
if (config.blurDeviation != _parametersBuffer->getBlurDeviation()) {
|
||||
auto& current = _parametersBuffer->blurInfo;
|
||||
current.z = config.blurDeviation;
|
||||
shouldUpdateGaussian = true;
|
||||
}
|
||||
|
||||
if (config.numSpiralTurns != getNumSpiralTurns()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().sampleInfo;
|
||||
if (config.numSpiralTurns != _parametersBuffer->getNumSpiralTurns()) {
|
||||
auto& current = _parametersBuffer->sampleInfo;
|
||||
current.z = config.numSpiralTurns;
|
||||
}
|
||||
|
||||
if (config.numSamples != getNumSamples()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().sampleInfo;
|
||||
if (config.numSamples != _parametersBuffer->getNumSamples()) {
|
||||
auto& current = _parametersBuffer->sampleInfo;
|
||||
current.x = config.numSamples;
|
||||
current.y = 1.0f / config.numSamples;
|
||||
}
|
||||
|
||||
const auto& resolutionLevel = config.resolutionLevel;
|
||||
if (resolutionLevel != getResolutionLevel()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().resolutionInfo;
|
||||
current.x = (float)resolutionLevel;
|
||||
|
||||
// Communicate the change to the Framebuffer cache
|
||||
DependencyManager::get<FramebufferCache>()->setAmbientOcclusionResolutionLevel(resolutionLevel);
|
||||
if (config.fetchMipsEnabled != _parametersBuffer->isFetchMipsEnabled()) {
|
||||
auto& current = _parametersBuffer->sampleInfo;
|
||||
current.w = (float)config.fetchMipsEnabled;
|
||||
}
|
||||
|
||||
if (config.blurRadius != getBlurRadius()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().blurInfo;
|
||||
if (!_framebuffer) {
|
||||
_framebuffer = std::make_shared<AmbientOcclusionFramebuffer>();
|
||||
}
|
||||
|
||||
if (config.perspectiveScale != _parametersBuffer->getPerspectiveScale()) {
|
||||
_parametersBuffer->resolutionInfo.z = config.perspectiveScale;
|
||||
}
|
||||
if (config.resolutionLevel != _parametersBuffer->getResolutionLevel()) {
|
||||
auto& current = _parametersBuffer->resolutionInfo;
|
||||
current.x = (float) config.resolutionLevel;
|
||||
}
|
||||
|
||||
if (config.blurRadius != _parametersBuffer->getBlurRadius()) {
|
||||
auto& current = _parametersBuffer->blurInfo;
|
||||
current.y = (float)config.blurRadius;
|
||||
shouldUpdateGaussian = true;
|
||||
}
|
||||
|
||||
if (config.ditheringEnabled != isDitheringEnabled()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().ditheringInfo;
|
||||
if (config.ditheringEnabled != _parametersBuffer->isDitheringEnabled()) {
|
||||
auto& current = _parametersBuffer->ditheringInfo;
|
||||
current.x = (float)config.ditheringEnabled;
|
||||
}
|
||||
|
||||
if (config.borderingEnabled != isBorderingEnabled()) {
|
||||
auto& current = _parametersBuffer.edit<Parameters>().ditheringInfo;
|
||||
if (config.borderingEnabled != _parametersBuffer->isBorderingEnabled()) {
|
||||
auto& current = _parametersBuffer->ditheringInfo;
|
||||
current.w = (float)config.borderingEnabled;
|
||||
}
|
||||
|
||||
|
@ -177,32 +260,6 @@ void AmbientOcclusionEffect::configure(const Config& config) {
|
|||
}
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& AmbientOcclusionEffect::getPyramidPipeline() {
|
||||
if (!_pyramidPipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(ssao_makePyramid_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), AmbientOcclusionEffect_DepthMapSlot));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
// Stencil test the pyramid passe for objects pixels only, not the background
|
||||
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
|
||||
|
||||
state->setColorWriteMask(true, false, false, false);
|
||||
|
||||
// Good to go add the brand new pipeline
|
||||
_pyramidPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
return _pyramidPipeline;
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() {
|
||||
if (!_occlusionPipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
|
||||
|
@ -210,9 +267,11 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() {
|
|||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("pyramidMap"), AmbientOcclusionEffect_PyramidMapSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("cameraCorrectionBuffer"), AmbientOcclusionEffect_CameraCorrectionSlot));
|
||||
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("pyramidMap"), AmbientOcclusionEffect_LinearDepthMapSlot));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
@ -234,6 +293,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getHBlurPipeline() {
|
|||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("cameraCorrectionBuffer"), AmbientOcclusionEffect_CameraCorrectionSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), AmbientOcclusionEffect_OcclusionMapSlot));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
@ -256,6 +316,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() {
|
|||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("cameraCorrectionBuffer"), AmbientOcclusionEffect_CameraCorrectionSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), AmbientOcclusionEffect_OcclusionMapSlot));
|
||||
|
||||
|
@ -272,76 +333,50 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() {
|
|||
return _vBlurPipeline;
|
||||
}
|
||||
|
||||
|
||||
void AmbientOcclusionEffect::setDepthInfo(float nearZ, float farZ) {
|
||||
_frameTransformBuffer.edit<FrameTransform>().depthInfo = glm::vec4(nearZ*farZ, farZ -nearZ, -farZ, 0.0f);
|
||||
}
|
||||
|
||||
void AmbientOcclusionEffect::updateGaussianDistribution() {
|
||||
auto coefs = _parametersBuffer.edit<Parameters>()._gaussianCoefs;
|
||||
GaussianDistribution::evalSampling(coefs, Parameters::GAUSSIAN_COEFS_LENGTH, getBlurRadius(), getBlurDeviation());
|
||||
auto coefs = _parametersBuffer->_gaussianCoefs;
|
||||
GaussianDistribution::evalSampling(coefs, Parameters::GAUSSIAN_COEFS_LENGTH, _parametersBuffer->getBlurRadius(), _parametersBuffer->getBlurDeviation());
|
||||
}
|
||||
|
||||
void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) {
|
||||
#ifdef FIX_THE_FRAMEBUFFER_CACHE
|
||||
void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
// FIXME: Different render modes should have different tasks
|
||||
if (args->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) {
|
||||
return;
|
||||
const auto& frameTransform = inputs.get0();
|
||||
const auto& linearDepthFramebuffer = inputs.get2();
|
||||
|
||||
auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture();
|
||||
auto sourceViewport = args->_viewport;
|
||||
auto occlusionViewport = sourceViewport;
|
||||
|
||||
if (!_framebuffer) {
|
||||
_framebuffer = std::make_shared<AmbientOcclusionFramebuffer>();
|
||||
}
|
||||
|
||||
if (_parametersBuffer->getResolutionLevel() > 0) {
|
||||
linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture();
|
||||
occlusionViewport = occlusionViewport >> _parametersBuffer->getResolutionLevel();
|
||||
}
|
||||
|
||||
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||
auto depthBuffer = framebufferCache->getPrimaryDepthTexture();
|
||||
auto normalBuffer = framebufferCache->getDeferredNormalTexture();
|
||||
auto pyramidFBO = framebufferCache->getDepthPyramidFramebuffer();
|
||||
auto occlusionFBO = framebufferCache->getOcclusionFramebuffer();
|
||||
auto occlusionBlurredFBO = framebufferCache->getOcclusionBlurredFramebuffer();
|
||||
_framebuffer->updateLinearDepth(linearDepthTexture);
|
||||
|
||||
|
||||
auto occlusionFBO = _framebuffer->getOcclusionFramebuffer();
|
||||
auto occlusionBlurredFBO = _framebuffer->getOcclusionBlurredFramebuffer();
|
||||
|
||||
outputs.edit0() = _framebuffer;
|
||||
outputs.edit1() = _parametersBuffer;
|
||||
|
||||
QSize framebufferSize = framebufferCache->getFrameBufferSize();
|
||||
float sMin = args->_viewport.x / (float)framebufferSize.width();
|
||||
float sWidth = args->_viewport.z / (float)framebufferSize.width();
|
||||
float tMin = args->_viewport.y / (float)framebufferSize.height();
|
||||
float tHeight = args->_viewport.w / (float)framebufferSize.height();
|
||||
auto framebufferSize = _framebuffer->getSourceFrameSize();
|
||||
|
||||
float sMin = occlusionViewport.x / (float)framebufferSize.x;
|
||||
float sWidth = occlusionViewport.z / (float)framebufferSize.x;
|
||||
float tMin = occlusionViewport.y / (float)framebufferSize.y;
|
||||
float tHeight = occlusionViewport.w / (float)framebufferSize.y;
|
||||
|
||||
auto resolutionLevel = getResolutionLevel();
|
||||
|
||||
// Update the depth info with near and far (same for stereo)
|
||||
setDepthInfo(args->getViewFrustum().getNearClip(), args->getViewFrustum().getFarClip());
|
||||
|
||||
_frameTransformBuffer.edit<FrameTransform>().pixelInfo = args->_viewport;
|
||||
//_parametersBuffer.edit<Parameters>()._ditheringInfo.y += 0.25f;
|
||||
|
||||
// Running in stero ?
|
||||
bool isStereo = args->_context->isStereo();
|
||||
if (!isStereo) {
|
||||
// Eval the mono projection
|
||||
mat4 monoProjMat;
|
||||
args->getViewFrustum().evalProjectionMatrix(monoProjMat);
|
||||
_frameTransformBuffer.edit<FrameTransform>().projection[0] = monoProjMat;
|
||||
_frameTransformBuffer.edit<FrameTransform>().stereoInfo = glm::vec4(0.0f, (float)args->_viewport.z, 0.0f, 0.0f);
|
||||
|
||||
} else {
|
||||
|
||||
mat4 projMats[2];
|
||||
mat4 eyeViews[2];
|
||||
args->_context->getStereoProjections(projMats);
|
||||
args->_context->getStereoViews(eyeViews);
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Compose the mono Eye space to Stereo clip space Projection Matrix
|
||||
auto sideViewMat = projMats[i] * eyeViews[i];
|
||||
_frameTransformBuffer.edit<FrameTransform>().projection[i] = sideViewMat;
|
||||
}
|
||||
|
||||
_frameTransformBuffer.edit<FrameTransform>().stereoInfo = glm::vec4(1.0f, (float)(args->_viewport.z >> 1), 0.0f, 1.0f);
|
||||
|
||||
}
|
||||
|
||||
auto pyramidPipeline = getPyramidPipeline();
|
||||
auto occlusionPipeline = getOcclusionPipeline();
|
||||
auto firstHBlurPipeline = getHBlurPipeline();
|
||||
auto lastVBlurPipeline = getVBlurPipeline();
|
||||
|
@ -351,7 +386,7 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
|
|||
|
||||
_gpuTimer.begin(batch);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setViewportTransform(occlusionViewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.resetViewTransform();
|
||||
|
||||
|
@ -360,35 +395,22 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
|
|||
model.setScale(glm::vec3(sWidth, tHeight, 1.0f));
|
||||
batch.setModelTransform(model);
|
||||
|
||||
batch.setUniformBuffer(AmbientOcclusionEffect_FrameTransformSlot, _frameTransformBuffer);
|
||||
batch.setUniformBuffer(AmbientOcclusionEffect_FrameTransformSlot, frameTransform->getFrameTransformBuffer());
|
||||
batch.setUniformBuffer(AmbientOcclusionEffect_ParamsSlot, _parametersBuffer);
|
||||
|
||||
|
||||
// Pyramid pass
|
||||
batch.setFramebuffer(pyramidFBO);
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(args->getViewFrustum().getFarClip(), 0.0f, 0.0f, 0.0f));
|
||||
batch.setPipeline(pyramidPipeline);
|
||||
batch.setResourceTexture(AmbientOcclusionEffect_DepthMapSlot, depthBuffer);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
// Make pyramid mips
|
||||
batch.generateTextureMips(pyramidFBO->getRenderBuffer(0));
|
||||
|
||||
// Adjust Viewport for rendering resolution
|
||||
if (resolutionLevel > 0) {
|
||||
glm::ivec4 viewport(args->_viewport.x, args->_viewport.y, args->_viewport.z >> resolutionLevel, args->_viewport.w >> resolutionLevel);
|
||||
batch.setViewportTransform(viewport);
|
||||
}
|
||||
|
||||
|
||||
// We need this with the mips levels
|
||||
batch.generateTextureMips(_framebuffer->getLinearDepthTexture());
|
||||
|
||||
// Occlusion pass
|
||||
batch.setFramebuffer(occlusionFBO);
|
||||
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(1.0f));
|
||||
batch.setPipeline(occlusionPipeline);
|
||||
batch.setResourceTexture(AmbientOcclusionEffect_PyramidMapSlot, pyramidFBO->getRenderBuffer(0));
|
||||
batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, _framebuffer->getLinearDepthTexture());
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
|
||||
if (getBlurRadius() > 0) {
|
||||
if (_parametersBuffer->getBlurRadius() > 0) {
|
||||
// Blur 1st pass
|
||||
batch.setFramebuffer(occlusionBlurredFBO);
|
||||
batch.setPipeline(firstHBlurPipeline);
|
||||
|
@ -402,10 +424,118 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
|
|||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
}
|
||||
|
||||
|
||||
batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, nullptr);
|
||||
batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, nullptr);
|
||||
|
||||
_gpuTimer.end(batch);
|
||||
});
|
||||
|
||||
// Update the timer
|
||||
std::static_pointer_cast<Config>(renderContext->jobConfig)->gpuTime = _gpuTimer.getAverage();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
DebugAmbientOcclusion::DebugAmbientOcclusion() {
|
||||
}
|
||||
|
||||
void DebugAmbientOcclusion::configure(const Config& config) {
|
||||
|
||||
_showCursorPixel = config.showCursorPixel;
|
||||
|
||||
auto cursorPos = glm::vec2(_parametersBuffer->pixelInfo);
|
||||
if (cursorPos != config.debugCursorTexcoord) {
|
||||
_parametersBuffer->pixelInfo = glm::vec4(config.debugCursorTexcoord, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& DebugAmbientOcclusion::getDebugPipeline() {
|
||||
if (!_debugPipeline) {
|
||||
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
|
||||
auto ps = gpu::Shader::createPixel(std::string(ssao_debugOcclusion_frag));
|
||||
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), AmbientOcclusionEffect_FrameTransformSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("debugAmbientOcclusionBuffer"), 2));
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("pyramidMap"), AmbientOcclusionEffect_LinearDepthMapSlot));
|
||||
gpu::Shader::makeProgram(*program, slotBindings);
|
||||
|
||||
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
|
||||
|
||||
state->setColorWriteMask(true, true, true, false);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
|
||||
// Good to go add the brand new pipeline
|
||||
_debugPipeline = gpu::Pipeline::create(program, state);
|
||||
}
|
||||
return _debugPipeline;
|
||||
}
|
||||
|
||||
void DebugAmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs) {
|
||||
assert(renderContext->args);
|
||||
assert(renderContext->args->hasViewFrustum());
|
||||
|
||||
if (!_showCursorPixel) {
|
||||
return;
|
||||
}
|
||||
|
||||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto& frameTransform = inputs.get0();
|
||||
const auto& linearDepthFramebuffer = inputs.get2();
|
||||
const auto& ambientOcclusionUniforms = inputs.get3();
|
||||
|
||||
// Skip if AO is not started yet
|
||||
if (!ambientOcclusionUniforms._buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto linearDepthTexture = linearDepthFramebuffer->getLinearDepthTexture();
|
||||
auto sourceViewport = args->_viewport;
|
||||
auto occlusionViewport = sourceViewport;
|
||||
|
||||
auto resolutionLevel = ambientOcclusionUniforms->getResolutionLevel();
|
||||
|
||||
if (resolutionLevel > 0) {
|
||||
linearDepthTexture = linearDepthFramebuffer->getHalfLinearDepthTexture();
|
||||
occlusionViewport = occlusionViewport >> ambientOcclusionUniforms->getResolutionLevel();
|
||||
}
|
||||
|
||||
|
||||
auto framebufferSize = glm::ivec2(linearDepthTexture->getDimensions());
|
||||
|
||||
float sMin = occlusionViewport.x / (float)framebufferSize.x;
|
||||
float sWidth = occlusionViewport.z / (float)framebufferSize.x;
|
||||
float tMin = occlusionViewport.y / (float)framebufferSize.y;
|
||||
float tHeight = occlusionViewport.w / (float)framebufferSize.y;
|
||||
|
||||
auto debugPipeline = getDebugPipeline();
|
||||
|
||||
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
||||
batch.setViewportTransform(sourceViewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.setViewTransform(Transform());
|
||||
|
||||
Transform model;
|
||||
model.setTranslation(glm::vec3(sMin, tMin, 0.0f));
|
||||
model.setScale(glm::vec3(sWidth, tHeight, 1.0f));
|
||||
batch.setModelTransform(model);
|
||||
|
||||
batch.setUniformBuffer(AmbientOcclusionEffect_FrameTransformSlot, frameTransform->getFrameTransformBuffer());
|
||||
batch.setUniformBuffer(AmbientOcclusionEffect_ParamsSlot, ambientOcclusionUniforms);
|
||||
batch.setUniformBuffer(2, _parametersBuffer);
|
||||
|
||||
batch.setPipeline(debugPipeline);
|
||||
batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, linearDepthTexture);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
|
||||
batch.setResourceTexture(AmbientOcclusionEffect_LinearDepthMapSlot, nullptr);
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -16,11 +16,49 @@
|
|||
|
||||
#include "render/DrawTask.h"
|
||||
|
||||
#include "DeferredFrameTransform.h"
|
||||
#include "DeferredFramebuffer.h"
|
||||
#include "SurfaceGeometryPass.h"
|
||||
|
||||
class AmbientOcclusionFramebuffer {
|
||||
public:
|
||||
AmbientOcclusionFramebuffer();
|
||||
|
||||
gpu::FramebufferPointer getOcclusionFramebuffer();
|
||||
gpu::TexturePointer getOcclusionTexture();
|
||||
|
||||
gpu::FramebufferPointer getOcclusionBlurredFramebuffer();
|
||||
gpu::TexturePointer getOcclusionBlurredTexture();
|
||||
|
||||
// Update the source framebuffer size which will drive the allocation of all the other resources.
|
||||
void updateLinearDepth(const gpu::TexturePointer& linearDepthBuffer);
|
||||
gpu::TexturePointer getLinearDepthTexture();
|
||||
const glm::ivec2& getSourceFrameSize() const { return _frameSize; }
|
||||
|
||||
protected:
|
||||
void clear();
|
||||
void allocate();
|
||||
|
||||
gpu::TexturePointer _linearDepthTexture;
|
||||
|
||||
gpu::FramebufferPointer _occlusionFramebuffer;
|
||||
gpu::TexturePointer _occlusionTexture;
|
||||
|
||||
gpu::FramebufferPointer _occlusionBlurredFramebuffer;
|
||||
gpu::TexturePointer _occlusionBlurredTexture;
|
||||
|
||||
|
||||
glm::ivec2 _frameSize;
|
||||
};
|
||||
|
||||
using AmbientOcclusionFramebufferPointer = std::shared_ptr<AmbientOcclusionFramebuffer>;
|
||||
|
||||
class AmbientOcclusionEffectConfig : public render::Job::Config::Persistent {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool enabled MEMBER enabled NOTIFY dirty)
|
||||
Q_PROPERTY(bool ditheringEnabled MEMBER ditheringEnabled NOTIFY dirty)
|
||||
Q_PROPERTY(bool borderingEnabled MEMBER borderingEnabled NOTIFY dirty)
|
||||
Q_PROPERTY(bool fetchMipsEnabled MEMBER fetchMipsEnabled NOTIFY dirty)
|
||||
Q_PROPERTY(float radius MEMBER radius WRITE setRadius)
|
||||
Q_PROPERTY(float obscuranceLevel MEMBER obscuranceLevel WRITE setObscuranceLevel)
|
||||
Q_PROPERTY(float falloffBias MEMBER falloffBias WRITE setFalloffBias)
|
||||
|
@ -49,72 +87,62 @@ public:
|
|||
double getGpuTime() { return gpuTime; }
|
||||
|
||||
float radius{ 0.5f };
|
||||
float perspectiveScale{ 1.0f };
|
||||
float obscuranceLevel{ 0.5f }; // intensify or dim down the obscurance effect
|
||||
float falloffBias{ 0.01f };
|
||||
float edgeSharpness{ 1.0f };
|
||||
float blurDeviation{ 2.5f };
|
||||
float numSpiralTurns{ 7.0f }; // defining an angle span to distribute the samples ray directions
|
||||
int numSamples{ 11 };
|
||||
int numSamples{ 16 };
|
||||
int resolutionLevel{ 1 };
|
||||
int blurRadius{ 4 }; // 0 means no blurring
|
||||
bool ditheringEnabled{ true }; // randomize the distribution of rays per pixel, should always be true
|
||||
bool ditheringEnabled{ true }; // randomize the distribution of taps per pixel, should always be true
|
||||
bool borderingEnabled{ true }; // avoid evaluating information from non existing pixels out of the frame, should always be true
|
||||
bool fetchMipsEnabled{ true }; // fetch taps in sub mips to otpimize cache, should always be true
|
||||
double gpuTime{ 0.0 };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
|
||||
namespace gpu {
|
||||
template <class T> class UniformBuffer : public gpu::BufferView {
|
||||
public:
|
||||
|
||||
static BufferPointer makeBuffer() {
|
||||
T t;
|
||||
return std::make_shared<gpu::Buffer>(sizeof(T), (const gpu::Byte*) &t);
|
||||
}
|
||||
~UniformBuffer<T>() {};
|
||||
UniformBuffer<T>() : gpu::BufferView(makeBuffer()) {}
|
||||
|
||||
const T* operator ->() const { return &get<T>(); }
|
||||
T* operator ->() {
|
||||
return &edit<T>(0);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
class AmbientOcclusionEffect {
|
||||
public:
|
||||
using Inputs = render::VaryingSet3<DeferredFrameTransformPointer, DeferredFramebufferPointer, LinearDepthFramebufferPointer>;
|
||||
using Outputs = render::VaryingSet2<AmbientOcclusionFramebufferPointer, gpu::BufferView>;
|
||||
using Config = AmbientOcclusionEffectConfig;
|
||||
using JobModel = render::Job::Model<AmbientOcclusionEffect, Config>;
|
||||
using JobModel = render::Job::ModelIO<AmbientOcclusionEffect, Inputs, Outputs, Config>;
|
||||
|
||||
AmbientOcclusionEffect();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs);
|
||||
|
||||
float getRadius() const { return _parametersBuffer.get<Parameters>().radiusInfo.x; }
|
||||
float getObscuranceLevel() const { return _parametersBuffer.get<Parameters>().radiusInfo.w; }
|
||||
float getFalloffBias() const { return (float)_parametersBuffer.get<Parameters>().ditheringInfo.z; }
|
||||
float getEdgeSharpness() const { return (float)_parametersBuffer.get<Parameters>().blurInfo.x; }
|
||||
float getBlurDeviation() const { return _parametersBuffer.get<Parameters>().blurInfo.z; }
|
||||
float getNumSpiralTurns() const { return _parametersBuffer.get<Parameters>().sampleInfo.z; }
|
||||
int getNumSamples() const { return (int)_parametersBuffer.get<Parameters>().sampleInfo.x; }
|
||||
int getResolutionLevel() const { return _parametersBuffer.get<Parameters>().resolutionInfo.x; }
|
||||
int getBlurRadius() const { return (int)_parametersBuffer.get<Parameters>().blurInfo.y; }
|
||||
bool isDitheringEnabled() const { return _parametersBuffer.get<Parameters>().ditheringInfo.x; }
|
||||
bool isBorderingEnabled() const { return _parametersBuffer.get<Parameters>().ditheringInfo.w; }
|
||||
|
||||
private:
|
||||
void updateGaussianDistribution();
|
||||
void setDepthInfo(float nearZ, float farZ);
|
||||
|
||||
typedef gpu::BufferView UniformBufferView;
|
||||
|
||||
// Class describing the uniform buffer with the transform info common to the AO shaders
|
||||
// It s changing every frame
|
||||
class FrameTransform {
|
||||
public:
|
||||
// Pixel info is { viemport width height and stereo on off}
|
||||
glm::vec4 pixelInfo;
|
||||
// Depth info is { n.f, f - n, -f}
|
||||
glm::vec4 depthInfo;
|
||||
// Stereo info
|
||||
glm::vec4 stereoInfo { 0.0 };
|
||||
// Mono proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space
|
||||
glm::mat4 projection[2];
|
||||
|
||||
FrameTransform() {}
|
||||
};
|
||||
gpu::BufferView _frameTransformBuffer;
|
||||
|
||||
// Class describing the uniform buffer with all the parameters common to the AO shaders
|
||||
class Parameters {
|
||||
public:
|
||||
// Resolution info
|
||||
glm::vec4 resolutionInfo { -1.0f, 0.0f, 0.0f, 0.0f };
|
||||
glm::vec4 resolutionInfo { -1.0f, 0.0f, 1.0f, 0.0f };
|
||||
// radius info is { R, R^2, 1 / R^6, ObscuranceScale}
|
||||
glm::vec4 radiusInfo{ 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f };
|
||||
// Dithering info
|
||||
|
@ -126,22 +154,92 @@ private:
|
|||
// gaussian distribution coefficients first is the sampling radius (max is 6)
|
||||
const static int GAUSSIAN_COEFS_LENGTH = 8;
|
||||
float _gaussianCoefs[GAUSSIAN_COEFS_LENGTH];
|
||||
|
||||
|
||||
Parameters() {}
|
||||
};
|
||||
gpu::BufferView _parametersBuffer;
|
||||
|
||||
const gpu::PipelinePointer& getPyramidPipeline();
|
||||
int getResolutionLevel() const { return resolutionInfo.x; }
|
||||
float getRadius() const { return radiusInfo.x; }
|
||||
float getPerspectiveScale() const { return resolutionInfo.z; }
|
||||
float getObscuranceLevel() const { return radiusInfo.w; }
|
||||
float getFalloffBias() const { return (float)ditheringInfo.z; }
|
||||
float getEdgeSharpness() const { return (float)blurInfo.x; }
|
||||
float getBlurDeviation() const { return blurInfo.z; }
|
||||
|
||||
float getNumSpiralTurns() const { return sampleInfo.z; }
|
||||
int getNumSamples() const { return (int)sampleInfo.x; }
|
||||
bool isFetchMipsEnabled() const { return sampleInfo.w; }
|
||||
|
||||
int getBlurRadius() const { return (int)blurInfo.y; }
|
||||
bool isDitheringEnabled() const { return ditheringInfo.x; }
|
||||
bool isBorderingEnabled() const { return ditheringInfo.w; }
|
||||
};
|
||||
using ParametersBuffer = gpu::UniformBuffer<Parameters>;
|
||||
|
||||
private:
|
||||
void updateGaussianDistribution();
|
||||
|
||||
ParametersBuffer _parametersBuffer;
|
||||
|
||||
const gpu::PipelinePointer& getOcclusionPipeline();
|
||||
const gpu::PipelinePointer& getHBlurPipeline(); // first
|
||||
const gpu::PipelinePointer& getVBlurPipeline(); // second
|
||||
|
||||
gpu::PipelinePointer _pyramidPipeline;
|
||||
gpu::PipelinePointer _occlusionPipeline;
|
||||
gpu::PipelinePointer _hBlurPipeline;
|
||||
gpu::PipelinePointer _vBlurPipeline;
|
||||
|
||||
AmbientOcclusionFramebufferPointer _framebuffer;
|
||||
|
||||
gpu::RangeTimer _gpuTimer;
|
||||
|
||||
friend class DebugAmbientOcclusion;
|
||||
};
|
||||
|
||||
|
||||
class DebugAmbientOcclusionConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty)
|
||||
Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty)
|
||||
public:
|
||||
DebugAmbientOcclusionConfig() : render::Job::Config(true) {}
|
||||
|
||||
bool showCursorPixel{ false };
|
||||
glm::vec2 debugCursorTexcoord{ 0.5f, 0.5f };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
};
|
||||
|
||||
|
||||
class DebugAmbientOcclusion {
|
||||
public:
|
||||
using Inputs = render::VaryingSet4<DeferredFrameTransformPointer, DeferredFramebufferPointer, LinearDepthFramebufferPointer, AmbientOcclusionEffect::ParametersBuffer>;
|
||||
using Config = DebugAmbientOcclusionConfig;
|
||||
using JobModel = render::Job::ModelI<DebugAmbientOcclusion, Inputs, Config>;
|
||||
|
||||
DebugAmbientOcclusion();
|
||||
|
||||
void configure(const Config& config);
|
||||
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const Inputs& inputs);
|
||||
|
||||
private:
|
||||
|
||||
// Class describing the uniform buffer with all the parameters common to the debug AO shaders
|
||||
class Parameters {
|
||||
public:
|
||||
// Pixel info
|
||||
glm::vec4 pixelInfo { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
Parameters() {}
|
||||
};
|
||||
gpu::UniformBuffer<Parameters> _parametersBuffer;
|
||||
|
||||
const gpu::PipelinePointer& getDebugPipeline();
|
||||
|
||||
gpu::PipelinePointer _debugPipeline;
|
||||
|
||||
bool _showCursorPixel{ false };
|
||||
};
|
||||
|
||||
#endif // hifi_AmbientOcclusionEffect_h
|
||||
|
|
|
@ -202,14 +202,14 @@ static const std::string DEFAULT_DEBUG_SCATTERING_SHADER{
|
|||
|
||||
static const std::string DEFAULT_AMBIENT_OCCLUSION_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(vec3(texture(obscuranceMap, uv).x), 1.0);"
|
||||
" return vec4(vec3(texture(obscuranceMap, uv).xyz), 1.0);"
|
||||
// When drawing color " return vec4(vec3(texture(occlusionMap, uv).xyz), 1.0);"
|
||||
// when drawing normal " return vec4(normalize(texture(occlusionMap, uv).xyz * 2.0 - vec3(1.0)), 1.0);"
|
||||
// when drawing normal" return vec4(normalize(texture(occlusionMap, uv).xyz * 2.0 - vec3(1.0)), 1.0);"
|
||||
" }"
|
||||
};
|
||||
static const std::string DEFAULT_AMBIENT_OCCLUSION_BLURRED_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(vec3(texture(occlusionBlurredMap, uv).x), 1.0);"
|
||||
" return vec4(vec3(texture(occlusionBlurredMap, uv).xyz), 1.0);"
|
||||
" }"
|
||||
};
|
||||
|
||||
|
@ -379,6 +379,7 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren
|
|||
auto& deferredFramebuffer = inputs.get0();
|
||||
auto& linearDepthTarget = inputs.get1();
|
||||
auto& surfaceGeometryFramebuffer = inputs.get2();
|
||||
auto& ambientOcclusionFramebuffer = inputs.get3();
|
||||
|
||||
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
|
||||
batch.enableStereo(false);
|
||||
|
@ -402,29 +403,51 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren
|
|||
|
||||
batch.setPipeline(getPipeline(_mode, first));
|
||||
|
||||
batch.setResourceTexture(Albedo, deferredFramebuffer->getDeferredColorTexture());
|
||||
batch.setResourceTexture(Normal, deferredFramebuffer->getDeferredNormalTexture());
|
||||
batch.setResourceTexture(Specular, deferredFramebuffer->getDeferredSpecularTexture());
|
||||
batch.setResourceTexture(Depth, deferredFramebuffer->getPrimaryDepthTexture());
|
||||
batch.setResourceTexture(Lighting, deferredFramebuffer->getLightingTexture());
|
||||
batch.setResourceTexture(Shadow, lightStage.lights[0]->shadow.framebuffer->getDepthStencilBuffer());
|
||||
batch.setResourceTexture(LinearDepth, linearDepthTarget->getLinearDepthTexture());
|
||||
batch.setResourceTexture(HalfLinearDepth, linearDepthTarget->getHalfLinearDepthTexture());
|
||||
batch.setResourceTexture(HalfNormal, linearDepthTarget->getHalfNormalTexture());
|
||||
|
||||
batch.setResourceTexture(Curvature, surfaceGeometryFramebuffer->getCurvatureTexture());
|
||||
batch.setResourceTexture(DiffusedCurvature, surfaceGeometryFramebuffer->getLowCurvatureTexture());
|
||||
if (DependencyManager::get<DeferredLightingEffect>()->isAmbientOcclusionEnabled()) {
|
||||
batch.setResourceTexture(AmbientOcclusion, framebufferCache->getOcclusionTexture());
|
||||
} else {
|
||||
// need to assign the white texture if ao is off
|
||||
batch.setResourceTexture(AmbientOcclusion, textureCache->getWhiteTexture());
|
||||
}
|
||||
batch.setResourceTexture(AmbientOcclusionBlurred, framebufferCache->getOcclusionBlurredTexture());
|
||||
if (deferredFramebuffer) {
|
||||
batch.setResourceTexture(Albedo, deferredFramebuffer->getDeferredColorTexture());
|
||||
batch.setResourceTexture(Normal, deferredFramebuffer->getDeferredNormalTexture());
|
||||
batch.setResourceTexture(Specular, deferredFramebuffer->getDeferredSpecularTexture());
|
||||
batch.setResourceTexture(Depth, deferredFramebuffer->getPrimaryDepthTexture());
|
||||
batch.setResourceTexture(Lighting, deferredFramebuffer->getLightingTexture());
|
||||
}
|
||||
if (!lightStage.lights.empty()) {
|
||||
batch.setResourceTexture(Shadow, lightStage.lights[0]->shadow.framebuffer->getDepthStencilBuffer());
|
||||
}
|
||||
|
||||
if (linearDepthTarget) {
|
||||
batch.setResourceTexture(LinearDepth, linearDepthTarget->getLinearDepthTexture());
|
||||
batch.setResourceTexture(HalfLinearDepth, linearDepthTarget->getHalfLinearDepthTexture());
|
||||
batch.setResourceTexture(HalfNormal, linearDepthTarget->getHalfNormalTexture());
|
||||
}
|
||||
if (surfaceGeometryFramebuffer) {
|
||||
batch.setResourceTexture(Curvature, surfaceGeometryFramebuffer->getCurvatureTexture());
|
||||
batch.setResourceTexture(DiffusedCurvature, surfaceGeometryFramebuffer->getLowCurvatureTexture());
|
||||
}
|
||||
if (ambientOcclusionFramebuffer) {
|
||||
batch.setResourceTexture(AmbientOcclusion, ambientOcclusionFramebuffer->getOcclusionTexture());
|
||||
batch.setResourceTexture(AmbientOcclusionBlurred, ambientOcclusionFramebuffer->getOcclusionBlurredTexture());
|
||||
}
|
||||
const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
const glm::vec2 bottomLeft(_size.x, _size.y);
|
||||
const glm::vec2 topRight(_size.z, _size.w);
|
||||
geometryBuffer->renderQuad(batch, bottomLeft, topRight, color);
|
||||
|
||||
|
||||
batch.setResourceTexture(Albedo, nullptr);
|
||||
batch.setResourceTexture(Normal, nullptr);
|
||||
batch.setResourceTexture(Specular, nullptr);
|
||||
batch.setResourceTexture(Depth, nullptr);
|
||||
batch.setResourceTexture(Lighting, nullptr);
|
||||
batch.setResourceTexture(Shadow, nullptr);
|
||||
batch.setResourceTexture(LinearDepth, nullptr);
|
||||
batch.setResourceTexture(HalfLinearDepth, nullptr);
|
||||
batch.setResourceTexture(HalfNormal, nullptr);
|
||||
|
||||
batch.setResourceTexture(Curvature, nullptr);
|
||||
batch.setResourceTexture(DiffusedCurvature, nullptr);
|
||||
|
||||
batch.setResourceTexture(AmbientOcclusion, nullptr);
|
||||
batch.setResourceTexture(AmbientOcclusionBlurred, nullptr);
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <render/DrawTask.h>
|
||||
#include "DeferredFramebuffer.h"
|
||||
#include "SurfaceGeometryPass.h"
|
||||
#include "AmbientOcclusionEffect.h"
|
||||
|
||||
class DebugDeferredBufferConfig : public render::Job::Config {
|
||||
Q_OBJECT
|
||||
|
@ -36,7 +37,7 @@ signals:
|
|||
|
||||
class DebugDeferredBuffer {
|
||||
public:
|
||||
using Inputs = render::VaryingSet4<DeferredFramebufferPointer, LinearDepthFramebufferPointer, SurfaceGeometryFramebufferPointer, gpu::FramebufferPointer>;
|
||||
using Inputs = render::VaryingSet4<DeferredFramebufferPointer, LinearDepthFramebufferPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer>;
|
||||
using Config = DebugDeferredBufferConfig;
|
||||
using JobModel = render::Job::ModelI<DebugDeferredBuffer, Inputs, Config>;
|
||||
|
||||
|
|
|
@ -403,7 +403,7 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c
|
|||
const DeferredFramebufferPointer& deferredFramebuffer,
|
||||
const LightingModelPointer& lightingModel,
|
||||
const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer,
|
||||
const gpu::FramebufferPointer& lowCurvatureNormalFramebuffer,
|
||||
const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer,
|
||||
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource) {
|
||||
|
||||
auto args = renderContext->args;
|
||||
|
@ -434,7 +434,7 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c
|
|||
|
||||
// FIXME: Different render modes should have different tasks
|
||||
if (args->_renderMode == RenderArgs::DEFAULT_RENDER_MODE && deferredLightingEffect->isAmbientOcclusionEnabled()) {
|
||||
batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, framebufferCache->getOcclusionTexture());
|
||||
batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, ambientOcclusionFramebuffer->getOcclusionTexture());
|
||||
} else {
|
||||
// need to assign the white texture if ao is off
|
||||
batch.setResourceTexture(DEFERRED_BUFFER_OBSCURANCE_UNIT, textureCache->getWhiteTexture());
|
||||
|
@ -449,9 +449,6 @@ void RenderDeferredSetup::run(const render::SceneContextPointer& sceneContext, c
|
|||
// Subsurface scattering specific
|
||||
if (surfaceGeometryFramebuffer) {
|
||||
batch.setResourceTexture(DEFERRED_BUFFER_CURVATURE_UNIT, surfaceGeometryFramebuffer->getCurvatureTexture());
|
||||
}
|
||||
if (lowCurvatureNormalFramebuffer) {
|
||||
// batch.setResourceTexture(DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT, lowCurvatureNormalFramebuffer->getRenderBuffer(0));
|
||||
batch.setResourceTexture(DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT, surfaceGeometryFramebuffer->getLowCurvatureTexture());
|
||||
}
|
||||
if (subsurfaceScatteringResource) {
|
||||
|
@ -698,7 +695,7 @@ void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderCo
|
|||
auto deferredFramebuffer = inputs.get1();
|
||||
auto lightingModel = inputs.get2();
|
||||
auto surfaceGeometryFramebuffer = inputs.get3();
|
||||
auto lowCurvatureNormalFramebuffer = inputs.get4();
|
||||
auto ssaoFramebuffer = inputs.get4();
|
||||
auto subsurfaceScatteringResource = inputs.get5();
|
||||
auto args = renderContext->args;
|
||||
|
||||
|
@ -706,7 +703,7 @@ void RenderDeferred::run(const SceneContextPointer& sceneContext, const RenderCo
|
|||
_gpuTimer.begin(batch);
|
||||
});
|
||||
|
||||
setupJob.run(sceneContext, renderContext, deferredTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, lowCurvatureNormalFramebuffer, subsurfaceScatteringResource);
|
||||
setupJob.run(sceneContext, renderContext, deferredTransform, deferredFramebuffer, lightingModel, surfaceGeometryFramebuffer, ssaoFramebuffer, subsurfaceScatteringResource);
|
||||
|
||||
lightsJob.run(sceneContext, renderContext, deferredTransform, deferredFramebuffer, lightingModel);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "LightStage.h"
|
||||
#include "SurfaceGeometryPass.h"
|
||||
#include "SubsurfaceScattering.h"
|
||||
#include "AmbientOcclusionEffect.h"
|
||||
|
||||
class RenderArgs;
|
||||
struct LightLocations;
|
||||
|
@ -138,7 +139,7 @@ public:
|
|||
const DeferredFramebufferPointer& deferredFramebuffer,
|
||||
const LightingModelPointer& lightingModel,
|
||||
const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer,
|
||||
const gpu::FramebufferPointer& lowCurvatureNormalFramebuffer,
|
||||
const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer,
|
||||
const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource);
|
||||
};
|
||||
|
||||
|
@ -178,7 +179,7 @@ signals:
|
|||
|
||||
class RenderDeferred {
|
||||
public:
|
||||
using Inputs = render::VaryingSet6 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer, gpu::FramebufferPointer, SubsurfaceScatteringResourcePointer>;
|
||||
using Inputs = render::VaryingSet6 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer, AmbientOcclusionFramebufferPointer, SubsurfaceScatteringResourcePointer>;
|
||||
using Config = RenderDeferredConfig;
|
||||
using JobModel = render::Job::ModelI<RenderDeferred, Inputs, Config>;
|
||||
|
||||
|
|
|
@ -93,6 +93,13 @@ bool isStereo() {
|
|||
float getStereoSideWidth(int resolutionLevel) {
|
||||
return float(int(frameTransform._stereoInfo.y) >> resolutionLevel);
|
||||
}
|
||||
float getStereoSideHeight(int resolutionLevel) {
|
||||
return float(int(frameTransform._pixelInfo.w) >> resolutionLevel);
|
||||
}
|
||||
|
||||
vec2 getSideImageSize(int resolutionLevel) {
|
||||
return vec2(float(int(frameTransform._stereoInfo.y) >> resolutionLevel), float(int(frameTransform._pixelInfo.w) >> resolutionLevel));
|
||||
}
|
||||
|
||||
ivec4 getStereoSideInfo(int xPos, int resolutionLevel) {
|
||||
int sideWidth = int(getStereoSideWidth(resolutionLevel));
|
||||
|
|
|
@ -22,10 +22,6 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) {
|
|||
if (_frameBufferSize != frameBufferSize) {
|
||||
_frameBufferSize = frameBufferSize;
|
||||
_selfieFramebuffer.reset();
|
||||
_occlusionFramebuffer.reset();
|
||||
_occlusionTexture.reset();
|
||||
_occlusionBlurredFramebuffer.reset();
|
||||
_occlusionBlurredTexture.reset();
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_cachedFramebuffers.clear();
|
||||
|
@ -45,33 +41,6 @@ void FramebufferCache::createPrimaryFramebuffer() {
|
|||
_selfieFramebuffer->setRenderBuffer(0, tex);
|
||||
|
||||
auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR);
|
||||
|
||||
resizeAmbientOcclusionBuffers();
|
||||
}
|
||||
|
||||
|
||||
void FramebufferCache::resizeAmbientOcclusionBuffers() {
|
||||
_occlusionFramebuffer.reset();
|
||||
_occlusionTexture.reset();
|
||||
_occlusionBlurredFramebuffer.reset();
|
||||
_occlusionBlurredTexture.reset();
|
||||
|
||||
|
||||
auto width = _frameBufferSize.width() >> _AOResolutionLevel;
|
||||
auto height = _frameBufferSize.height() >> _AOResolutionLevel;
|
||||
auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGB);
|
||||
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
|
||||
// auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format
|
||||
|
||||
_occlusionTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
||||
_occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
_occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture);
|
||||
// _occlusionFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
|
||||
_occlusionBlurredTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
||||
_occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
_occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture);
|
||||
// _occlusionBlurredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
}
|
||||
|
||||
|
||||
|
@ -98,40 +67,3 @@ gpu::FramebufferPointer FramebufferCache::getSelfieFramebuffer() {
|
|||
}
|
||||
return _selfieFramebuffer;
|
||||
}
|
||||
|
||||
void FramebufferCache::setAmbientOcclusionResolutionLevel(int level) {
|
||||
const int MAX_AO_RESOLUTION_LEVEL = 4;
|
||||
level = std::max(0, std::min(level, MAX_AO_RESOLUTION_LEVEL));
|
||||
if (level != _AOResolutionLevel) {
|
||||
_AOResolutionLevel = level;
|
||||
resizeAmbientOcclusionBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer FramebufferCache::getOcclusionFramebuffer() {
|
||||
if (!_occlusionFramebuffer) {
|
||||
resizeAmbientOcclusionBuffers();
|
||||
}
|
||||
return _occlusionFramebuffer;
|
||||
}
|
||||
|
||||
gpu::TexturePointer FramebufferCache::getOcclusionTexture() {
|
||||
if (!_occlusionTexture) {
|
||||
resizeAmbientOcclusionBuffers();
|
||||
}
|
||||
return _occlusionTexture;
|
||||
}
|
||||
|
||||
gpu::FramebufferPointer FramebufferCache::getOcclusionBlurredFramebuffer() {
|
||||
if (!_occlusionBlurredFramebuffer) {
|
||||
resizeAmbientOcclusionBuffers();
|
||||
}
|
||||
return _occlusionBlurredFramebuffer;
|
||||
}
|
||||
|
||||
gpu::TexturePointer FramebufferCache::getOcclusionBlurredTexture() {
|
||||
if (!_occlusionBlurredTexture) {
|
||||
resizeAmbientOcclusionBuffers();
|
||||
}
|
||||
return _occlusionBlurredTexture;
|
||||
}
|
|
@ -27,12 +27,6 @@ public:
|
|||
void setFrameBufferSize(QSize frameBufferSize);
|
||||
const QSize& getFrameBufferSize() const { return _frameBufferSize; }
|
||||
|
||||
void setAmbientOcclusionResolutionLevel(int level);
|
||||
gpu::FramebufferPointer getOcclusionFramebuffer();
|
||||
gpu::TexturePointer getOcclusionTexture();
|
||||
gpu::FramebufferPointer getOcclusionBlurredFramebuffer();
|
||||
gpu::TexturePointer getOcclusionBlurredTexture();
|
||||
|
||||
/// Returns the framebuffer object used to render selfie maps;
|
||||
gpu::FramebufferPointer getSelfieFramebuffer();
|
||||
|
||||
|
@ -50,21 +44,10 @@ private:
|
|||
|
||||
gpu::FramebufferPointer _selfieFramebuffer;
|
||||
|
||||
gpu::FramebufferPointer _occlusionFramebuffer;
|
||||
gpu::TexturePointer _occlusionTexture;
|
||||
|
||||
gpu::FramebufferPointer _occlusionBlurredFramebuffer;
|
||||
gpu::TexturePointer _occlusionBlurredTexture;
|
||||
|
||||
QSize _frameBufferSize{ 100, 100 };
|
||||
int _AOResolutionLevel = 1; // AO perform at half res
|
||||
|
||||
std::mutex _mutex;
|
||||
std::list<gpu::FramebufferPointer> _cachedFramebuffers;
|
||||
|
||||
// Resize/reallocate the buffers used for AO
|
||||
// the size of the AO buffers is scaled by the AOResolutionScale;
|
||||
void resizeAmbientOcclusionBuffers();
|
||||
};
|
||||
|
||||
#endif // hifi_FramebufferCache_h
|
||||
|
|
|
@ -35,7 +35,8 @@
|
|||
#include "simple_vert.h"
|
||||
#include "simple_textured_frag.h"
|
||||
#include "simple_textured_unlit_frag.h"
|
||||
#include "simple_srgb_textured_unlit_no_tex_alpha_frag.h"
|
||||
#include "simple_opaque_web_browser_frag.h"
|
||||
#include "simple_transparent_web_browser_frag.h"
|
||||
#include "glowLine_vert.h"
|
||||
#include "glowLine_geom.h"
|
||||
#include "glowLine_frag.h"
|
||||
|
@ -1763,25 +1764,55 @@ inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) {
|
|||
return a.getRaw() == b.getRaw();
|
||||
}
|
||||
|
||||
void GeometryCache::bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(gpu::Batch& batch) {
|
||||
batch.setPipeline(getSimpleSRGBTexturedUnlitNoTexAlphaPipeline());
|
||||
void GeometryCache::bindOpaqueWebBrowserProgram(gpu::Batch& batch) {
|
||||
batch.setPipeline(getOpaqueWebBrowserProgram());
|
||||
// Set a default normal map
|
||||
batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING,
|
||||
DependencyManager::get<TextureCache>()->getNormalFittingTexture());
|
||||
DependencyManager::get<TextureCache>()->getNormalFittingTexture());
|
||||
}
|
||||
|
||||
gpu::PipelinePointer GeometryCache::getSimpleSRGBTexturedUnlitNoTexAlphaPipeline() {
|
||||
// Compile the shaders, once
|
||||
gpu::PipelinePointer GeometryCache::getOpaqueWebBrowserProgram() {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&]() {
|
||||
auto VS = gpu::Shader::createVertex(std::string(simple_vert));
|
||||
auto PS = gpu::Shader::createPixel(std::string(simple_srgb_textured_unlit_no_tex_alpha_frag));
|
||||
auto PS = gpu::Shader::createPixel(std::string(simple_opaque_web_browser_frag));
|
||||
|
||||
_simpleSRGBTexturedUnlitNoTexAlphaShader = gpu::Shader::createProgram(VS, PS);
|
||||
_simpleOpaqueWebBrowserShader = gpu::Shader::createProgram(VS, PS);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING));
|
||||
gpu::Shader::makeProgram(*_simpleSRGBTexturedUnlitNoTexAlphaShader, slotBindings);
|
||||
gpu::Shader::makeProgram(*_simpleOpaqueWebBrowserShader, slotBindings);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setCullMode(gpu::State::CULL_NONE);
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(false,
|
||||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
||||
_simpleOpaqueWebBrowserPipeline = gpu::Pipeline::create(_simpleOpaqueWebBrowserShader, state);
|
||||
});
|
||||
|
||||
return _simpleOpaqueWebBrowserPipeline;
|
||||
}
|
||||
|
||||
void GeometryCache::bindTransparentWebBrowserProgram(gpu::Batch& batch) {
|
||||
batch.setPipeline(getTransparentWebBrowserProgram());
|
||||
// Set a default normal map
|
||||
batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING,
|
||||
DependencyManager::get<TextureCache>()->getNormalFittingTexture());
|
||||
}
|
||||
|
||||
gpu::PipelinePointer GeometryCache::getTransparentWebBrowserProgram() {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&]() {
|
||||
auto VS = gpu::Shader::createVertex(std::string(simple_vert));
|
||||
auto PS = gpu::Shader::createPixel(std::string(simple_transparent_web_browser_frag));
|
||||
|
||||
_simpleTransparentWebBrowserShader = gpu::Shader::createProgram(VS, PS);
|
||||
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), render::ShapePipeline::Slot::MAP::NORMAL_FITTING));
|
||||
gpu::Shader::makeProgram(*_simpleTransparentWebBrowserShader, slotBindings);
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setCullMode(gpu::State::CULL_NONE);
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
|
@ -1789,10 +1820,10 @@ gpu::PipelinePointer GeometryCache::getSimpleSRGBTexturedUnlitNoTexAlphaPipeline
|
|||
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
|
||||
gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
|
||||
_simpleSRGBTexturedUnlitNoTexAlphaPipeline = gpu::Pipeline::create(_simpleSRGBTexturedUnlitNoTexAlphaShader, state);
|
||||
_simpleTransparentWebBrowserPipeline = gpu::Pipeline::create(_simpleTransparentWebBrowserShader, state);
|
||||
});
|
||||
|
||||
return _simpleSRGBTexturedUnlitNoTexAlphaPipeline;
|
||||
return _simpleTransparentWebBrowserPipeline;
|
||||
}
|
||||
|
||||
void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) {
|
||||
|
|
|
@ -149,8 +149,7 @@ public:
|
|||
|
||||
int allocateID() { return _nextID++; }
|
||||
static const int UNKNOWN_ID;
|
||||
|
||||
|
||||
|
||||
// Bind the pipeline and get the state to render static geometry
|
||||
void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool transparent = false, bool culled = true,
|
||||
bool unlit = false, bool depthBias = false);
|
||||
|
@ -158,8 +157,11 @@ public:
|
|||
gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool culled = true,
|
||||
bool unlit = false, bool depthBias = false);
|
||||
|
||||
void bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(gpu::Batch& batch);
|
||||
gpu::PipelinePointer getSimpleSRGBTexturedUnlitNoTexAlphaPipeline();
|
||||
void bindOpaqueWebBrowserProgram(gpu::Batch& batch);
|
||||
gpu::PipelinePointer getOpaqueWebBrowserProgram();
|
||||
|
||||
void bindTransparentWebBrowserProgram(gpu::Batch& batch);
|
||||
gpu::PipelinePointer getTransparentWebBrowserProgram();
|
||||
|
||||
render::ShapePipelinePointer getOpaqueShapePipeline() { return GeometryCache::_simpleOpaquePipeline; }
|
||||
render::ShapePipelinePointer getTransparentShapePipeline() { return GeometryCache::_simpleTransparentPipeline; }
|
||||
|
@ -423,9 +425,11 @@ private:
|
|||
gpu::PipelinePointer _glowLinePipeline;
|
||||
QHash<SimpleProgramKey, gpu::PipelinePointer> _simplePrograms;
|
||||
|
||||
gpu::ShaderPointer _simpleSRGBTexturedUnlitNoTexAlphaShader;
|
||||
gpu::PipelinePointer _simpleSRGBTexturedUnlitNoTexAlphaPipeline;
|
||||
gpu::ShaderPointer _simpleOpaqueWebBrowserShader;
|
||||
gpu::PipelinePointer _simpleOpaqueWebBrowserPipeline;
|
||||
|
||||
gpu::ShaderPointer _simpleTransparentWebBrowserShader;
|
||||
gpu::PipelinePointer _simpleTransparentWebBrowserPipeline;
|
||||
};
|
||||
|
||||
#endif // hifi_GeometryCache_h
|
||||
|
|
|
@ -46,11 +46,9 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr
|
|||
}
|
||||
}
|
||||
|
||||
MeshPartPayload::MeshPartPayload(const std::shared_ptr<const model::Mesh>& mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform) {
|
||||
|
||||
MeshPartPayload::MeshPartPayload(const std::shared_ptr<const model::Mesh>& mesh, int partIndex, model::MaterialPointer material) {
|
||||
updateMeshPart(mesh, partIndex);
|
||||
updateMaterial(material);
|
||||
updateTransform(transform, offsetTransform);
|
||||
}
|
||||
|
||||
void MeshPartPayload::updateMeshPart(const std::shared_ptr<const model::Mesh>& drawMesh, int partIndex) {
|
||||
|
@ -414,8 +412,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const {
|
|||
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
|
||||
// to false to rebuild out mesh groups.
|
||||
if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) {
|
||||
_model->_meshGroupsKnown = false; // regenerate these lists next time around.
|
||||
_model->_readyWhenAdded = false; // in case any of our users are using scenes
|
||||
_model->_needsFixupInScene = true; // trigger remove/add cycle
|
||||
_model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
|
||||
return ShapeKey::Builder::invalid();
|
||||
}
|
||||
|
@ -533,10 +530,21 @@ void ModelMeshPartPayload::startFade() {
|
|||
void ModelMeshPartPayload::render(RenderArgs* args) const {
|
||||
PerformanceTimer perfTimer("ModelMeshPartPayload::render");
|
||||
|
||||
if (!_model->_readyWhenAdded || !_model->_isVisible || !_hasStartedFade) {
|
||||
if (!_model->addedToScene() || !_model->isVisible()) {
|
||||
return; // bail asap
|
||||
}
|
||||
|
||||
// If we didn't start the fade in, check if we are ready to now....
|
||||
if (!_hasStartedFade && _model->isLoaded() && _model->getGeometry()->areTexturesLoaded()) {
|
||||
const_cast<ModelMeshPartPayload&>(*this).startFade();
|
||||
}
|
||||
|
||||
// If we still didn't start the fade in, bail
|
||||
if (!_hasStartedFade) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// When an individual mesh parts like this finishes its fade, we will mark the Model as
|
||||
// having render items that need updating
|
||||
bool nextIsFading = _isFading ? isStillFading() : false;
|
||||
|
|
|
@ -26,7 +26,7 @@ class Model;
|
|||
class MeshPartPayload {
|
||||
public:
|
||||
MeshPartPayload() {}
|
||||
MeshPartPayload(const std::shared_ptr<const model::Mesh>& mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform);
|
||||
MeshPartPayload(const std::shared_ptr<const model::Mesh>& mesh, int partIndex, model::MaterialPointer material);
|
||||
|
||||
typedef render::Payload<MeshPartPayload> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
|
|
|
@ -37,9 +37,9 @@ float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f;
|
|||
#define HTTP_INVALID_COM "http://invalid.com"
|
||||
|
||||
const int NUM_COLLISION_HULL_COLORS = 24;
|
||||
std::vector<model::MaterialPointer> _collisionHullMaterials;
|
||||
std::vector<model::MaterialPointer> _collisionMaterials;
|
||||
|
||||
void initCollisionHullMaterials() {
|
||||
void initCollisionMaterials() {
|
||||
// generates bright colors in red, green, blue, yellow, magenta, and cyan spectrums
|
||||
// (no browns, greys, or dark shades)
|
||||
float component[NUM_COLLISION_HULL_COLORS] = {
|
||||
|
@ -50,7 +50,7 @@ void initCollisionHullMaterials() {
|
|||
1.0f, 1.0f, 1.0f, 1.0f,
|
||||
0.8f, 0.6f, 0.4f, 0.2f
|
||||
};
|
||||
_collisionHullMaterials.reserve(NUM_COLLISION_HULL_COLORS);
|
||||
_collisionMaterials.reserve(NUM_COLLISION_HULL_COLORS);
|
||||
|
||||
// each component gets the same cuve
|
||||
// but offset by a multiple of one third the full width
|
||||
|
@ -72,7 +72,7 @@ void initCollisionHullMaterials() {
|
|||
material->setAlbedo(glm::vec3(red, green, blue));
|
||||
material->setMetallic(0.02f);
|
||||
material->setRoughness(0.5f);
|
||||
_collisionHullMaterials.push_back(material);
|
||||
_collisionMaterials.push_back(material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,6 @@ Model::Model(RigPointer rig, QObject* parent) :
|
|||
_renderGeometry(),
|
||||
_collisionGeometry(),
|
||||
_renderWatcher(_renderGeometry),
|
||||
_collisionWatcher(_collisionGeometry),
|
||||
_translation(0.0f),
|
||||
_rotation(),
|
||||
_scale(1.0f, 1.0f, 1.0f),
|
||||
|
@ -100,7 +99,6 @@ Model::Model(RigPointer rig, QObject* parent) :
|
|||
_calculatedMeshPartBoxesValid(false),
|
||||
_calculatedMeshBoxesValid(false),
|
||||
_calculatedMeshTrianglesValid(false),
|
||||
_meshGroupsKnown(false),
|
||||
_isWireframe(false),
|
||||
_rig(rig)
|
||||
{
|
||||
|
@ -112,7 +110,6 @@ Model::Model(RigPointer rig, QObject* parent) :
|
|||
setSnapModelToRegistrationPoint(true, glm::vec3(0.5f));
|
||||
|
||||
connect(&_renderWatcher, &GeometryResourceWatcher::finished, this, &Model::loadURLFinished);
|
||||
connect(&_collisionWatcher, &GeometryResourceWatcher::finished, this, &Model::loadCollisionModelURLFinished);
|
||||
}
|
||||
|
||||
Model::~Model() {
|
||||
|
@ -122,18 +119,11 @@ Model::~Model() {
|
|||
AbstractViewStateInterface* Model::_viewState = NULL;
|
||||
|
||||
bool Model::needsFixupInScene() const {
|
||||
if (readyToAddToScene()) {
|
||||
if (_needsUpdateTextures && _renderGeometry->areTexturesLoaded()) {
|
||||
_needsUpdateTextures = false;
|
||||
return true;
|
||||
}
|
||||
if (!_readyWhenAdded) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return (_needsFixupInScene || !_addedToScene) && !_needsReload && isLoaded();
|
||||
}
|
||||
|
||||
// TODO?: should we combine translation and rotation into single method to avoid double-work?
|
||||
// (figure out where we call these)
|
||||
void Model::setTranslation(const glm::vec3& translation) {
|
||||
_translation = translation;
|
||||
updateRenderItems();
|
||||
|
@ -172,7 +162,15 @@ void Model::setOffset(const glm::vec3& offset) {
|
|||
}
|
||||
|
||||
void Model::updateRenderItems() {
|
||||
if (!_addedToScene) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 scale = getScale();
|
||||
if (_collisionGeometry) {
|
||||
// _collisionGeometry is already scaled
|
||||
scale = glm::vec3(1.0f);
|
||||
}
|
||||
_needsUpdateClusterMatrices = true;
|
||||
_renderItemsNeedUpdate = false;
|
||||
|
||||
|
@ -180,7 +178,7 @@ void Model::updateRenderItems() {
|
|||
// the application will ensure only the last lambda is actually invoked.
|
||||
void* key = (void*)this;
|
||||
std::weak_ptr<Model> weakSelf = shared_from_this();
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf]() {
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [weakSelf, scale]() {
|
||||
|
||||
// do nothing, if the model has already been destroyed.
|
||||
auto self = weakSelf.lock();
|
||||
|
@ -191,7 +189,7 @@ void Model::updateRenderItems() {
|
|||
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
|
||||
|
||||
Transform modelTransform;
|
||||
modelTransform.setScale(self->_scale);
|
||||
modelTransform.setScale(scale);
|
||||
modelTransform.setTranslation(self->_translation);
|
||||
modelTransform.setRotation(self->_rotation);
|
||||
|
||||
|
@ -203,10 +201,6 @@ void Model::updateRenderItems() {
|
|||
modelMeshOffset.postTranslate(self->_offset);
|
||||
}
|
||||
|
||||
// only apply offset only, collision mesh does not share the same unit scale as the FBX file's mesh.
|
||||
Transform collisionMeshOffset;
|
||||
collisionMeshOffset.postTranslate(self->_offset);
|
||||
|
||||
uint32_t deleteGeometryCounter = self->_deleteGeometryCounter;
|
||||
|
||||
render::PendingChanges pendingChanges;
|
||||
|
@ -227,6 +221,9 @@ void Model::updateRenderItems() {
|
|||
});
|
||||
}
|
||||
|
||||
// collision mesh does not share the same unit scale as the FBX file's mesh: only apply offset
|
||||
Transform collisionMeshOffset;
|
||||
collisionMeshOffset.setIdentity();
|
||||
foreach (auto itemID, self->_collisionRenderItems.keys()) {
|
||||
pendingChanges.updateItem<MeshPartPayload>(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) {
|
||||
// update the model transform for this render item.
|
||||
|
@ -574,8 +571,8 @@ void Model::renderSetup(RenderArgs* args) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!_meshGroupsKnown && isLoaded()) {
|
||||
segregateMeshGroups();
|
||||
if (!_addedToScene && isLoaded()) {
|
||||
createRenderItemSet();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -596,43 +593,46 @@ void Model::setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scen
|
|||
|
||||
bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges,
|
||||
render::Item::Status::Getters& statusGetters,
|
||||
bool showCollisionHull) {
|
||||
if ((!_meshGroupsKnown || showCollisionHull != _showCollisionHull) && isLoaded()) {
|
||||
_showCollisionHull = showCollisionHull;
|
||||
segregateMeshGroups();
|
||||
render::Item::Status::Getters& statusGetters) {
|
||||
bool readyToRender = _collisionGeometry || isLoaded();
|
||||
if (!_addedToScene && readyToRender) {
|
||||
createRenderItemSet();
|
||||
}
|
||||
|
||||
bool somethingAdded = false;
|
||||
|
||||
if (_modelMeshRenderItems.empty()) {
|
||||
foreach (auto renderItem, _modelMeshRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
||||
if (statusGetters.size()) {
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
if (_collisionGeometry) {
|
||||
if (_collisionRenderItems.empty()) {
|
||||
foreach (auto renderItem, _collisionRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
if (statusGetters.size()) {
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
}
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
_collisionRenderItems.insert(item, renderPayload);
|
||||
}
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
_modelMeshRenderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
somethingAdded = !_collisionRenderItems.empty();
|
||||
}
|
||||
}
|
||||
if (_collisionRenderItems.empty()) {
|
||||
foreach (auto renderItem, _collisionRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
if (statusGetters.size()) {
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
} else {
|
||||
if (_modelMeshRenderItems.empty()) {
|
||||
foreach (auto renderItem, _modelMeshRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
||||
if (statusGetters.size()) {
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
}
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
_modelMeshRenderItems.insert(item, renderPayload);
|
||||
}
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
_collisionRenderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
somethingAdded = !_modelMeshRenderItems.empty();
|
||||
}
|
||||
}
|
||||
|
||||
updateRenderItems();
|
||||
|
||||
_readyWhenAdded = readyToAddToScene();
|
||||
if (somethingAdded) {
|
||||
_addedToScene = true;
|
||||
updateRenderItems();
|
||||
_needsFixupInScene = false;
|
||||
}
|
||||
|
||||
return somethingAdded;
|
||||
}
|
||||
|
@ -643,13 +643,13 @@ void Model::removeFromScene(std::shared_ptr<render::Scene> scene, render::Pendin
|
|||
}
|
||||
_modelMeshRenderItems.clear();
|
||||
_modelMeshRenderItemsSet.clear();
|
||||
|
||||
foreach (auto item, _collisionRenderItems.keys()) {
|
||||
pendingChanges.removeItem(item);
|
||||
}
|
||||
_collisionRenderItems.clear();
|
||||
_collisionRenderItemsSet.clear();
|
||||
_meshGroupsKnown = false;
|
||||
_readyWhenAdded = false;
|
||||
_addedToScene = false;
|
||||
}
|
||||
|
||||
void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
|
||||
|
@ -804,6 +804,7 @@ int Model::getLastFreeJointIndex(int jointIndex) const {
|
|||
void Model::setTextures(const QVariantMap& textures) {
|
||||
if (isLoaded()) {
|
||||
_needsUpdateTextures = true;
|
||||
_needsFixupInScene = true;
|
||||
_renderGeometry->setTextures(textures);
|
||||
}
|
||||
}
|
||||
|
@ -825,8 +826,8 @@ void Model::setURL(const QUrl& url) {
|
|||
|
||||
_needsReload = true;
|
||||
_needsUpdateTextures = true;
|
||||
_meshGroupsKnown = false;
|
||||
_visualGeometryRequestFailed = false;
|
||||
_needsFixupInScene = true;
|
||||
invalidCalculatedMeshBoxes();
|
||||
deleteGeometry();
|
||||
|
||||
|
@ -843,23 +844,6 @@ void Model::loadURLFinished(bool success) {
|
|||
emit setURLFinished(success);
|
||||
}
|
||||
|
||||
void Model::setCollisionModelURL(const QUrl& url) {
|
||||
if (_collisionUrl == url && _collisionWatcher.getURL() == url) {
|
||||
return;
|
||||
}
|
||||
_collisionUrl = url;
|
||||
_collisionGeometryRequestFailed = false;
|
||||
_collisionWatcher.setResource(DependencyManager::get<ModelCache>()->getGeometryResource(url));
|
||||
}
|
||||
|
||||
void Model::loadCollisionModelURLFinished(bool success) {
|
||||
if (!success) {
|
||||
_collisionGeometryRequestFailed = true;
|
||||
}
|
||||
|
||||
emit setCollisionModelURLFinished(success);
|
||||
}
|
||||
|
||||
bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const {
|
||||
return _rig->getJointPositionInWorldFrame(jointIndex, position, _translation, _rotation);
|
||||
}
|
||||
|
@ -1236,21 +1220,21 @@ AABox Model::getRenderableMeshBound() const {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::segregateMeshGroups() {
|
||||
Geometry::Pointer geometry;
|
||||
bool showingCollisionHull = false;
|
||||
if (_showCollisionHull && _collisionGeometry) {
|
||||
if (isCollisionLoaded()) {
|
||||
geometry = _collisionGeometry;
|
||||
showingCollisionHull = true;
|
||||
} else {
|
||||
return;
|
||||
void Model::createRenderItemSet() {
|
||||
if (_collisionGeometry) {
|
||||
if (_collisionRenderItemsSet.empty()) {
|
||||
createCollisionRenderItemSet();
|
||||
}
|
||||
} else {
|
||||
assert(isLoaded());
|
||||
geometry = _renderGeometry;
|
||||
if (_modelMeshRenderItemsSet.empty()) {
|
||||
createVisibleRenderItemSet();
|
||||
}
|
||||
}
|
||||
const auto& meshes = geometry->getMeshes();
|
||||
};
|
||||
|
||||
void Model::createVisibleRenderItemSet() {
|
||||
assert(isLoaded());
|
||||
const auto& meshes = _renderGeometry->getMeshes();
|
||||
|
||||
// all of our mesh vectors must match in size
|
||||
if ((int)meshes.size() != _meshStates.size()) {
|
||||
|
@ -1259,13 +1243,9 @@ void Model::segregateMeshGroups() {
|
|||
}
|
||||
|
||||
// We should not have any existing renderItems if we enter this section of code
|
||||
Q_ASSERT(_modelMeshRenderItems.isEmpty());
|
||||
Q_ASSERT(_modelMeshRenderItemsSet.isEmpty());
|
||||
Q_ASSERT(_collisionRenderItems.isEmpty());
|
||||
Q_ASSERT(_collisionRenderItemsSet.isEmpty());
|
||||
|
||||
_modelMeshRenderItemsSet.clear();
|
||||
_collisionRenderItemsSet.clear();
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_translation);
|
||||
|
@ -1280,60 +1260,117 @@ void Model::segregateMeshGroups() {
|
|||
uint32_t numMeshes = (uint32_t)meshes.size();
|
||||
for (uint32_t i = 0; i < numMeshes; i++) {
|
||||
const auto& mesh = meshes.at(i);
|
||||
if (mesh) {
|
||||
if (!mesh) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create the render payloads
|
||||
int numParts = (int)mesh->getNumParts();
|
||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||
if (showingCollisionHull) {
|
||||
if (_collisionHullMaterials.empty()) {
|
||||
initCollisionHullMaterials();
|
||||
}
|
||||
_collisionRenderItemsSet << std::make_shared<MeshPartPayload>(mesh, partIndex, _collisionHullMaterials[partIndex % NUM_COLLISION_HULL_COLORS], transform, offset);
|
||||
} else {
|
||||
_modelMeshRenderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
|
||||
}
|
||||
|
||||
shapeID++;
|
||||
}
|
||||
// Create the render payloads
|
||||
int numParts = (int)mesh->getNumParts();
|
||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||
_modelMeshRenderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
|
||||
shapeID++;
|
||||
}
|
||||
}
|
||||
_meshGroupsKnown = true;
|
||||
}
|
||||
|
||||
void Model::createCollisionRenderItemSet() {
|
||||
assert((bool)_collisionGeometry);
|
||||
if (_collisionMaterials.empty()) {
|
||||
initCollisionMaterials();
|
||||
}
|
||||
|
||||
const auto& meshes = _collisionGeometry->getMeshes();
|
||||
|
||||
// We should not have any existing renderItems if we enter this section of code
|
||||
Q_ASSERT(_collisionRenderItemsSet.isEmpty());
|
||||
|
||||
Transform identity;
|
||||
identity.setIdentity();
|
||||
Transform offset;
|
||||
offset.postTranslate(_offset);
|
||||
|
||||
// Run through all of the meshes, and place them into their segregated, but unsorted buckets
|
||||
uint32_t numMeshes = (uint32_t)meshes.size();
|
||||
for (uint32_t i = 0; i < numMeshes; i++) {
|
||||
const auto& mesh = meshes.at(i);
|
||||
if (!mesh) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create the render payloads
|
||||
int numParts = (int)mesh->getNumParts();
|
||||
for (int partIndex = 0; partIndex < numParts; partIndex++) {
|
||||
model::MaterialPointer& material = _collisionMaterials[partIndex % NUM_COLLISION_HULL_COLORS];
|
||||
auto payload = std::make_shared<MeshPartPayload>(mesh, partIndex, material);
|
||||
payload->updateTransform(identity, offset);
|
||||
_collisionRenderItemsSet << payload;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Model::isRenderable() const {
|
||||
return !_meshStates.isEmpty() || (isLoaded() && _renderGeometry->getMeshes().empty());
|
||||
}
|
||||
|
||||
bool Model::initWhenReady(render::ScenePointer scene) {
|
||||
if (isActive() && isRenderable() && !_meshGroupsKnown && isLoaded()) {
|
||||
segregateMeshGroups();
|
||||
// NOTE: this only called by SkeletonModel
|
||||
if (_addedToScene || !isRenderable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
render::PendingChanges pendingChanges;
|
||||
createRenderItemSet();
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(_translation);
|
||||
transform.setRotation(_rotation);
|
||||
render::PendingChanges pendingChanges;
|
||||
|
||||
Transform offset;
|
||||
offset.setScale(_scale);
|
||||
offset.postTranslate(_offset);
|
||||
|
||||
foreach (auto renderItem, _modelMeshRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
||||
_modelMeshRenderItems.insert(item, renderPayload);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
}
|
||||
bool addedPendingChanges = false;
|
||||
if (_collisionGeometry) {
|
||||
foreach (auto renderItem, _collisionRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
_collisionRenderItems.insert(item, renderPayload);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
}
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
updateRenderItems();
|
||||
|
||||
_readyWhenAdded = true;
|
||||
return true;
|
||||
addedPendingChanges = !_collisionRenderItems.empty();
|
||||
} else {
|
||||
foreach (auto renderItem, _modelMeshRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
||||
_modelMeshRenderItems.insert(item, renderPayload);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
}
|
||||
addedPendingChanges = !_modelMeshRenderItems.empty();
|
||||
}
|
||||
return false;
|
||||
_addedToScene = addedPendingChanges;
|
||||
if (addedPendingChanges) {
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
// NOTE: updateRender items enqueues identical pendingChanges (using a lambda)
|
||||
// so it looks like we're doing double work here, but I don't want to remove the call
|
||||
// for fear there is some side effect we'll miss. -- Andrew 2016.07.21
|
||||
// TODO: figure out if we really need this call to updateRenderItems() or not.
|
||||
updateRenderItems();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class CollisionRenderGeometry : public Geometry {
|
||||
public:
|
||||
CollisionRenderGeometry(model::MeshPointer mesh) {
|
||||
_fbxGeometry = std::make_shared<FBXGeometry>();
|
||||
std::shared_ptr<GeometryMeshes> meshes = std::make_shared<GeometryMeshes>();
|
||||
meshes->push_back(mesh);
|
||||
_meshes = meshes;
|
||||
_meshParts = std::shared_ptr<const GeometryMeshParts>();
|
||||
}
|
||||
};
|
||||
|
||||
void Model::setCollisionMesh(model::MeshPointer mesh) {
|
||||
if (mesh) {
|
||||
_collisionGeometry = std::make_shared<CollisionRenderGeometry>(mesh);
|
||||
} else {
|
||||
_collisionGeometry.reset();
|
||||
}
|
||||
_needsFixupInScene = true;
|
||||
}
|
||||
|
||||
ModelBlender::ModelBlender() :
|
||||
|
|
|
@ -81,24 +81,20 @@ public:
|
|||
// new Scene/Engine rendering support
|
||||
void setVisibleInScene(bool newValue, std::shared_ptr<render::Scene> scene);
|
||||
bool needsFixupInScene() const;
|
||||
bool readyToAddToScene(RenderArgs* renderArgs = nullptr) const {
|
||||
return !_needsReload && isRenderable() && isActive();
|
||||
}
|
||||
|
||||
bool needsReload() const { return _needsReload; }
|
||||
bool initWhenReady(render::ScenePointer scene);
|
||||
bool addToScene(std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges,
|
||||
bool showCollisionHull = false) {
|
||||
render::PendingChanges& pendingChanges) {
|
||||
auto getters = render::Item::Status::Getters(0);
|
||||
return addToScene(scene, pendingChanges, getters, showCollisionHull);
|
||||
return addToScene(scene, pendingChanges, getters);
|
||||
}
|
||||
bool addToScene(std::shared_ptr<render::Scene> scene,
|
||||
render::PendingChanges& pendingChanges,
|
||||
render::Item::Status::Getters& statusGetters,
|
||||
bool showCollisionHull = false);
|
||||
render::Item::Status::Getters& statusGetters);
|
||||
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
void renderSetup(RenderArgs* args);
|
||||
bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _renderGeometry->getMeshes().empty()); }
|
||||
bool isRenderable() const;
|
||||
|
||||
bool isVisible() const { return _isVisible; }
|
||||
|
||||
|
@ -114,7 +110,6 @@ public:
|
|||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
|
||||
|
||||
bool isLoaded() const { return (bool)_renderGeometry; }
|
||||
bool isCollisionLoaded() const { return (bool)_collisionGeometry; }
|
||||
|
||||
void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; }
|
||||
bool isWireframe() const { return _isWireframe; }
|
||||
|
@ -141,13 +136,6 @@ public:
|
|||
/// Provided as a convenience, will crash if !isLoaded()
|
||||
// And so that getGeometry() isn't chained everywhere
|
||||
const FBXGeometry& getFBXGeometry() const { assert(isLoaded()); return _renderGeometry->getFBXGeometry(); }
|
||||
/// Provided as a convenience, will crash if !isCollisionLoaded()
|
||||
const FBXGeometry& getCollisionFBXGeometry() const { assert(isCollisionLoaded()); return _collisionGeometry->getFBXGeometry(); }
|
||||
|
||||
// Set the model to use for collisions.
|
||||
// Should only be called from the model's rendering thread to avoid access violations of changed geometry.
|
||||
Q_INVOKABLE void setCollisionModelURL(const QUrl& url);
|
||||
const QUrl& getCollisionURL() const { return _collisionUrl; }
|
||||
|
||||
bool isActive() const { return isLoaded(); }
|
||||
|
||||
|
@ -185,6 +173,7 @@ public:
|
|||
bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const;
|
||||
bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const;
|
||||
bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const;
|
||||
|
||||
/// \param jointIndex index of joint in model structure
|
||||
/// \param rotation[out] rotation of joint in model-frame
|
||||
/// \return true if joint exists
|
||||
|
@ -239,18 +228,19 @@ public:
|
|||
|
||||
// returns 'true' if needs fullUpdate after geometry change
|
||||
bool updateGeometry();
|
||||
void setCollisionMesh(model::MeshPointer mesh);
|
||||
|
||||
void setLoadingPriority(float priority) { _loadingPriority = priority; }
|
||||
|
||||
public slots:
|
||||
void loadURLFinished(bool success);
|
||||
void loadCollisionModelURLFinished(bool success);
|
||||
|
||||
signals:
|
||||
void setURLFinished(bool success);
|
||||
void setCollisionModelURLFinished(bool success);
|
||||
|
||||
protected:
|
||||
bool addedToScene() const { return _addedToScene; }
|
||||
|
||||
void setPupilDilation(float dilation) { _pupilDilation = dilation; }
|
||||
float getPupilDilation() const { return _pupilDilation; }
|
||||
|
@ -282,10 +272,9 @@ protected:
|
|||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||
|
||||
Geometry::Pointer _renderGeometry; // only ever set by its watcher
|
||||
Geometry::Pointer _collisionGeometry; // only ever set by its watcher
|
||||
Geometry::Pointer _collisionGeometry;
|
||||
|
||||
GeometryResourceWatcher _renderWatcher;
|
||||
GeometryResourceWatcher _collisionWatcher;
|
||||
|
||||
glm::vec3 _translation;
|
||||
glm::quat _rotation;
|
||||
|
@ -353,7 +342,6 @@ protected:
|
|||
QVector<float> _blendshapeCoefficients;
|
||||
|
||||
QUrl _url;
|
||||
QUrl _collisionUrl;
|
||||
bool _isVisible;
|
||||
|
||||
gpu::Buffers _blendedVertexBuffers;
|
||||
|
@ -376,10 +364,10 @@ protected:
|
|||
|
||||
void recalculateMeshBoxes(bool pickAgainstTriangles = false);
|
||||
|
||||
void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes
|
||||
static model::MaterialPointer _collisionHullMaterial;
|
||||
void createRenderItemSet();
|
||||
void createVisibleRenderItemSet();
|
||||
void createCollisionRenderItemSet();
|
||||
|
||||
bool _meshGroupsKnown;
|
||||
bool _isWireframe;
|
||||
|
||||
|
||||
|
@ -396,10 +384,10 @@ protected:
|
|||
QSet<std::shared_ptr<ModelMeshPartPayload>> _modelMeshRenderItemsSet;
|
||||
QMap<render::ItemID, render::PayloadPointer> _modelMeshRenderItems;
|
||||
|
||||
bool _readyWhenAdded { false };
|
||||
bool _addedToScene { false }; // has been added to scene
|
||||
bool _needsFixupInScene { true }; // needs to be removed/re-added to scene
|
||||
bool _needsReload { true };
|
||||
bool _needsUpdateClusterMatrices { true };
|
||||
bool _showCollisionHull { false };
|
||||
mutable bool _needsUpdateTextures { true };
|
||||
|
||||
friend class ModelMeshPartPayload;
|
||||
|
|
|
@ -124,10 +124,6 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
|
|||
const auto linearDepthPassInputs = LinearDepthPass::Inputs(deferredFrameTransform, deferredFramebuffer).hasVarying();
|
||||
const auto linearDepthPassOutputs = addJob<LinearDepthPass>("LinearDepth", linearDepthPassInputs);
|
||||
const auto linearDepthTarget = linearDepthPassOutputs.getN<LinearDepthPass::Outputs>(0);
|
||||
const auto linearDepthTexture = linearDepthPassOutputs.getN<LinearDepthPass::Outputs>(2);
|
||||
const auto halfLinearDepthTexture = linearDepthPassOutputs.getN<LinearDepthPass::Outputs>(3);
|
||||
const auto halfNormalTexture = linearDepthPassOutputs.getN<LinearDepthPass::Outputs>(4);
|
||||
|
||||
|
||||
// Curvature pass
|
||||
const auto surfaceGeometryPassInputs = SurfaceGeometryPass::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget).hasVarying();
|
||||
|
@ -141,14 +137,17 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
|
|||
const auto scatteringResource = addJob<SubsurfaceScattering>("Scattering");
|
||||
|
||||
// AO job
|
||||
addJob<AmbientOcclusionEffect>("AmbientOcclusion");
|
||||
const auto ambientOcclusionInputs = AmbientOcclusionEffect::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget).hasVarying();
|
||||
const auto ambientOcclusionOutputs = addJob<AmbientOcclusionEffect>("AmbientOcclusion", ambientOcclusionInputs);
|
||||
const auto ambientOcclusionFramebuffer = ambientOcclusionOutputs.getN<AmbientOcclusionEffect::Outputs>(0);
|
||||
const auto ambientOcclusionUniforms = ambientOcclusionOutputs.getN<AmbientOcclusionEffect::Outputs>(1);
|
||||
|
||||
// Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now.
|
||||
addJob<DrawLight>("DrawLight", lights);
|
||||
|
||||
const auto deferredLightingInputs = RenderDeferred::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel,
|
||||
surfaceGeometryFramebuffer, lowCurvatureNormalFramebuffer, scatteringResource).hasVarying();
|
||||
|
||||
surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, scatteringResource).hasVarying();
|
||||
|
||||
// DeferredBuffer is complete, now let's shade it into the LightingBuffer
|
||||
addJob<RenderDeferred>("RenderDeferred", deferredLightingInputs);
|
||||
|
||||
|
@ -175,11 +174,15 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) {
|
|||
|
||||
// Debugging stages
|
||||
{
|
||||
// Debugging Deferred buffer job
|
||||
const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer));
|
||||
addJob<DebugDeferredBuffer>("DebugDeferredBuffer", debugFramebuffers);
|
||||
|
||||
addJob<DebugSubsurfaceScattering>("DebugScattering", deferredLightingInputs);
|
||||
|
||||
// Debugging Deferred buffer job
|
||||
const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, lowCurvatureNormalFramebuffer));
|
||||
addJob<DebugDeferredBuffer>("DebugDeferredBuffer", debugFramebuffers);
|
||||
const auto debugAmbientOcclusionInputs = DebugAmbientOcclusion::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionUniforms).hasVarying();
|
||||
addJob<DebugAmbientOcclusion>("DebugAmbientOcclusion", debugAmbientOcclusionInputs);
|
||||
|
||||
|
||||
// Scene Octree Debuging job
|
||||
{
|
||||
|
|
|
@ -61,6 +61,9 @@ void LinearDepthFramebuffer::updatePrimaryDepth(const gpu::TexturePointer& depth
|
|||
void LinearDepthFramebuffer::clear() {
|
||||
_linearDepthFramebuffer.reset();
|
||||
_linearDepthTexture.reset();
|
||||
_downsampleFramebuffer.reset();
|
||||
_halfLinearDepthTexture.reset();
|
||||
_halfNormalTexture.reset();
|
||||
}
|
||||
|
||||
void LinearDepthFramebuffer::allocate() {
|
||||
|
@ -71,7 +74,6 @@ void LinearDepthFramebuffer::allocate() {
|
|||
// For Linear Depth:
|
||||
_linearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), width, height,
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||
// _linearDepthTexture->autoGenerateMips(1);
|
||||
_linearDepthFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
_linearDepthFramebuffer->setRenderBuffer(0, _linearDepthTexture);
|
||||
_linearDepthFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat());
|
||||
|
@ -79,6 +81,7 @@ void LinearDepthFramebuffer::allocate() {
|
|||
// For Downsampling:
|
||||
_halfLinearDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y,
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||
_halfLinearDepthTexture->autoGenerateMips(5);
|
||||
|
||||
_halfNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB), _halfFrameSize.x, _halfFrameSize.y,
|
||||
gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR_MIP_POINT)));
|
||||
|
@ -193,7 +196,7 @@ void LinearDepthPass::run(const render::SceneContextPointer& sceneContext, const
|
|||
batch.setResourceTexture(DepthLinearPass_NormalMapSlot, normalTexture);
|
||||
batch.setPipeline(downsamplePipeline);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
|
||||
_gpuTimer.end(batch);
|
||||
});
|
||||
|
||||
|
|
30
libraries/render-utils/src/simple_opaque_web_browser.slf
Normal file
30
libraries/render-utils/src/simple_opaque_web_browser.slf
Normal file
|
@ -0,0 +1,30 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// simple_opaque_web_browser.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Anthony Thibault on 7/25/16.
|
||||
// Copyright 2016 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 gpu/Color.slh@>
|
||||
<@include DeferredBufferWrite.slh@>
|
||||
|
||||
// the albedo texture
|
||||
uniform sampler2D originalTexture;
|
||||
|
||||
// the interpolated normal
|
||||
in vec3 _normal;
|
||||
in vec4 _color;
|
||||
in vec2 _texCoord0;
|
||||
|
||||
void main(void) {
|
||||
vec4 texel = texture(originalTexture, _texCoord0.st);
|
||||
texel = colorToLinearRGBA(texel);
|
||||
packDeferredFragmentUnlit(normalize(_normal), 1.0, _color.rgb * texel.rgb);
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// simple_srgb_texture_unlit_no_tex_alpha.frag
|
||||
// simple_transparent_web_browser.frag
|
||||
// fragment shader
|
||||
//
|
||||
// Created by Anthony Thibault on 7/25/16.
|
||||
|
@ -26,19 +26,11 @@ in vec2 _texCoord0;
|
|||
void main(void) {
|
||||
vec4 texel = texture(originalTexture, _texCoord0.st);
|
||||
texel = colorToLinearRGBA(texel);
|
||||
|
||||
const float ALPHA_THRESHOLD = 0.999;
|
||||
if (_color.a < ALPHA_THRESHOLD) {
|
||||
packDeferredFragmentTranslucent(
|
||||
normalize(_normal),
|
||||
_color.a,
|
||||
_color.rgb * texel.rgb,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_ROUGHNESS);
|
||||
} else {
|
||||
packDeferredFragmentUnlit(
|
||||
normalize(_normal),
|
||||
1.0,
|
||||
_color.rgb * texel.rgb);
|
||||
}
|
||||
packDeferredFragmentTranslucent(
|
||||
normalize(_normal),
|
||||
_color.a,
|
||||
_color.rgb * texel.rgb,
|
||||
DEFAULT_FRESNEL,
|
||||
DEFAULT_ROUGHNESS);
|
||||
}
|
||||
|
|
@ -30,13 +30,8 @@ vec2 unpackOcclusionDepth(vec3 raw) {
|
|||
<@endfunc@>
|
||||
|
||||
<@func declareAmbientOcclusion()@>
|
||||
|
||||
struct AmbientOcclusionFrameTransform {
|
||||
vec4 _pixelInfo;
|
||||
vec4 _depthInfo;
|
||||
vec4 _stereoInfo;
|
||||
mat4 _projection[2];
|
||||
};
|
||||
<@include DeferredTransform.slh@>
|
||||
<$declareDeferredFrameTransform()$>
|
||||
|
||||
struct AmbientOcclusionParams {
|
||||
vec4 _resolutionInfo;
|
||||
|
@ -47,52 +42,20 @@ struct AmbientOcclusionParams {
|
|||
float _gaussianCoefs[8];
|
||||
};
|
||||
|
||||
uniform ambientOcclusionFrameTransformBuffer {
|
||||
AmbientOcclusionFrameTransform frameTransform;
|
||||
};
|
||||
uniform ambientOcclusionParamsBuffer {
|
||||
AmbientOcclusionParams params;
|
||||
};
|
||||
|
||||
|
||||
float getPerspectiveScale() {
|
||||
|
||||
return (params._resolutionInfo.z);
|
||||
}
|
||||
int getResolutionLevel() {
|
||||
|
||||
return int(params._resolutionInfo.x);
|
||||
}
|
||||
|
||||
vec2 getWidthHeight() {
|
||||
return vec2(ivec2(frameTransform._pixelInfo.zw) >> getResolutionLevel());
|
||||
}
|
||||
float getProjScale() {
|
||||
return getWidthHeight().y * frameTransform._projection[0][1][1] * 0.5;
|
||||
}
|
||||
mat4 getProjection(int side) {
|
||||
return frameTransform._projection[side];
|
||||
}
|
||||
|
||||
bool isStereo() {
|
||||
return frameTransform._stereoInfo.x > 0.0f;
|
||||
}
|
||||
|
||||
float getStereoSideWidth() {
|
||||
return float(int(frameTransform._stereoInfo.y) >> getResolutionLevel());
|
||||
}
|
||||
|
||||
ivec3 getStereoSideInfo(int xPos) {
|
||||
int sideWidth = int(getStereoSideWidth());
|
||||
return ivec3(xPos < sideWidth ? ivec2(0, 0) : ivec2(1, sideWidth), sideWidth);
|
||||
}
|
||||
|
||||
|
||||
float evalZeyeFromZdb(float depth) {
|
||||
return frameTransform._depthInfo.x / (depth * frameTransform._depthInfo.y + frameTransform._depthInfo.z);
|
||||
}
|
||||
|
||||
vec3 evalEyeNormal(vec3 C) {
|
||||
//return normalize(cross(dFdy(C), dFdx(C)));
|
||||
return normalize(cross(dFdx(C), dFdy(C)));
|
||||
}
|
||||
|
||||
|
||||
float getRadius() {
|
||||
return params._radiusInfo.x;
|
||||
}
|
||||
|
@ -130,6 +93,10 @@ float getNumSpiralTurns() {
|
|||
return params._sampleInfo.z;
|
||||
}
|
||||
|
||||
int doFetchMips() {
|
||||
return int(params._sampleInfo.w);
|
||||
}
|
||||
|
||||
float getBlurEdgeSharpness() {
|
||||
return params._blurInfo.x;
|
||||
}
|
||||
|
@ -163,6 +130,181 @@ float getBlurCoef(int c) {
|
|||
|
||||
<@endfunc@>
|
||||
|
||||
<@func declareSamplingDisk()@>
|
||||
|
||||
float getAngleDitheringWorldPos(in vec3 pixelWorldPos) {
|
||||
vec3 worldPosFract = fract(pixelWorldPos * 1.0);
|
||||
|
||||
ivec3 pixelPos = ivec3(worldPosFract * 256);
|
||||
|
||||
return isDitheringEnabled() * ((3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) + (3 * pixelPos.y ^ pixelPos.z + pixelPos.x * pixelPos.z)) * 10 + getFrameDithering();
|
||||
}
|
||||
|
||||
float getAngleDithering(in ivec2 pixelPos) {
|
||||
// Hash function used in the AlchemyAO paper
|
||||
return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering();
|
||||
}
|
||||
|
||||
float evalDiskRadius(float Zeye, vec2 imageSize) {
|
||||
// Choose the screen-space sample radius
|
||||
// proportional to the projected area of the sphere
|
||||
float ssDiskRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Zeye ) * getPerspectiveScale();
|
||||
|
||||
// clamp the disk to fit in the image otherwise too many unknown
|
||||
ssDiskRadius = min(ssDiskRadius, imageSize.y * 0.5);
|
||||
|
||||
return ssDiskRadius;
|
||||
}
|
||||
|
||||
const float TWO_PI = 6.28;
|
||||
|
||||
vec3 getUnitTapLocation(int sampleNumber, float spinAngle){
|
||||
// Radius relative to ssR
|
||||
float alpha = float(sampleNumber + 0.5) * getInvNumSamples();
|
||||
float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle;
|
||||
return vec3(cos(angle), sin(angle), alpha);
|
||||
}
|
||||
|
||||
vec3 getTapLocation(int sampleNumber, float spinAngle, float outerRadius) {
|
||||
vec3 tap = getUnitTapLocation(sampleNumber, spinAngle);
|
||||
tap.xy *= tap.z;
|
||||
tap *= outerRadius;
|
||||
return tap;
|
||||
}
|
||||
|
||||
vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 imageSize) {
|
||||
vec3 tap = getTapLocation(sampleNumber, spinAngle, outerRadius);
|
||||
vec2 tapPos = pixelPos + tap.xy;
|
||||
|
||||
if (!(isBorderingEnabled() > 0.0)) {
|
||||
return tap;
|
||||
}
|
||||
bool redoTap = false;
|
||||
|
||||
if ((tapPos.x < 0.5)) {
|
||||
tapPos.x = -tapPos.x;
|
||||
redoTap = true;
|
||||
} else if ((tapPos.x > imageSize.x - 0.5)) {
|
||||
tapPos.x -= (imageSize.x - tapPos.x);
|
||||
redoTap = true;
|
||||
}
|
||||
|
||||
if ((tapPos.y < 0.5)) {
|
||||
tapPos.y = -tapPos.y;
|
||||
redoTap = true;
|
||||
} else if ((tapPos.y > imageSize.y - 0.5)) {
|
||||
tapPos.y -= (imageSize.y - tapPos.y);
|
||||
redoTap = true;
|
||||
}
|
||||
/*
|
||||
if ((tapPos.x < 0.5)) {
|
||||
tapPos.x = 0.5;
|
||||
redoTap = true;
|
||||
} else if ((tapPos.x > imageSize.x - 0.5)) {
|
||||
tapPos.x = imageSize.x - 0.5;
|
||||
redoTap = true;
|
||||
}
|
||||
|
||||
if ((tapPos.y < 0.5)) {
|
||||
tapPos.y = 0.5;
|
||||
redoTap = true;
|
||||
} else if ((tapPos.y > imageSize.y - 0.5)) {
|
||||
tapPos.y = imageSize.y - 0.5;
|
||||
redoTap = true;
|
||||
}
|
||||
*/
|
||||
|
||||
if (redoTap) {
|
||||
tap.xy = tapPos - pixelPos;
|
||||
tap.z = length(tap.xy);
|
||||
tap.z = 0;
|
||||
}
|
||||
|
||||
return tap;
|
||||
}
|
||||
|
||||
<@endfunc@>
|
||||
|
||||
|
||||
<@func declareFetchDepthPyramidMap()@>
|
||||
|
||||
|
||||
// the depth pyramid texture
|
||||
uniform sampler2D pyramidMap;
|
||||
|
||||
float getZEye(ivec2 pixel, int level) {
|
||||
return -texelFetch(pyramidMap, pixel, level).x;
|
||||
}
|
||||
|
||||
const int LOG_MAX_OFFSET = 3;
|
||||
const int MAX_MIP_LEVEL = 5;
|
||||
int evalMipFromRadius(float radius) {
|
||||
// mipLevel = floor(log(ssR / MAX_OFFSET));
|
||||
return clamp(findMSB(int(radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL);
|
||||
}
|
||||
|
||||
|
||||
vec3 fetchTapUnfiltered(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) {
|
||||
ivec2 ssP = ivec2(tap.xy) + ssC;
|
||||
ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y);
|
||||
|
||||
|
||||
vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize;
|
||||
|
||||
vec2 fetchUV = vec2(tapUV.x + side.w * 0.5 * (side.x - tapUV.x), tapUV.y);
|
||||
|
||||
vec3 P;
|
||||
P.xy = tapUV;
|
||||
P.z = -texture(pyramidMap, fetchUV).x;
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
vec3 fetchTap(ivec4 side, ivec2 ssC, vec3 tap, vec2 imageSize) {
|
||||
int mipLevel = evalMipFromRadius(tap.z * doFetchMips());
|
||||
|
||||
ivec2 ssP = ivec2(tap.xy) + ssC;
|
||||
ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y);
|
||||
|
||||
// We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map.
|
||||
// Manually clamp to the texture size because texelFetch bypasses the texture unit
|
||||
// ivec2 mipSize = textureSize(pyramidMap, mipLevel);
|
||||
ivec2 mipSize = max(ivec2(imageSize) >> mipLevel, ivec2(1));
|
||||
|
||||
ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), mipSize - ivec2(1));
|
||||
|
||||
vec2 tapUV = (vec2(ssP) + vec2(0.5)) / imageSize;
|
||||
vec2 fetchUV = vec2(tapUV.x + side.w * 0.5 * (side.x - tapUV.x), tapUV.y);
|
||||
// vec2 tapUV = (vec2(mipP) + vec2(0.5)) / vec2(mipSize);
|
||||
|
||||
vec3 P;
|
||||
P.xy = tapUV;
|
||||
// P.z = -texelFetch(pyramidMap, mipP, mipLevel).x;
|
||||
P.z = -textureLod(pyramidMap, fetchUV, float(mipLevel)).x;
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
|
||||
|
||||
<@endfunc@>
|
||||
|
||||
|
||||
<@func declareEvalObscurance()@>
|
||||
|
||||
float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) {
|
||||
vec3 v = Q - C;
|
||||
float vv = dot(v, v);
|
||||
float vn = dot(v, n_C);
|
||||
|
||||
// Fall off function as recommended in SAO paper
|
||||
const float epsilon = 0.01;
|
||||
float f = max(getRadius2() - vv, 0.0);
|
||||
return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0);
|
||||
}
|
||||
|
||||
<@endfunc@>
|
||||
|
||||
<@func declareBlurPass(axis)@>
|
||||
|
||||
<$declarePackOcclusionDepth()$>
|
||||
|
@ -188,7 +330,7 @@ vec2 evalTapWeightedValue(ivec3 side, int r, ivec2 ssC, float key) {
|
|||
ivec2 tapOffset = <$axis$> * (r * RADIUS_SCALE);
|
||||
ivec2 ssP = (ssC + tapOffset);
|
||||
|
||||
if ((ssP.x < side.y || ssP.x >= side.z + side.y) || (ssP.y < 0 || ssP.y >= int(getWidthHeight().y))) {
|
||||
if ((ssP.x < side.y || ssP.x >= side.z + side.y) || (ssP.y < 0 || ssP.y >= int(getWidthHeight(getResolutionLevel()).y))) {
|
||||
return vec2(0.0);
|
||||
}
|
||||
vec2 tapOZ = fetchOcclusionDepth(ssC + tapOffset);
|
||||
|
@ -206,7 +348,7 @@ vec3 getBlurredOcclusion(vec2 coord) {
|
|||
ivec2 ssC = ivec2(coord);
|
||||
|
||||
// Stereo side info
|
||||
ivec3 side = getStereoSideInfo(ssC.x);
|
||||
ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel());
|
||||
|
||||
vec3 rawSample;
|
||||
vec2 occlusionDepth = fetchOcclusionDepthRaw(ssC, rawSample);
|
||||
|
@ -220,11 +362,11 @@ vec3 getBlurredOcclusion(vec2 coord) {
|
|||
int blurRadius = getBlurRadius();
|
||||
// negative side first
|
||||
for (int r = -blurRadius; r <= -1; ++r) {
|
||||
weightedSums += evalTapWeightedValue(side, r, ssC, key);
|
||||
weightedSums += evalTapWeightedValue(side.xyz, r, ssC, key);
|
||||
}
|
||||
// then positive side
|
||||
for (int r = 1; r <= blurRadius; ++r) {
|
||||
weightedSums += evalTapWeightedValue(side, r, ssC, key);
|
||||
weightedSums += evalTapWeightedValue(side.xyz, r, ssC, key);
|
||||
}
|
||||
|
||||
// Final normalization
|
||||
|
|
130
libraries/render-utils/src/ssao_debugOcclusion.slf
Normal file
130
libraries/render-utils/src/ssao_debugOcclusion.slf
Normal file
|
@ -0,0 +1,130 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// Created by Sam Gateau on 1/1/16.
|
||||
// Copyright 2016 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 ssao.slh@>
|
||||
<$declareAmbientOcclusion()$>
|
||||
<$declareFetchDepthPyramidMap()$>
|
||||
<$declareSamplingDisk()$>
|
||||
<$declareEvalObscurance()$>
|
||||
|
||||
<$declarePackOcclusionDepth()$>
|
||||
|
||||
<@include gpu/Color.slh@>
|
||||
<$declareColorWheel()$>
|
||||
|
||||
struct DebugParams{
|
||||
vec4 pixelInfo;
|
||||
};
|
||||
|
||||
uniform debugAmbientOcclusionBuffer {
|
||||
DebugParams debugParams;
|
||||
};
|
||||
|
||||
vec2 getDebugCursorTexcoord(){
|
||||
return debugParams.pixelInfo.xy;
|
||||
}
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
vec2 imageSize = getSideImageSize(getResolutionLevel());
|
||||
|
||||
// In debug adjust the correct frag pixel based on base resolution
|
||||
vec2 fragCoord = gl_FragCoord.xy;
|
||||
if (getResolutionLevel() > 0) {
|
||||
fragCoord /= float (1 << getResolutionLevel());
|
||||
}
|
||||
|
||||
// Pixel Debugged
|
||||
vec2 cursorUV = getDebugCursorTexcoord();
|
||||
vec2 cursorPixelPos = cursorUV * imageSize;
|
||||
|
||||
ivec2 ssC = ivec2(cursorPixelPos);
|
||||
|
||||
// Fetch the z under the pixel (stereo or not)
|
||||
float Zeye = getZEye(ssC, 0);
|
||||
|
||||
// Stereo side info
|
||||
ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel());
|
||||
|
||||
// From now on, ssC is the pixel pos in the side
|
||||
ssC.x -= side.y;
|
||||
vec2 fragPos = (vec2(ssC) + vec2(0.5)) / imageSize;
|
||||
|
||||
// The position and normal of the pixel fragment in Eye space
|
||||
vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos);
|
||||
vec3 Cn = evalEyeNormal(Cp);
|
||||
|
||||
// Choose the screen-space sample radius
|
||||
float ssDiskRadius = evalDiskRadius(Cp.z, imageSize);
|
||||
|
||||
vec2 fragToCursor = cursorPixelPos - fragCoord.xy;
|
||||
if (dot(fragToCursor,fragToCursor) > ssDiskRadius * ssDiskRadius) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// Let's make noise
|
||||
//float randomPatternRotationAngle = getAngleDithering(ssC);
|
||||
vec3 wCp = (getViewInverse() * vec4(Cp, 1.0)).xyz;
|
||||
float randomPatternRotationAngle = getAngleDitheringWorldPos(wCp);
|
||||
|
||||
|
||||
// Accumulate the Obscurance for each samples
|
||||
float sum = 0.0;
|
||||
float keepTapRadius = 1.0;
|
||||
int keepedMip = -1;
|
||||
bool keep = false;
|
||||
|
||||
for (int i = 0; i < getNumSamples(); ++i) {
|
||||
vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize);
|
||||
|
||||
// The occluding point in camera space
|
||||
vec2 fragToTap = vec2(ssC) + tap.xy - fragCoord.xy;
|
||||
if (dot(fragToTap,fragToTap) < keepTapRadius) {
|
||||
keep = true;
|
||||
keepedMip = evalMipFromRadius(tap.z * doFetchMips());
|
||||
}
|
||||
|
||||
vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize);
|
||||
|
||||
vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy);
|
||||
|
||||
sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q);
|
||||
}
|
||||
|
||||
|
||||
float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples());
|
||||
|
||||
<! // KEEP IT for Debugging
|
||||
// Bilateral box-filter over a quad for free, respecting depth edges
|
||||
// (the difference that this makes is subtle)
|
||||
if (abs(dFdx(Cp.z)) < 0.02) {
|
||||
A -= dFdx(A) * ((ssC.x & 1) - 0.5);
|
||||
}
|
||||
if (abs(dFdy(Cp.z)) < 0.02) {
|
||||
A -= dFdy(A) * ((ssC.y & 1) - 0.5);
|
||||
}
|
||||
!>
|
||||
|
||||
outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0);
|
||||
|
||||
if ((dot(fragToCursor,fragToCursor) < (100.0 * keepTapRadius * keepTapRadius) )) {
|
||||
// outFragColor = vec4(vec3(A), 1.0);
|
||||
outFragColor = vec4(vec3(A), 1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!keep) {
|
||||
outFragColor = vec4(0.1);
|
||||
} else {
|
||||
outFragColor.rgb = colorWheel(float(keepedMip)/float(MAX_MIP_LEVEL));
|
||||
}
|
||||
}
|
|
@ -11,129 +11,58 @@
|
|||
|
||||
<@include ssao.slh@>
|
||||
<$declareAmbientOcclusion()$>
|
||||
<$declareFetchDepthPyramidMap()$>
|
||||
<$declareSamplingDisk()$>
|
||||
<$declareEvalObscurance()$>
|
||||
|
||||
<$declarePackOcclusionDepth()$>
|
||||
|
||||
|
||||
const int LOG_MAX_OFFSET = 3;
|
||||
const int MAX_MIP_LEVEL = 5;
|
||||
|
||||
// the depth pyramid texture
|
||||
uniform sampler2D pyramidMap;
|
||||
|
||||
float getZEye(ivec2 pixel) {
|
||||
return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x;
|
||||
}
|
||||
|
||||
vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) {
|
||||
// compute the view space position using the depth
|
||||
// basically manually pick the proj matrix components to do the inverse
|
||||
float Xe = (-Zeye * (texcoord.x * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][0] - frameTransform._projection[side][3][0]) / frameTransform._projection[side][0][0];
|
||||
float Ye = (-Zeye * (texcoord.y * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][1] - frameTransform._projection[side][3][1]) / frameTransform._projection[side][1][1];
|
||||
return vec3(Xe, Ye, Zeye);
|
||||
}
|
||||
|
||||
out vec4 outFragColor;
|
||||
|
||||
uniform sampler2D normalMap;
|
||||
|
||||
float getAngleDithering(in ivec2 pixelPos) {
|
||||
// Hash function used in the AlchemyAO paper
|
||||
return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering();
|
||||
}
|
||||
|
||||
const float TWO_PI = 6.28;
|
||||
|
||||
vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR){
|
||||
// Radius relative to ssR
|
||||
float alpha = float(sampleNumber + 0.5) * getInvNumSamples();
|
||||
float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle;
|
||||
|
||||
ssR = alpha;
|
||||
return vec2(cos(angle), sin(angle));
|
||||
}
|
||||
|
||||
vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) {
|
||||
// Derivation:
|
||||
// mipLevel = floor(log(ssR / MAX_OFFSET));
|
||||
int mipLevel = clamp(findMSB(int(ssR)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL);
|
||||
|
||||
ivec2 ssOffset = ivec2(ssR * unitOffset);
|
||||
ivec2 ssP = ssOffset + ssC;
|
||||
if (bool(isBorderingEnabled())) {
|
||||
ssP.x = ((ssP.x < 0 || ssP.x >= side.z) ? ssC.x - ssOffset.x : ssP.x);
|
||||
ssP.y = ((ssP.y < 0 || ssP.y >= int(getWidthHeight().y)) ? ssC.y - ssOffset.y : ssP.y);
|
||||
}
|
||||
|
||||
ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y);
|
||||
|
||||
vec3 P;
|
||||
|
||||
// We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map.
|
||||
// Manually clamp to the texture size because texelFetch bypasses the texture unit
|
||||
ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), textureSize(pyramidMap, getResolutionLevel() + mipLevel) - ivec2(1));
|
||||
P.z = -texelFetch(pyramidMap, mipP, getResolutionLevel() + mipLevel).r;
|
||||
|
||||
// Offset to pixel center
|
||||
vec2 tapUV = (vec2(ssP) + vec2(0.5)) / float(side.z);
|
||||
P = evalEyePositionFromZeye(side.x, P.z, tapUV);
|
||||
return P;
|
||||
}
|
||||
|
||||
float sampleAO(in ivec3 side, in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle) {
|
||||
// Offset on the unit disk, spun for this pixel
|
||||
float ssR;
|
||||
vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR);
|
||||
ssR *= ssDiskRadius;
|
||||
|
||||
|
||||
|
||||
// The occluding point in camera space
|
||||
vec3 Q = getOffsetPosition(side, ssC, unitOffset, ssR);
|
||||
|
||||
vec3 v = Q - C;
|
||||
float vv = dot(v, v);
|
||||
float vn = dot(v, n_C);
|
||||
|
||||
// Fall off function as recommended in SAO paper
|
||||
const float epsilon = 0.01;
|
||||
float f = max(getRadius2() - vv, 0.0);
|
||||
return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec2 imageSize = getSideImageSize(getResolutionLevel());
|
||||
|
||||
// Pixel being shaded
|
||||
ivec2 ssC = ivec2(gl_FragCoord.xy);
|
||||
vec2 fragCoord = gl_FragCoord.xy;
|
||||
ivec2 ssC = ivec2(fragCoord.xy);
|
||||
|
||||
// Fetch the z under the pixel (stereo or not)
|
||||
float Zeye = getZEye(ssC);
|
||||
float Zeye = getZEye(ssC, 0);
|
||||
|
||||
// Stereo side info
|
||||
ivec3 side = getStereoSideInfo(ssC.x);
|
||||
ivec4 side = getStereoSideInfo(ssC.x, getResolutionLevel());
|
||||
|
||||
// From now on, ssC is the pixel pos in the side
|
||||
ssC.x -= side.y;
|
||||
vec2 fragPos = (vec2(ssC) + 0.5) / getStereoSideWidth();
|
||||
vec2 fragPos = (vec2(ssC) + vec2(0.5)) / imageSize;
|
||||
|
||||
// The position and normal of the pixel fragment in Eye space
|
||||
vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos);
|
||||
vec3 Cn = evalEyeNormal(Cp);
|
||||
|
||||
// Choose the screen-space sample radius
|
||||
// proportional to the projected area of the sphere
|
||||
float ssDiskRadius = -getProjScale() * getRadius() / Cp.z;
|
||||
float ssDiskRadius = evalDiskRadius(Cp.z, imageSize);
|
||||
|
||||
// Let's make noise
|
||||
float randomPatternRotationAngle = getAngleDithering(ssC);
|
||||
//vec3 wCp = (getViewInverse() * vec4(Cp, 1.0)).xyz;
|
||||
//float randomPatternRotationAngle = getAngleDitheringWorldPos(wCp);
|
||||
|
||||
// Accumulate the Obscurance for each samples
|
||||
float sum = 0.0;
|
||||
for (int i = 0; i < getNumSamples(); ++i) {
|
||||
sum += sampleAO(side, ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle);
|
||||
vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize);
|
||||
|
||||
vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize);
|
||||
|
||||
vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy);
|
||||
|
||||
sum += float(tap.z > 0.0) * evalAO(Cp, Cn, Q);
|
||||
}
|
||||
|
||||
float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples());
|
||||
|
||||
<! // KEEP IT for Debugging
|
||||
// KEEP IT for Debugging
|
||||
// Bilateral box-filter over a quad for free, respecting depth edges
|
||||
// (the difference that this makes is subtle)
|
||||
if (abs(dFdx(Cp.z)) < 0.02) {
|
||||
|
@ -142,20 +71,16 @@ void main(void) {
|
|||
if (abs(dFdy(Cp.z)) < 0.02) {
|
||||
A -= dFdy(A) * ((ssC.y & 1) - 0.5);
|
||||
}
|
||||
!>
|
||||
|
||||
|
||||
outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0);
|
||||
|
||||
<! // KEEP IT for Debugging
|
||||
// Debug Normal: outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0);
|
||||
// Debug Radius outFragColor = vec4(vec3(ssDiskRadius / 100.0), 1.0);
|
||||
// Debug MaxMiplevel outFragColor = vec4(1.0 - vec3(float(clamp(findMSB(int(ssDiskRadius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL))/ float(MAX_MIP_LEVEL)), 1.0);
|
||||
// Debug OffsetPosition
|
||||
float ssR;
|
||||
vec2 unitOffset = tapLocation(int(getNumSamples() - 1), 0, ssR);
|
||||
vec3 Q = getOffsetPosition(side, ssC, unitOffset, ssR * ssDiskRadius);
|
||||
//outFragColor = vec4(vec3(Q.x / 10.0, Q.y / 2.0, -Q.z/ 3.0), 1.0);
|
||||
vec3 v = normalize(Q - Cp);
|
||||
outFragColor = vec4((v + vec3(1.0))* 0.5, 1.0);
|
||||
!>
|
||||
/* {
|
||||
vec3 tap = getTapLocationClamped(2, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize);
|
||||
vec3 tapUVZ = fetchTap(side, ssC, tap, imageSize);
|
||||
vec2 fetchUV = vec2(tapUVZ.x + side.w * 0.5 * (side.x - tapUVZ.x), tapUVZ.y);
|
||||
vec3 Q = evalEyePositionFromZeye(side.x, tapUVZ.z, tapUVZ.xy);
|
||||
outFragColor = vec4(fetchUV, 0.0, 1.0);
|
||||
}*/
|
||||
|
||||
}
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
set(TARGET_NAME script-engine)
|
||||
setup_hifi_library(Gui Network Script ScriptTools WebSockets Widgets)
|
||||
|
||||
target_zlib()
|
||||
|
||||
add_dependency_external_projects(quazip)
|
||||
find_package(QuaZip REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES})
|
||||
|
||||
if (WIN32)
|
||||
add_paths_to_fixup_libs(${QUAZIP_DLL_PATH})
|
||||
endif ()
|
||||
|
||||
link_hifi_libraries(shared networking octree gpu ui procedural model model-networking recording avatars fbx entities controllers animation audio physics)
|
||||
|
|
145
libraries/script-engine/src/FileScriptingInterface.cpp
Normal file
145
libraries/script-engine/src/FileScriptingInterface.cpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
//
|
||||
// FileScriptingInterface.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Elisa Lupin-Jimenez on 6/28/16.
|
||||
// Copyright 2016 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 <QTemporaryDir>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QBuffer>
|
||||
#include <QTextCodec>
|
||||
#include <QIODevice>
|
||||
#include <QUrl>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QFileInfo>
|
||||
#include <quazip5/quazip.h>
|
||||
#include <quazip5/JlCompress.h>
|
||||
#include "ResourceManager.h"
|
||||
|
||||
#include "FileScriptingInterface.h"
|
||||
|
||||
|
||||
FileScriptingInterface::FileScriptingInterface(QObject* parent) : QObject(parent) {
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
void FileScriptingInterface::runUnzip(QString path, QUrl url) {
|
||||
qDebug() << "Url that was downloaded: " + url.toString();
|
||||
qDebug() << "Path where download is saved: " + path;
|
||||
QString fileName = "/" + path.section("/", -1);
|
||||
QString tempDir = path;
|
||||
tempDir.remove(fileName);
|
||||
qDebug() << "Temporary directory at: " + tempDir;
|
||||
if (!isTempDir(tempDir)) {
|
||||
qDebug() << "Temporary directory mismatch; risk of losing files";
|
||||
return;
|
||||
}
|
||||
|
||||
QString file = unzipFile(path, tempDir);
|
||||
if (file != "") {
|
||||
qDebug() << "Object file to upload: " + file;
|
||||
QUrl url = QUrl::fromLocalFile(file);
|
||||
emit unzipSuccess(url.toString());
|
||||
} else {
|
||||
qDebug() << "unzip failed";
|
||||
}
|
||||
qDebug() << "Removing temporary directory at: " + tempDir;
|
||||
QDir(tempDir).removeRecursively();
|
||||
}
|
||||
|
||||
// fix to check that we are only referring to a temporary directory
|
||||
bool FileScriptingInterface::isTempDir(QString tempDir) {
|
||||
QString folderName = "/" + tempDir.section("/", -1);
|
||||
QString tempContainer = tempDir;
|
||||
tempContainer.remove(folderName);
|
||||
QTemporaryDir test;
|
||||
QString testDir = test.path();
|
||||
folderName = "/" + testDir.section("/", -1);
|
||||
QString testContainer = testDir;
|
||||
testContainer.remove(folderName);
|
||||
if (testContainer == tempContainer) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileScriptingInterface::isZippedFbx(QUrl url) {
|
||||
if (url.toString().contains(".zip") && url.toString().contains("fbx")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// this function is not in use
|
||||
QString FileScriptingInterface::getTempDir() {
|
||||
QTemporaryDir dir;
|
||||
dir.setAutoRemove(false);
|
||||
return dir.path();
|
||||
// do something to delete this temp dir later
|
||||
}
|
||||
|
||||
QString FileScriptingInterface::convertUrlToPath(QUrl url) {
|
||||
QString newUrl;
|
||||
QString oldUrl = url.toString();
|
||||
newUrl = oldUrl.section("filename=", 1, 1);
|
||||
qDebug() << "Filename should be: " + newUrl;
|
||||
return newUrl;
|
||||
}
|
||||
|
||||
// this function is not in use
|
||||
void FileScriptingInterface::downloadZip(QString path, const QString link) {
|
||||
QUrl url = QUrl(link);
|
||||
auto request = ResourceManager::createResourceRequest(nullptr, url);
|
||||
connect(request, &ResourceRequest::finished, this, [this, path]{
|
||||
unzipFile(path, ""); // so intellisense isn't mad
|
||||
});
|
||||
request->send();
|
||||
}
|
||||
|
||||
|
||||
QString FileScriptingInterface::unzipFile(QString path, QString tempDir) {
|
||||
|
||||
QDir dir(path);
|
||||
QString dirName = dir.path();
|
||||
QString target = tempDir + "/model_repo";
|
||||
QStringList list = JlCompress::extractDir(dirName, target);
|
||||
|
||||
qDebug() << list;
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
return list.front();
|
||||
} else {
|
||||
qDebug() << "Extraction failed";
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// this function is not in use
|
||||
void FileScriptingInterface::recursiveFileScan(QFileInfo file, QString* dirName) {
|
||||
/*if (!file.isDir()) {
|
||||
qDebug() << "Regular file logged: " + file.fileName();
|
||||
return;
|
||||
}*/
|
||||
QFileInfoList files;
|
||||
|
||||
if (file.fileName().contains(".zip")) {
|
||||
qDebug() << "Extracting archive: " + file.fileName();
|
||||
JlCompress::extractDir(file.fileName());
|
||||
}
|
||||
files = file.dir().entryInfoList();
|
||||
|
||||
/*if (files.empty()) {
|
||||
files = JlCompress::getFileList(file.fileName());
|
||||
}*/
|
||||
|
||||
foreach (QFileInfo file, files) {
|
||||
qDebug() << "Looking into file: " + file.fileName();
|
||||
recursiveFileScan(file, dirName);
|
||||
}
|
||||
return;
|
||||
}
|
43
libraries/script-engine/src/FileScriptingInterface.h
Normal file
43
libraries/script-engine/src/FileScriptingInterface.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// FileScriptingInterface.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Elisa Lupin-Jimenez on 6/28/16.
|
||||
// Copyright 2016 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_FileScriptingInterface_h
|
||||
#define hifi_FileScriptingInterface_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QFileInfo>
|
||||
#include <QString>
|
||||
|
||||
class FileScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FileScriptingInterface(QObject* parent);
|
||||
|
||||
|
||||
public slots:
|
||||
bool isZippedFbx(QUrl url);
|
||||
QString convertUrlToPath(QUrl url);
|
||||
void runUnzip(QString path, QUrl url);
|
||||
QString getTempDir();
|
||||
|
||||
signals:
|
||||
void unzipSuccess(QString url);
|
||||
|
||||
private:
|
||||
bool isTempDir(QString tempDir);
|
||||
QString unzipFile(QString path, QString tempDir);
|
||||
void recursiveFileScan(QFileInfo file, QString* dirName);
|
||||
void downloadZip(QString path, const QString link);
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_FileScriptingInterface_h
|
|
@ -49,6 +49,7 @@
|
|||
#include "BatchLoader.h"
|
||||
#include "DataViewClass.h"
|
||||
#include "EventTypes.h"
|
||||
#include "FileScriptingInterface.h" // unzip project
|
||||
#include "MenuItemProperties.h"
|
||||
#include "ScriptAudioInjector.h"
|
||||
#include "ScriptCache.h"
|
||||
|
@ -501,6 +502,9 @@ void ScriptEngine::init() {
|
|||
registerGlobalObject("Mat4", &_mat4Library);
|
||||
registerGlobalObject("Uuid", &_uuidLibrary);
|
||||
registerGlobalObject("Messages", DependencyManager::get<MessagesClient>().data());
|
||||
|
||||
registerGlobalObject("File", new FileScriptingInterface(this));
|
||||
|
||||
qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);
|
||||
qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "DoubleHashKey.h"
|
||||
|
||||
const uint32_t NUM_PRIMES = 64;
|
||||
const uint32_t PRIMES[] = {
|
||||
const uint32_t PRIMES[] = {
|
||||
4194301U, 4194287U, 4194277U, 4194271U, 4194247U, 4194217U, 4194199U, 4194191U,
|
||||
4194187U, 4194181U, 4194173U, 4194167U, 4194143U, 4194137U, 4194131U, 4194107U,
|
||||
4194103U, 4194023U, 4194011U, 4194007U, 4193977U, 4193971U, 4193963U, 4193957U,
|
||||
|
@ -27,8 +27,8 @@ uint32_t DoubleHashKey::hashFunction(uint32_t value, uint32_t primeIndex) {
|
|||
uint32_t hash = PRIMES[primeIndex % NUM_PRIMES] * (value + 1U);
|
||||
hash += ~(hash << 15);
|
||||
hash ^= (hash >> 10);
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 6);
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 6);
|
||||
hash += ~(hash << 11);
|
||||
return hash ^ (hash >> 16);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue