Merge branch 'master' of github.com:highfidelity/hifi into head-puck

This commit is contained in:
Dante Ruiz 2017-05-30 22:52:57 +01:00
commit a0ba1a64b5
45 changed files with 1496 additions and 587 deletions

View file

@ -1,4 +1,4 @@
###Dependencies
### Dependencies
* [cmake](https://cmake.org/download/) ~> 3.3.2
* [Qt](https://www.qt.io/download-open-source) ~> 5.6.2
@ -6,7 +6,7 @@
* IMPORTANT: Use the latest available version of OpenSSL to avoid security vulnerabilities.
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
####CMake External Project Dependencies
#### CMake External Project Dependencies
* [boostconfig](https://github.com/boostorg/config) ~> 1.58
* [Bullet Physics Engine](https://github.com/bulletphysics/bullet3/releases) ~> 2.83
@ -30,16 +30,19 @@ These are not placed in your normal build tree when doing an out of source build
If you would like to use a specific install of a dependency instead of the version that would be grabbed as a CMake ExternalProject, you can pass -DUSE_LOCAL_$NAME=0 (where $NAME is the name of the subfolder in [cmake/externals](cmake/externals)) when you run CMake to tell it not to get that dependency as an external project.
###OS Specific Build Guides
### OS Specific Build Guides
* [BUILD_OSX.md](BUILD_OSX.md) - additional instructions for OS X.
* [BUILD_LINUX.md](BUILD_LINUX.md) - additional instructions for Linux.
* [BUILD_WIN.md](BUILD_WIN.md) - additional instructions for Windows.
* [BUILD_ANDROID.md](BUILD_ANDROID.md) - additional instructions for Android
###CMake
### CMake
Hifi uses CMake to generate build files and project files for your platform.
####Qt
#### Qt
In order for CMake to find the Qt5 find modules, you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt installation.
This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment).
@ -50,7 +53,8 @@ The path it needs to be set to will depend on where and how Qt5 was installed. e
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.2/lib/cmake
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
####Generating build files
#### Generating build files
Create a build directory in the root of your checkout and then run the CMake build from there. This will keep the rest of the directory clean.
mkdir build
@ -59,14 +63,15 @@ Create a build directory in the root of your checkout and then run the CMake bui
If cmake gives you the same error message repeatedly after the build fails (e.g. you had a typo in the QT_CMAKE_PREFIX_PATH that you fixed but the `.cmake` files still cannot be found), try removing `CMakeCache.txt`.
####Variables
#### Variables
Any variables that need to be set for CMake to find dependencies can be set as ENV variables in your shell profile, or passed directly to CMake with a `-D` flag appended to the `cmake ..` command.
For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation:
cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.2/lib/cmake
####Finding Dependencies
#### Finding Dependencies
The following applies for dependencies we do not grab via CMake ExternalProject (OpenSSL is an example), or for dependencies you have opted not to grab as a CMake ExternalProject (via -DUSE_LOCAL_$NAME=0). The list of dependencies we grab by default as external projects can be found in [the CMake External Project Dependencies section](#cmake-external-project-dependencies).
@ -78,8 +83,8 @@ In the examples below the variable $NAME would be replaced by the name of the de
* $NAME_ROOT_DIR - set this variable in your ENV
* HIFI_LIB_DIR - set this variable in your ENV to your High Fidelity lib folder, should contain a folder '$name'
###Optional Components
### Optional Components
####Devices
#### Devices
You can support external input/output devices such as Leap Motion, MIDI, and more by adding each individual SDK in the visible building path. Refer to the readme file available in each device folder in [interface/external/](interface/external) for the detailed explanation of the requirements to use the device.

View file

@ -1,6 +1,6 @@
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Android specific instructions are found in this file.
###Android Dependencies
### Android Dependencies
You will need the following tools to build our Android targets.
@ -17,23 +17,23 @@ You will need the following tools to build our Android targets.
You will also need to cross-compile the dependencies required for all platforms for Android, and help CMake find these compiled libraries on your machine.
####Scribe
#### Scribe
High Fidelity has a shader pre-processing tool called `scribe` that various libraries will call on during the build process. You must compile scribe using your native toolchain (following the build instructions for your platform) and then pass a CMake variable or set an ENV variable `SCRIBE_PATH` that is a path to the scribe executable.
CMake will fatally error if it does not find the scribe executable while using the android toolchain.
####Optional Components
#### Optional Components
* [Oculus Mobile SDK](https://developer.oculus.com/downloads/#sdk=mobile) ~> 0.4.2
####ANDROID_LIB_DIR
#### ANDROID_LIB_DIR
Since you won't be installing Android dependencies to system paths on your development machine, CMake will need a little help tracking down your Android dependencies.
This is most easily accomplished by installing all Android dependencies in the same folder. You can place this folder wherever you like on your machine. In this build guide and across our CMakeLists files this folder is referred to as `ANDROID_LIB_DIR`. You can set `ANDROID_LIB_DIR` in your environment or by passing when you run CMake.
####Qt
#### Qt
Install Qt 5.5.1 for Android for your host environment from the [Qt downloads page](http://www.qt.io/download/). Install Qt to ``$ANDROID_LIB_DIR/Qt``. This is required so that our root CMakeLists file can help CMake find your Android Qt installation.
@ -41,7 +41,7 @@ The component required for the Android build is the `Android armv7` component.
If you would like to install Qt to a different location, or attempt to build with a different Qt version, you can pass `ANDROID_QT_CMAKE_PREFIX_PATH` to CMake. Point to the `cmake` folder inside `$VERSION_NUMBER/android_armv7/lib`. Otherwise, our root CMakeLists will set it to `$ANDROID_LIB_DIR/Qt/5.5/android_armv7/lib/cmake`.
####OpenSSL
#### OpenSSL
Cross-compilation of OpenSSL has been tested from an OS X machine running 10.10 compiling OpenSSL 1.0.2. It is likely that the steps below will work for other OpenSSL versions than 1.0.2.
@ -76,7 +76,7 @@ This should generate libcrypto and libssl in the root of the OpenSSL directory.
If you have been building other components it is possible that the OpenSSL compile will fail based on the values other cross-compilations (tbb, bullet) have set. Ensure that you are in a new terminal window to avoid compilation errors from previously set environment variables.
####Oculus Mobile SDK
#### Oculus Mobile SDK
The Oculus Mobile SDK is optional, for Gear VR support. It is not required to compile gvr-interface.
@ -91,7 +91,7 @@ ndk-build
This will create the liboculus.a archive that our FindLibOVR module will look for when cmake is run.
#####Hybrid testing
##### Hybrid testing
Currently the 'vr_dual' mode that would allow us to run a hybrid app has limited support in the Oculus Mobile SDK. The best way to have an application we can launch without having to connect to the GearVR is to put the Gear VR Service into developer mode. This stops Oculus Home from taking over the device when it is plugged into the Gear VR headset, and allows the application to be launched from the Applications page.
@ -99,7 +99,7 @@ To put the Gear VR Service into developer mode you need an application with an O
Once the application is on your device, go to `Settings->Application Manager->Gear VR Service->Manage Storage`. Tap on `VR Service Version` six times. It will scan your device to verify that you have an osig file in an application on your device, and then it will let you enable Developer mode.
###CMake
### CMake
We use CMake to generate the makefiles that compile and deploy the Android APKs to your device. In order to create Makefiles for the Android targets, CMake requires that some environment variables are set, and that other variables are passed to it when it is run.

View file

@ -1,6 +1,7 @@
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file.
###Qt5 Dependencies
### Qt5 Dependencies
Should you choose not to install Qt5 via a package manager that handles dependencies for you, you may be missing some Qt5 dependencies. On Ubuntu, for example, the following additional packages are required:
libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev

View file

@ -1,12 +1,13 @@
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file.
###Homebrew
### Homebrew
[Homebrew](https://brew.sh/) is an excellent package manager for OS X. It makes install of some High Fidelity dependencies very simple.
brew tap homebrew/versions
brew install cmake openssl
###OpenSSL
### OpenSSL
Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations.
For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR:
@ -15,7 +16,8 @@ For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR:
Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change.
###Qt
### Qt
Download and install the [Qt 5.6.2 for macOS](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-mac-x64-clang-5.6.2.dmg).
Keep the default components checked when going through the installer.
@ -23,7 +25,8 @@ Keep the default components checked when going through the installer.
Once Qt is installed, you need to manually configure the following:
* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.2/5.6/clang_64/lib/cmake/` directory.
###Xcode
### Xcode
If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles.
cmake .. -GXcode

View file

@ -1,32 +1,32 @@
This is a stand-alone guide for creating your first High Fidelity build for Windows 64-bit.
###Step 1. Installing Visual Studio 2013
### Step 1. Installing Visual Studio 2013
If you don't already have the Community or Professional edition of Visual Studio 2013, download and install [Visual Studio Community 2013](https://www.visualstudio.com/en-us/news/releasenotes/vs2013-community-vs). You do not need to install any of the optional components when going through the installer.
Note: Newer versions of Visual Studio are not yet compatible.
###Step 2. Installing CMake
### Step 2. Installing CMake
Download and install the [CMake 3.8.0 win64-x64 Installer](https://cmake.org/files/v3.8/cmake-3.8.0-win64-x64.msi). Make sure "Add CMake to system PATH for all users" is checked when going through the installer.
###Step 3. Installing Qt
### Step 3. Installing Qt
Download and install the [Qt 5.6.2 for Windows 64-bit (VS 2013)](http://download.qt.io/official_releases/qt/5.6/5.6.2/qt-opensource-windows-x86-msvc2013_64-5.6.2.exe).
Keep the default components checked when going through the installer.
###Step 4. Setting Qt Environment Variable
### Step 4. Setting Qt Environment Variable
Go to "Control Panel > System > Advanced System Settings > Environment Variables > New..." (or search “Environment Variables” in Start Search).
* Set "Variable name": QT_CMAKE_PREFIX_PATH
* Set "Variable value": `%QT_DIR%\5.6\msvc2013_64\lib\cmake`
###Step 5. Installing OpenSSL
### Step 5. Installing OpenSSL
Download and install the [Win64 OpenSSL v1.0.2k Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2k.exe).
Download and install the [Win64 OpenSSL v1.0.2L Installer](https://slproweb.com/download/Win64OpenSSL-1_0_2L.exe).
###Step 6. Running CMake to Generate Build Files
### Step 6. Running CMake to Generate Build Files
Run Command Prompt from Start and run the following commands:
cd "%HIFI_DIR%"
@ -36,7 +36,7 @@ Run Command Prompt from Start and run the following commands:
Where %HIFI_DIR% is the directory for the highfidelity repository.
###Step 7. Making a Build
### Step 7. Making a Build
Open '%HIFI_DIR%\build\hifi.sln' using Visual Studio.
@ -44,7 +44,7 @@ Change the Solution Configuration (next to the green play button) from "Debug" t
Run Build > Build Solution.
###Step 8. Testing Interface
### Step 8. Testing Interface
Create another environment variable (see Step #4)
* Set "Variable name": _NO_DEBUG_HEAP
@ -56,7 +56,7 @@ Now, you should have a full build of High Fidelity and be able to run the Interf
Note: You can also run Interface by launching it from command line or File Explorer from %HIFI_DIR%\build\interface\Release\interface.exe
###Troubleshooting
### Troubleshooting
For any problems after Step #6, first try this:
* Delete your locally cloned copy of the highfidelity repository
@ -64,18 +64,18 @@ For any problems after Step #6, first try this:
* Redownload the [repository](https://github.com/highfidelity/hifi)
* Restart directions from Step #6
####CMake gives you the same error message repeatedly after the build fails
#### CMake gives you the same error message repeatedly after the build fails
Remove `CMakeCache.txt` found in the '%HIFI_DIR%\build' directory
####nmake cannot be found
#### nmake cannot be found
Make sure nmake.exe is located at the following path:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin
If not, add the directory where nmake is located to the PATH environment variable.
####Qt is throwing an error
#### Qt is throwing an error
Make sure you have the correct version (5.6.2) installed and 'QT_CMAKE_PREFIX_PATH' environment variable is set correctly.

View file

@ -2,15 +2,15 @@ Follow the [build guide](BUILD.md) to figure out how to build High Fidelity for
During generation, CMake should produce an `install` target and a `package` target.
###Install
### Install
The `install` target will copy the High Fidelity targets and their dependencies to your `CMAKE_INSTALL_PREFIX`.
###Packaging
### Packaging
To produce an installer, run the `package` target.
####Windows
#### Windows
To produce an executable installer on Windows, the following are required:
@ -20,6 +20,6 @@ To produce an executable installer on Windows, the following are required:
Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.
####OS X
#### OS X
Run the `package` target to create an Apple Disk Image (.dmg).

View file

@ -436,6 +436,10 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
const char* portStr = getCmdOption(argc, constArgv, "--listenPort");
const int listenPort = portStr ? atoi(portStr) : INVALID_PORT;
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
Setting::init();
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
@ -456,10 +460,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
QCoreApplication::addLibraryPath(audioDLLPath);
#endif
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
bool previousSessionCrashed = CrashHandler::checkForResetSettings(runningMarkerExisted, suppressPrompt);
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
DependencyManager::registerInheritance<AvatarHashMap, AvatarManager>();
DependencyManager::registerInheritance<EntityDynamicFactoryInterface, InterfaceDynamicFactory>();
@ -4881,12 +4881,6 @@ QRect Application::getDesirableApplicationGeometry() const {
return applicationGeometry;
}
glm::vec3 Application::getSunDirection() const {
// Sun direction is in fact just the location of the sun relative to the origin
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
return skyStage->getSunLight()->getDirection();
}
// FIXME, preprocessor guard this check to occur only in DEBUG builds
static QThread * activeRenderingThread = nullptr;
@ -4953,90 +4947,6 @@ namespace render {
}
}
// Background Render Data & rendering functions
class BackgroundRenderData {
public:
typedef render::Payload<BackgroundRenderData> Payload;
typedef Payload::DataPointer Pointer;
static render::ItemID _item; // unique WorldBoxRenderData
};
render::ItemID BackgroundRenderData::_item = 0;
namespace render {
template <> const ItemKey payloadGetKey(const BackgroundRenderData::Pointer& stuff) {
return ItemKey::Builder::background();
}
template <> const Item::Bound payloadGetBound(const BackgroundRenderData::Pointer& stuff) {
return Item::Bound();
}
template <> void payloadRender(const BackgroundRenderData::Pointer& background, RenderArgs* args) {
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
// Background rendering decision
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
auto backgroundMode = skyStage->getBackgroundMode();
switch (backgroundMode) {
case model::SunSkyStage::SKY_DEFAULT: {
auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
auto sceneKeyLight = scene->getKeyLight();
scene->setSunModelEnable(false);
sceneKeyLight->setColor(ColorUtils::toVec3(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR));
sceneKeyLight->setIntensity(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY);
sceneKeyLight->setAmbientIntensity(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY);
sceneKeyLight->setDirection(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION);
// fall through: render a skybox (if available), or the defaults (if requested)
}
case model::SunSkyStage::SKY_BOX: {
auto skybox = skyStage->getSkybox();
if (!skybox->empty()) {
PerformanceTimer perfTimer("skybox");
skybox->render(batch, args->getViewFrustum());
break;
}
// 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();
if (defaultSkyboxAmbientTexture) {
sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance());
sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
} else {
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(
"Failed to get a valid Default Skybox Ambient Texture ? probably because it couldn't be find during initialization step");
}
// fall through: render defaults skybox
} else {
break;
}
}
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:
break;
}
}
}
void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool selfAvatarOnly) {
// FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering.
@ -5060,15 +4970,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
// The pending changes collecting the changes here
render::Transaction transaction;
// FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities
// Background rendering decision
if (!render::Item::isValidID(BackgroundRenderData::_item)) {
auto backgroundRenderData = make_shared<BackgroundRenderData>();
auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);
BackgroundRenderData::_item = _main3DScene->allocateID();
transaction.resetItem(BackgroundRenderData::_item, backgroundRenderPayload);
}
// Assuming nothing gets rendered through that
if (!selfAvatarOnly) {
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
@ -5104,12 +5005,6 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
});
}
// Setup the current Zone Entity lighting
{
auto stage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(stage->getSunLight());
}
{
PerformanceTimer perfTimer("SceneProcessTransaction");
_main3DScene->enqueueTransaction(transaction);

View file

@ -460,8 +460,6 @@ private:
void queryOctree(NodeType_t serverType, PacketType packetType, NodeToJurisdictionMap& jurisdictions, bool forceResend = false);
glm::vec3 getSunDirection() const;
void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed);
int sendNackPackets();

View file

@ -24,12 +24,15 @@
#include "Application.h"
#include "Menu.h"
#include <SettingHandle.h>
#include <RunningMarker.h>
#include <SettingHandle.h>
#include <SettingHelpers.h>
bool CrashHandler::checkForResetSettings(bool wasLikelyCrash, bool suppressPrompt) {
Settings settings;
QSettings::setDefaultFormat(JSON_FORMAT);
QSettings settings;
settings.beginGroup("Developer");
QVariant displayCrashOptions = settings.value(MenuOption::DisplayCrashOptions);
QVariant askToResetSettingsOption = settings.value(MenuOption::AskToResetSettings);
@ -106,7 +109,7 @@ void CrashHandler::handleCrash(CrashHandler::Action action) {
return;
}
Settings settings;
QSettings settings;
const QString ADDRESS_MANAGER_GROUP = "AddressManager";
const QString ADDRESS_KEY = "address";
const QString AVATAR_GROUP = "Avatar";

View file

@ -444,6 +444,9 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) {
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
if (this->_pressed && event.getType() == PointerEvent::Move) {
return;
}
// Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover.
// FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times).
// This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery".

View file

@ -13,6 +13,8 @@
#include <glm/glm.hpp>
#include <QRunnable>
#include <QThreadPool>
#include <QDataStream>
#include <QtCore/QDebug>
#include <QtNetwork/QNetworkRequest>
@ -49,13 +51,43 @@ Sound::Sound(const QUrl& url, bool isStereo, bool isAmbisonic) :
_isAmbisonic(isAmbisonic),
_isReady(false)
{
}
void Sound::downloadFinished(const QByteArray& data) {
// this is a QRunnable, will delete itself after it has finished running
SoundProcessor* soundProcessor = new SoundProcessor(_url, data, _isStereo, _isAmbisonic);
connect(soundProcessor, &SoundProcessor::onSuccess, this, &Sound::soundProcessSuccess);
connect(soundProcessor, &SoundProcessor::onError, this, &Sound::soundProcessError);
QThreadPool::globalInstance()->start(soundProcessor);
}
void Sound::soundProcessSuccess(QByteArray data, bool stereo, bool ambisonic, float duration) {
qCDebug(audio) << "Setting ready state for sound file" << _url.toDisplayString();
_byteArray = data;
_isStereo = stereo;
_isAmbisonic = ambisonic;
_duration = duration;
_isReady = true;
finishedLoading(true);
emit ready();
}
void Sound::soundProcessError(int error, QString str) {
qCCritical(audio) << "Failed to process sound file" << _url.toDisplayString() << "code =" << error << str;
emit failed(QNetworkReply::UnknownContentError);
finishedLoading(false);
}
void SoundProcessor::run() {
qCDebug(audio) << "Processing sound file" << _url.toDisplayString();
// replace our byte array with the downloaded data
QByteArray rawAudioByteArray = QByteArray(data);
QString fileName = getURL().fileName().toLower();
QByteArray rawAudioByteArray = QByteArray(_data);
QString fileName = _url.fileName().toLower();
static const QString WAV_EXTENSION = ".wav";
static const QString RAW_EXTENSION = ".raw";
@ -72,31 +104,28 @@ void Sound::downloadFinished(const QByteArray& data) {
// since it's raw the only way for us to know that is if the file was called .stereo.raw
if (fileName.toLower().endsWith("stereo.raw")) {
_isStereo = true;
qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file.";
qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << _url << "as stereo audio file.";
}
// Process as 48khz RAW file
downSample(rawAudioByteArray, 48000);
} else {
qCDebug(audio) << "Unknown sound file type";
emit onError(300, "Failed to load sound file, reason: unknown sound file type");
return;
}
finishedLoading(true);
_isReady = true;
emit ready();
emit onSuccess(_data, _isStereo, _isAmbisonic, _duration);
}
void Sound::downSample(const QByteArray& rawAudioByteArray, int sampleRate) {
void SoundProcessor::downSample(const QByteArray& rawAudioByteArray, int sampleRate) {
// we want to convert it to the format that the audio-mixer wants
// which is signed, 16-bit, 24Khz
if (sampleRate == AudioConstants::SAMPLE_RATE) {
// no resampling needed
_byteArray = rawAudioByteArray;
_data = rawAudioByteArray;
} else {
int numChannels = _isAmbisonic ? AudioConstants::AMBISONIC : (_isStereo ? AudioConstants::STEREO : AudioConstants::MONO);
@ -106,15 +135,15 @@ void Sound::downSample(const QByteArray& rawAudioByteArray, int sampleRate) {
int numSourceFrames = rawAudioByteArray.size() / (numChannels * sizeof(AudioConstants::AudioSample));
int maxDestinationFrames = resampler.getMaxOutput(numSourceFrames);
int maxDestinationBytes = maxDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample);
_byteArray.resize(maxDestinationBytes);
_data.resize(maxDestinationBytes);
int numDestinationFrames = resampler.render((int16_t*)rawAudioByteArray.data(),
(int16_t*)_byteArray.data(),
(int16_t*)_data.data(),
numSourceFrames);
// truncate to actual output
int numDestinationBytes = numDestinationFrames * numChannels * sizeof(AudioConstants::AudioSample);
_byteArray.resize(numDestinationBytes);
_data.resize(numDestinationBytes);
}
}
@ -163,7 +192,7 @@ struct WAVEFormat {
};
// returns wavfile sample rate, used for resampling
int Sound::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) {
int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) {
// Create a data stream to analyze the data
QDataStream waveStream(const_cast<QByteArray *>(&inputAudioByteArray), QIODevice::ReadOnly);

View file

@ -12,6 +12,7 @@
#ifndef hifi_Sound_h
#define hifi_Sound_h
#include <QRunnable>
#include <QtCore/QObject>
#include <QtNetwork/QNetworkReply>
#include <QtScript/qscriptengine.h>
@ -28,12 +29,15 @@ public:
bool isAmbisonic() const { return _isAmbisonic; }
bool isReady() const { return _isReady; }
float getDuration() const { return _duration; }
const QByteArray& getByteArray() const { return _byteArray; }
signals:
void ready();
protected slots:
void soundProcessSuccess(QByteArray data, bool stereo, bool ambisonic, float duration);
void soundProcessError(int error, QString str);
private:
QByteArray _byteArray;
@ -42,10 +46,33 @@ private:
bool _isReady;
float _duration; // In seconds
virtual void downloadFinished(const QByteArray& data) override;
};
class SoundProcessor : public QObject, public QRunnable {
Q_OBJECT
public:
SoundProcessor(const QUrl& url, const QByteArray& data, bool stereo, bool ambisonic)
: _url(url), _data(data), _isStereo(stereo), _isAmbisonic(ambisonic)
{
}
virtual void run() override;
void downSample(const QByteArray& rawAudioByteArray, int sampleRate);
int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);
virtual void downloadFinished(const QByteArray& data) override;
signals:
void onSuccess(QByteArray data, bool stereo, bool ambisonic, float duration);
void onError(int error, QString str);
private:
QUrl _url;
QByteArray _data;
bool _isStereo;
bool _isAmbisonic;
float _duration;
};
typedef QSharedPointer<Sound> SharedSoundPointer;

View file

@ -23,7 +23,6 @@
#include <PerfStat.h>
#include <SceneScriptingInterface.h>
#include <ScriptEngine.h>
#include <procedural/ProceduralSkybox.h>
#include "EntityTreeRenderer.h"
@ -141,7 +140,6 @@ void EntityTreeRenderer::clear() {
// reset the zone to the default (while we load the next scene)
_layeredZones.clear();
applyZoneAndHasSkybox(nullptr);
OctreeRenderer::clear();
}
@ -196,14 +194,6 @@ void EntityTreeRenderer::update() {
// Handle enter/leave entity logic
bool updated = checkEnterLeaveEntities();
// If we haven't already updated and previously attempted to load a texture,
// check if the texture loaded and apply it
if (!updated &&
((_pendingAmbientTexture && (!_ambientTexture || _ambientTexture->isLoaded())) ||
(_pendingSkyboxTexture && (!_skyboxTexture || _skyboxTexture->isLoaded())))) {
applySkyboxAndHasAmbient();
}
// Even if we're not moving the mouse, if we started clicking on an entity and we have
// not yet released the hold then this is still considered a holdingClickOnEntity event
// and we want to simulate this message here as well as in mouse move
@ -371,176 +361,6 @@ bool EntityTreeRenderer::applyLayeredZones() {
return true;
}
bool EntityTreeRenderer::applyZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone) {
auto textureCache = DependencyManager::get<TextureCache>();
auto scene = DependencyManager::get<SceneScriptingInterface>();
auto sceneStage = scene->getStage();
auto skyStage = scene->getSkyStage();
auto sceneKeyLight = sceneStage->getKeyLight();
// If there is no zone, use the default background
if (!zone) {
_zoneUserData = QString();
skyStage->getSkybox()->clear();
_pendingSkyboxTexture = false;
_skyboxTexture.clear();
_pendingAmbientTexture = false;
_ambientTexture.clear();
sceneKeyLight->resetAmbientSphere();
sceneKeyLight->setAmbientMap(nullptr);
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT);
return false;
}
// Set the keylight
sceneKeyLight->setColor(ColorUtils::toVec3(zone->getKeyLightProperties().getColor()));
sceneKeyLight->setIntensity(zone->getKeyLightProperties().getIntensity());
sceneKeyLight->setAmbientIntensity(zone->getKeyLightProperties().getAmbientIntensity());
sceneKeyLight->setDirection(zone->getKeyLightProperties().getDirection());
// Set the stage
bool isSunModelEnabled = zone->getStageProperties().getSunModelEnabled();
sceneStage->setSunModelEnable(isSunModelEnabled);
if (isSunModelEnabled) {
sceneStage->setLocation(zone->getStageProperties().getLongitude(),
zone->getStageProperties().getLatitude(),
zone->getStageProperties().getAltitude());
auto sceneTime = sceneStage->getTime();
sceneTime->setHour(zone->getStageProperties().calculateHour());
sceneTime->setDay(zone->getStageProperties().calculateDay());
}
// Set the ambient texture
_ambientTextureURL = zone->getKeyLightProperties().getAmbientURL();
if (_ambientTextureURL.isEmpty()) {
_pendingAmbientTexture = false;
_ambientTexture.clear();
} else {
_pendingAmbientTexture = true;
}
// Set the skybox texture
return layerZoneAndHasSkybox(zone);
}
bool EntityTreeRenderer::layerZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone) {
assert(zone);
auto textureCache = DependencyManager::get<TextureCache>();
auto scene = DependencyManager::get<SceneScriptingInterface>();
auto skyStage = scene->getSkyStage();
auto skybox = skyStage->getSkybox();
bool hasSkybox = false;
switch (zone->getBackgroundMode()) {
case BACKGROUND_MODE_SKYBOX:
hasSkybox = true;
skybox->setColor(zone->getSkyboxProperties().getColorVec3());
if (_zoneUserData != zone->getUserData()) {
_zoneUserData = zone->getUserData();
std::dynamic_pointer_cast<ProceduralSkybox>(skybox)->parse(_zoneUserData);
}
_skyboxTextureURL = zone->getSkyboxProperties().getURL();
if (_skyboxTextureURL.isEmpty()) {
_pendingSkyboxTexture = false;
_skyboxTexture.clear();
} else {
_pendingSkyboxTexture = true;
}
applySkyboxAndHasAmbient();
skyStage->setBackgroundMode(model::SunSkyStage::SKY_BOX);
break;
case BACKGROUND_MODE_INHERIT:
default:
// Clear the skybox to release its textures
skybox->clear();
_zoneUserData = QString();
_pendingSkyboxTexture = false;
_skyboxTexture.clear();
// Let the application background through
if (applySkyboxAndHasAmbient()) {
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_TEXTURE);
} else {
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE);
}
break;
}
return hasSkybox;
}
bool EntityTreeRenderer::applySkyboxAndHasAmbient() {
auto textureCache = DependencyManager::get<TextureCache>();
auto scene = DependencyManager::get<SceneScriptingInterface>();
auto sceneStage = scene->getStage();
auto skyStage = scene->getSkyStage();
auto sceneKeyLight = sceneStage->getKeyLight();
auto skybox = skyStage->getSkybox();
bool isAmbientSet = false;
if (_pendingAmbientTexture && !_ambientTexture) {
_ambientTexture = textureCache->getTexture(_ambientTextureURL, image::TextureUsage::CUBE_TEXTURE);
}
if (_ambientTexture && _ambientTexture->isLoaded()) {
_pendingAmbientTexture = false;
auto texture = _ambientTexture->getGPUTexture();
if (texture) {
isAmbientSet = true;
sceneKeyLight->setAmbientSphere(texture->getIrradiance());
sceneKeyLight->setAmbientMap(texture);
} else {
qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL();
}
}
if (_pendingSkyboxTexture &&
(!_skyboxTexture || (_skyboxTexture->getURL() != _skyboxTextureURL))) {
_skyboxTexture = textureCache->getTexture(_skyboxTextureURL, image::TextureUsage::CUBE_TEXTURE);
}
if (_skyboxTexture && _skyboxTexture->isLoaded()) {
_pendingSkyboxTexture = false;
auto texture = _skyboxTexture->getGPUTexture();
if (texture) {
skybox->setCubemap(texture);
if (!isAmbientSet) {
sceneKeyLight->setAmbientSphere(texture->getIrradiance());
sceneKeyLight->setAmbientMap(texture);
isAmbientSet = true;
}
} else {
qCDebug(entitiesrenderer) << "Failed to load skybox texture:" << _skyboxTexture->getURL();
skybox->setCubemap(nullptr);
}
} else {
skybox->setCubemap(nullptr);
}
if (!isAmbientSet) {
sceneKeyLight->resetAmbientSphere();
sceneKeyLight->setAmbientMap(nullptr);
}
return isAmbientSet;
}
const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) {
const FBXGeometry* result = NULL;
@ -1186,8 +1006,6 @@ std::pair<EntityTreeRenderer::LayeredZones::iterator, bool> EntityTreeRenderer::
void EntityTreeRenderer::LayeredZones::apply() {
assert(_entityTreeRenderer);
applyPartial(begin());
}
void EntityTreeRenderer::LayeredZones::update(std::shared_ptr<ZoneEntityItem> zone) {
@ -1202,12 +1020,6 @@ void EntityTreeRenderer::LayeredZones::update(std::shared_ptr<ZoneEntityItem> zo
} else {
LayeredZone zoneLayer(zone);
// should we update? only if this zone is tighter than the current skybox zone
bool shouldUpdate = false;
if (_skyboxLayer == end() || zoneLayer <= *_skyboxLayer) {
shouldUpdate = true;
}
// find this zone's layer, if it exists
iterator layer = end();
auto it = _map.find(zoneLayer.id);
@ -1227,41 +1039,9 @@ void EntityTreeRenderer::LayeredZones::update(std::shared_ptr<ZoneEntityItem> zo
std::tie(layer, std::ignore) = insert(zoneLayer);
_map.emplace(layer->id, layer);
}
if (shouldUpdate) {
applyPartial(layer);
}
}
}
void EntityTreeRenderer::LayeredZones::applyPartial(iterator layer) {
bool hasSkybox = false;
_skyboxLayer = end();
if (layer == end()) {
if (empty()) {
_entityTreeRenderer->applyZoneAndHasSkybox(nullptr);
return;
} else { // a layer was removed - reapply from beginning
layer = begin();
}
}
if (layer == begin()) {
hasSkybox = _entityTreeRenderer->applyZoneAndHasSkybox(layer->zone);
} else {
hasSkybox = _entityTreeRenderer->layerZoneAndHasSkybox(layer->zone);
}
if (layer != end()) {
while (!hasSkybox && ++layer != end()) {
hasSkybox = _entityTreeRenderer->layerZoneAndHasSkybox(layer->zone);
}
}
_skyboxLayer = layer;
}
bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) {
bool result = std::equal(other.begin(), other._skyboxLayer, begin());
if (result) {

View file

@ -147,9 +147,6 @@ private:
void addEntityToScene(EntityItemPointer entity);
bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr);
bool applyZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone);
bool layerZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone);
bool applySkyboxAndHasAmbient();
bool applyLayeredZones();
void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false);

View file

@ -13,14 +13,90 @@
#include <gpu/Batch.h>
#include <model/Stage.h>
#include <AbstractViewStateInterface.h>
#include <DependencyManager.h>
#include <GeometryCache.h>
#include <PerfStat.h>
#include <procedural/ProceduralSkybox.h>
#include "EntityTreeRenderer.h"
#include "RenderableEntityItem.h"
#include <LightPayload.h>
#include "DeferredLightingEffect.h"
class RenderableZoneEntityItemMeta {
public:
RenderableZoneEntityItemMeta(EntityItemPointer entity);
~RenderableZoneEntityItemMeta();
typedef render::Payload<RenderableZoneEntityItemMeta> Payload;
typedef Payload::DataPointer Pointer;
EntityItemPointer entity;
void render(RenderArgs* args);
void setVisible(bool visible) { _isVisible = visible; }
bool isVisible() const { return _isVisible; }
render::Item::Bound& editBound() { _needUpdate = true; return _bound; }
model::LightPointer editSunLight() { _needSunUpdate = true; return _sunLight; }
model::LightPointer editAmbientLight() { _needAmbientUpdate = true; return _ambientLight; }
model::SunSkyStagePointer editBackground() { _needBackgroundUpdate = true; return _background; }
model::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); }
void setAmbientURL(const QString& ambientUrl);
void setSkyboxURL(const QString& skyboxUrl);
void setBackgroundMode(BackgroundMode mode);
void setSkyboxColor(const glm::vec3& color);
void setProceduralUserData(QString userData);
protected:
render::Item::Bound _bound;
model::LightPointer _sunLight;
model::LightPointer _ambientLight;
model::SunSkyStagePointer _background;
BackgroundMode _backgroundMode { BACKGROUND_MODE_INHERIT };
LightStagePointer _stage;
LightStage::Index _sunIndex { LightStage::INVALID_INDEX };
LightStage::Index _ambientIndex { LightStage::INVALID_INDEX };
BackgroundStagePointer _backgroundStage;
BackgroundStage::Index _backgroundIndex { BackgroundStage::INVALID_INDEX };
bool _needUpdate { true };
bool _needSunUpdate { true };
bool _needAmbientUpdate { true };
bool _needBackgroundUpdate { true };
bool _isVisible { true };
void updateAmbientMap();
void updateSkyboxMap();
// More attributes used for rendering:
QString _ambientTextureURL;
NetworkTexturePointer _ambientTexture;
bool _pendingAmbientTexture { false };
bool _validAmbientTexture { false };
QString _skyboxTextureURL;
NetworkTexturePointer _skyboxTexture;
bool _pendingSkyboxTexture { false };
bool _validSkyboxTexture { false };
QString _proceduralUserData;
};
// Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
static const float SPHERE_ENTITY_SCALE = 0.5f;
@ -67,8 +143,15 @@ bool RenderableZoneEntityItem::setProperties(const EntityItemProperties& propert
void RenderableZoneEntityItem::somethingChangedNotification() {
DependencyManager::get<EntityTreeRenderer>()->updateZone(_id);
// If graphics elements are changed, we need to update the render items
notifyChangedRenderItem();
// Poopagate back to parent
ZoneEntityItem::somethingChangedNotification();
}
int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
@ -174,18 +257,158 @@ bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
return false;
}
class RenderableZoneEntityItemMeta {
public:
RenderableZoneEntityItemMeta(EntityItemPointer entity) : entity(entity){ }
typedef render::Payload<RenderableZoneEntityItemMeta> Payload;
typedef Payload::DataPointer Pointer;
bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,
render::Transaction& transaction) {
_myMetaItem = scene->allocateID();
auto renderData = std::make_shared<RenderableZoneEntityItemMeta>(self);
auto renderPayload = std::make_shared<RenderableZoneEntityItemMeta::Payload>(renderData);
updateKeyZoneItemFromEntity((*renderData));
updateKeySunFromEntity((*renderData));
updateKeyAmbientFromEntity((*renderData));
updateKeyBackgroundFromEntity((*renderData));
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
renderPayload->addStatusGetters(statusGetters);
transaction.resetItem(_myMetaItem, renderPayload);
return true;
}
void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
render::Transaction& transaction) {
transaction.removeItem(_myMetaItem);
render::Item::clearID(_myMetaItem);
if (_model) {
_model->removeFromScene(scene, transaction);
}
}
void RenderableZoneEntityItem::notifyBoundChanged() {
notifyChangedRenderItem();
}
void RenderableZoneEntityItem::updateKeySunFromEntity(RenderableZoneEntityItemMeta& keyZonePayload) {
auto sunLight = keyZonePayload.editSunLight();
sunLight->setType(model::Light::SUN);
sunLight->setPosition(this->getPosition());
sunLight->setOrientation(this->getRotation());
// Set the keylight
sunLight->setColor(ColorUtils::toVec3(this->getKeyLightProperties().getColor()));
sunLight->setIntensity(this->getKeyLightProperties().getIntensity());
sunLight->setDirection(this->getKeyLightProperties().getDirection());
}
void RenderableZoneEntityItem::updateKeyAmbientFromEntity(RenderableZoneEntityItemMeta& keyZonePayload) {
auto ambientLight = keyZonePayload.editAmbientLight();
ambientLight->setType(model::Light::AMBIENT);
ambientLight->setPosition(this->getPosition());
ambientLight->setOrientation(this->getRotation());
// Set the keylight
ambientLight->setAmbientIntensity(this->getKeyLightProperties().getAmbientIntensity());
if (this->getKeyLightProperties().getAmbientURL().isEmpty()) {
keyZonePayload.setAmbientURL(this->getSkyboxProperties().getURL());
} else {
keyZonePayload.setAmbientURL(this->getKeyLightProperties().getAmbientURL());
}
}
void RenderableZoneEntityItem::updateKeyBackgroundFromEntity(RenderableZoneEntityItemMeta& keyZonePayload) {
auto background = keyZonePayload.editBackground();
keyZonePayload.setBackgroundMode(this->getBackgroundMode());
keyZonePayload.setSkyboxColor(this->getSkyboxProperties().getColorVec3());
keyZonePayload.setProceduralUserData(this->getUserData());
keyZonePayload.setSkyboxURL(this->getSkyboxProperties().getURL());
}
void RenderableZoneEntityItem::updateKeyZoneItemFromEntity(RenderableZoneEntityItemMeta& keyZonePayload) {
keyZonePayload.setVisible(this->getVisible());
bool success;
keyZonePayload.editBound() = this->getAABox(success);
if (!success) {
keyZonePayload.editBound() = render::Item::Bound();
}
/* TODO: Implement the sun model behavior / Keep this code here for reference, this is how we
{
// Set the stage
bool isSunModelEnabled = this->getStageProperties().getSunModelEnabled();
sceneStage->setSunModelEnable(isSunModelEnabled);
if (isSunModelEnabled) {
sceneStage->setLocation(this->getStageProperties().getLongitude(),
this->getStageProperties().getLatitude(),
this->getStageProperties().getAltitude());
auto sceneTime = sceneStage->getTime();
sceneTime->setHour(this->getStageProperties().calculateHour());
sceneTime->setDay(this->getStageProperties().calculateDay());
}
}*/
}
void RenderableZoneEntityItem::sceneUpdateRenderItemFromEntity(render::Transaction& transaction) {
if (!render::Item::isValidID(_myMetaItem)) {
return;
}
bool sunChanged = _keyLightPropertiesChanged;
bool backgroundChanged = _backgroundPropertiesChanged;
bool stageChanged = _stagePropertiesChanged;
bool skyboxChanged = _skyboxPropertiesChanged;
transaction.updateItem<RenderableZoneEntityItemMeta>(_myMetaItem, [=](RenderableZoneEntityItemMeta& data) {
updateKeyZoneItemFromEntity(data);
if (sunChanged) {
updateKeySunFromEntity(data);
}
if (sunChanged || skyboxChanged) {
updateKeyAmbientFromEntity(data);
}
if (backgroundChanged || skyboxChanged) {
updateKeyBackgroundFromEntity(data);
}
});
}
void RenderableZoneEntityItem::notifyChangedRenderItem() {
if (!render::Item::isValidID(_myMetaItem)) {
return;
}
render::Transaction transaction;
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
sceneUpdateRenderItemFromEntity(transaction);
scene->enqueueTransaction(transaction);
}
EntityItemPointer entity;
};
namespace render {
template <> const ItemKey payloadGetKey(const RenderableZoneEntityItemMeta::Pointer& payload) {
return ItemKey::Builder::opaqueShape();
return ItemKey::Builder().withTypeMeta().build();
}
template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) {
@ -200,51 +423,199 @@ namespace render {
return render::Item::Bound();
}
template <> void payloadRender(const RenderableZoneEntityItemMeta::Pointer& payload, RenderArgs* args) {
if (args) {
if (payload && payload->entity) {
payload->entity->render(args);
payload->render(args);
}
}
RenderableZoneEntityItemMeta::RenderableZoneEntityItemMeta(EntityItemPointer entity) :
entity(entity),
_sunLight(std::make_shared<model::Light>()),
_ambientLight(std::make_shared<model::Light>()),
_background(std::make_shared<model::SunSkyStage>())
{
_background->setSkybox(std::make_shared<ProceduralSkybox>());
}
RenderableZoneEntityItemMeta::~RenderableZoneEntityItemMeta() {
if (_stage) {
if (!LightStage::isIndexInvalid(_sunIndex)) {
_stage->removeLight(_sunIndex);
}
if (!LightStage::isIndexInvalid(_ambientIndex)) {
_stage->removeLight(_ambientIndex);
}
}
if (_backgroundStage) {
if (!BackgroundStage::isIndexInvalid(_backgroundIndex)) {
_backgroundStage->removeBackground(_backgroundIndex);
}
}
}
void RenderableZoneEntityItemMeta::setAmbientURL(const QString& ambientUrl) {
// nothing change if nothing change
if (_ambientTextureURL == ambientUrl) {
return;
}
_ambientTextureURL = ambientUrl;
if (_ambientTextureURL.isEmpty()) {
_validAmbientTexture = false;
_pendingAmbientTexture = false;
_ambientTexture.clear();
_ambientLight->setAmbientMap(nullptr);
_ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY);
} else {
_pendingAmbientTexture = true;
auto textureCache = DependencyManager::get<TextureCache>();
_ambientTexture = textureCache->getTexture(_ambientTextureURL, image::TextureUsage::CUBE_TEXTURE);
// keep whatever is assigned on the ambient map/sphere until texture is loaded
}
}
void RenderableZoneEntityItemMeta::updateAmbientMap() {
if (_pendingAmbientTexture) {
if (_ambientTexture && _ambientTexture->isLoaded()) {
_pendingAmbientTexture = false;
auto texture = _ambientTexture->getGPUTexture();
if (texture) {
if (texture->getIrradiance()) {
_ambientLight->setAmbientSphere(*texture->getIrradiance());
} else {
_ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY);
}
editAmbientLight()->setAmbientMap(texture);
_validAmbientTexture = true;
} else {
qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL();
}
}
}
}
bool RenderableZoneEntityItem::addToScene(EntityItemPointer self, const render::ScenePointer& scene,
render::Transaction& transaction) {
_myMetaItem = scene->allocateID();
auto renderData = std::make_shared<RenderableZoneEntityItemMeta>(self);
auto renderPayload = std::make_shared<RenderableZoneEntityItemMeta::Payload>(renderData);
render::Item::Status::Getters statusGetters;
makeEntityItemStatusGetters(getThisPointer(), statusGetters);
renderPayload->addStatusGetters(statusGetters);
transaction.resetItem(_myMetaItem, renderPayload);
return true;
}
void RenderableZoneEntityItem::removeFromScene(EntityItemPointer self, const render::ScenePointer& scene,
render::Transaction& transaction) {
transaction.removeItem(_myMetaItem);
render::Item::clearID(_myMetaItem);
if (_model) {
_model->removeFromScene(scene, transaction);
}
}
void RenderableZoneEntityItem::notifyBoundChanged() {
if (!render::Item::isValidID(_myMetaItem)) {
void RenderableZoneEntityItemMeta::setSkyboxURL(const QString& skyboxUrl) {
// nothing change if nothing change
if (_skyboxTextureURL == skyboxUrl) {
return;
}
render::Transaction transaction;
render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene();
if (scene) {
transaction.updateItem<RenderableZoneEntityItemMeta>(_myMetaItem, [](RenderableZoneEntityItemMeta& data) {
});
_skyboxTextureURL = skyboxUrl;
scene->enqueueTransaction(transaction);
if (_skyboxTextureURL.isEmpty()) {
_validSkyboxTexture = false;
_pendingSkyboxTexture = false;
_skyboxTexture.clear();
editSkybox()->setCubemap(nullptr);
} else {
qCWarning(entitiesrenderer) << "RenderableZoneEntityItem::notifyBoundChanged(), Unexpected null scene, possibly during application shutdown";
_pendingSkyboxTexture = true;
auto textureCache = DependencyManager::get<TextureCache>();
_skyboxTexture = textureCache->getTexture(_skyboxTextureURL, image::TextureUsage::CUBE_TEXTURE);
}
}
void RenderableZoneEntityItemMeta::updateSkyboxMap() {
if (_pendingSkyboxTexture) {
if (_skyboxTexture && _skyboxTexture->isLoaded()) {
_pendingSkyboxTexture = false;
auto texture = _skyboxTexture->getGPUTexture();
if (texture) {
editSkybox()->setCubemap(texture);
_validSkyboxTexture = true;
} else {
qCDebug(entitiesrenderer) << "Failed to load Skybox texture:" << _skyboxTexture->getURL();
}
}
}
}
void RenderableZoneEntityItemMeta::setBackgroundMode(BackgroundMode mode) {
_backgroundMode = mode;
}
void RenderableZoneEntityItemMeta::setSkyboxColor(const glm::vec3& color) {
editSkybox()->setColor(color);
}
void RenderableZoneEntityItemMeta::setProceduralUserData(QString userData) {
if (_proceduralUserData != userData) {
_proceduralUserData = userData;
std::dynamic_pointer_cast<ProceduralSkybox>(editSkybox())->parse(_proceduralUserData);
}
}
void RenderableZoneEntityItemMeta::render(RenderArgs* args) {
if (!_stage) {
_stage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
}
if (!_backgroundStage) {
_backgroundStage = DependencyManager::get<DeferredLightingEffect>()->getBackgroundStage();
}
{ // Sun
// Need an update ?
if (_needSunUpdate) {
// Do we need to allocate the light in the stage ?
if (LightStage::isIndexInvalid(_sunIndex)) {
_sunIndex = _stage->addLight(_sunLight);
} else {
_stage->updateLightArrayBuffer(_sunIndex);
}
_needSunUpdate = false;
}
}
{ // Ambient
updateAmbientMap();
// Need an update ?
if (_needAmbientUpdate) {
// Do we need to allocate the light in the stage ?
if (LightStage::isIndexInvalid(_ambientIndex)) {
_ambientIndex = _stage->addLight(_ambientLight);
} else {
_stage->updateLightArrayBuffer(_ambientIndex);
}
_needAmbientUpdate = false;
}
}
{ // Skybox
updateSkyboxMap();
if (_needBackgroundUpdate) {
if (BackgroundStage::isIndexInvalid(_backgroundIndex)) {
_backgroundIndex = _backgroundStage->addBackground(_background);
} else {
}
_needBackgroundUpdate = false;
}
}
if (isVisible()) {
// FInally, push the light visible in the frame
// THe directional key light for sure
_stage->_currentFrame.pushSunLight(_sunIndex);
// The ambient light only if it has a valid texture to render with
if (_validAmbientTexture || _validSkyboxTexture) {
_stage->_currentFrame.pushAmbientLight(_ambientIndex);
}
// The background only if the mode is not inherit
if (_backgroundMode != BACKGROUND_MODE_INHERIT) {
_backgroundStage->_currentFrame.pushBackground(_backgroundIndex);
}
}
}

View file

@ -16,6 +16,9 @@
#include <ZoneEntityItem.h>
class NetworkGeometry;
class KeyLightPayload;
class RenderableZoneEntityItemMeta;
class RenderableZoneEntityItem : public ZoneEntityItem {
public:
@ -42,7 +45,7 @@ public:
virtual void removeFromScene(EntityItemPointer self, const render::ScenePointer& scene, render::Transaction& transaction) override;
render::ItemID getRenderItemID() const { return _myMetaItem; }
private:
virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); notifyBoundChanged(); }
virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); notifyBoundChanged(); }
@ -52,10 +55,18 @@ private:
template<typename Lambda>
void changeProperties(Lambda functor);
void notifyChangedRenderItem();
void sceneUpdateRenderItemFromEntity(render::Transaction& transaction);
void updateKeyZoneItemFromEntity(RenderableZoneEntityItemMeta& keyZonePayload);
void updateKeySunFromEntity(RenderableZoneEntityItemMeta& keyZonePayload);
void updateKeyAmbientFromEntity(RenderableZoneEntityItemMeta& keyZonePayload);
void updateKeyBackgroundFromEntity(RenderableZoneEntityItemMeta& keyZonePayload);
ModelPointer _model;
bool _needsInitialSimulation;
render::ItemID _myMetaItem{ render::Item::INVALID_ITEM_ID };
};

View file

@ -71,22 +71,6 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) {
bool somethingChanged = false;
somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
bool somethingChangedInKeyLight = _keyLightProperties.setProperties(properties);
bool somethingChangedInStage = _stageProperties.setProperties(properties);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundMode, setBackgroundMode);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL);
bool somethingChangedInSkybox = _skyboxProperties.setProperties(properties);
somethingChanged = somethingChanged || somethingChangedInKeyLight || somethingChangedInStage || somethingChangedInSkybox;
if (somethingChanged) {
bool wantDebug = false;
if (wantDebug) {
@ -101,6 +85,30 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) {
return somethingChanged;
}
bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& properties) {
bool somethingChanged = EntityItem::setSubClassProperties(properties); // set the properties in our base class
_keyLightPropertiesChanged = _keyLightProperties.setProperties(properties);
_stagePropertiesChanged = _stageProperties.setProperties(properties);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundMode, setBackgroundMode);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL);
_skyboxPropertiesChanged = _skyboxProperties.setProperties(properties);
somethingChanged = somethingChanged || _keyLightPropertiesChanged || _stagePropertiesChanged || _skyboxPropertiesChanged;
return somethingChanged;
}
int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
@ -109,14 +117,14 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
const unsigned char* dataAt = data;
int bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, somethingChanged);
propertyFlags, overwriteLocalData, _keyLightPropertiesChanged);
somethingChanged = somethingChanged || _keyLightPropertiesChanged;
bytesRead += bytesFromKeylight;
dataAt += bytesFromKeylight;
int bytesFromStage = _stageProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, somethingChanged);
propertyFlags, overwriteLocalData, _stagePropertiesChanged);
somethingChanged = somethingChanged || _stagePropertiesChanged;
bytesRead += bytesFromStage;
dataAt += bytesFromStage;
@ -125,7 +133,8 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode);
int bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, somethingChanged);
propertyFlags, overwriteLocalData, _skyboxPropertiesChanged);
somethingChanged = somethingChanged || _skyboxPropertiesChanged;
bytesRead += bytesFromSkybox;
dataAt += bytesFromSkybox;
@ -185,6 +194,16 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL());
}
void ZoneEntityItem::somethingChangedNotification() {
EntityItem::somethingChangedNotification();
withWriteLock([&] {
_keyLightPropertiesChanged = false;
_backgroundPropertiesChanged = false;
_stagePropertiesChanged = false;
_skyboxPropertiesChanged = false;
});
}
void ZoneEntityItem::debugDump() const {
quint64 now = usecTimestampNow();
qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------";

View file

@ -29,10 +29,16 @@ public:
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
virtual bool setSubClassProperties(const EntityItemProperties& properties) override;
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
/// Override this in your derived class if you'd like to be informed when something about the state of the entity
/// has changed. This will be called with properties change or when new data is loaded from a stream
/// Overriding this function to capture the information that a keylight / Ambient / skybox properties has changed
virtual void somethingChangedNotification() override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
@ -64,7 +70,7 @@ public:
const KeyLightPropertyGroup& getKeyLightProperties() const { return _keyLightProperties; }
void setBackgroundMode(BackgroundMode value) { _backgroundMode = value; }
void setBackgroundMode(BackgroundMode value) { _backgroundMode = value; _backgroundPropertiesChanged = true; }
BackgroundMode getBackgroundMode() const { return _backgroundMode; }
const SkyboxPropertyGroup& getSkyboxProperties() const { return _skyboxProperties; }
@ -106,6 +112,14 @@ protected:
bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED };
QString _filterURL { DEFAULT_FILTER_URL };
// Dirty flags turn true when either keylight properties is changing values.
// This gets back to false in the somethingChangedNotification() call
// Which is called after a setProperties() or a readEntitySubClassFromBUfferCall on the entity.
bool _keyLightPropertiesChanged { false };
bool _backgroundPropertiesChanged { false };
bool _skyboxPropertiesChanged { false };
bool _stagePropertiesChanged { false };
static bool _drawZoneBoundaries;
static bool _zonesArePickable;
};

View file

@ -493,10 +493,6 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(const QImage& s
if (validAlpha) {
processTextureAlpha(image, validAlpha, alphaAsMask);
// NOTE: This disables BC1a compression because it was producing odd artifacts on text textures
// for the tutorial. Instead we use BC3 (which is larger) but doesn't produce the same artifacts).
alphaAsMask = false;
}
gpu::TexturePointer theTexture = nullptr;
@ -506,7 +502,9 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(const QImage& s
gpu::Element formatGPU;
if (isColorTexturesCompressionEnabled()) {
if (validAlpha) {
formatGPU = alphaAsMask ? gpu::Element::COLOR_COMPRESSED_SRGBA_MASK : gpu::Element::COLOR_COMPRESSED_SRGBA;
// NOTE: This disables BC1a compression because it was producing odd artifacts on text textures
// for the tutorial. Instead we use BC3 (which is larger) but doesn't produce the same artifacts).
formatGPU = gpu::Element::COLOR_COMPRESSED_SRGBA;
} else {
formatGPU = gpu::Element::COLOR_COMPRESSED_SRGB;
}

View file

@ -212,10 +212,11 @@ namespace khronos {
template <uint32_t ALIGNMENT>
inline uint32_t evalAlignedCompressedBlockCount(uint32_t value) {
// FIXME add static assert that ALIGNMENT is a power of 2
return (value + (ALIGNMENT - 1) / ALIGNMENT);
static uint32_t ALIGNMENT_REMAINDER = ALIGNMENT - 1;
return (value + ALIGNMENT_REMAINDER) / ALIGNMENT;
}
inline uint8_t evalBlockAlignemnt(InternalFormat format, uint32_t value) {
inline uint32_t evalCompressedBlockCount(InternalFormat format, uint32_t value) {
switch (format) {
case InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT: // BC1
case InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: // BC1A

View file

@ -30,7 +30,7 @@ uint32_t Header::evalMaxDimension() const {
uint32_t Header::evalPixelOrBlockDimension(uint32_t pixelDimension) const {
if (isCompressed()) {
return khronos::gl::texture::evalBlockAlignemnt(getGLInternaFormat(), pixelDimension);
return khronos::gl::texture::evalCompressedBlockCount(getGLInternaFormat(), pixelDimension);
}
return pixelDimension;
}

View file

@ -145,7 +145,7 @@ void Light::setAmbientSpherePreset(gpu::SphericalHarmonics::Preset preset) {
_ambientSchemaBuffer.edit().ambientSphere.assignPreset(preset);
}
void Light::setAmbientMap(gpu::TexturePointer ambientMap) {
void Light::setAmbientMap(const gpu::TexturePointer& ambientMap) {
_ambientMap = ambientMap;
if (ambientMap) {
setAmbientMapNumMips(_ambientMap->getNumMips());

View file

@ -68,7 +68,8 @@ public:
enum Type {
SUN = 0,
AMBIENT = 0,
SUN,
POINT,
SPOT,
@ -112,7 +113,6 @@ public:
void setIntensity(float intensity);
bool isRanged() const { return (getType() == POINT) || (getType() == SPOT); }
bool hasAmbient() const { return (getType() == SUN); }
// FalloffRradius is the physical radius of the light sphere through which energy shines,
// expressed in meters. It is used only to calculate the falloff curve of the light.
@ -143,7 +143,7 @@ public:
const gpu::SphericalHarmonics& getAmbientSphere() const { return _ambientSchemaBuffer->ambientSphere; }
void setAmbientSpherePreset(gpu::SphericalHarmonics::Preset preset);
void setAmbientMap(gpu::TexturePointer ambientMap);
void setAmbientMap(const gpu::TexturePointer& ambientMap);
gpu::TexturePointer getAmbientMap() const { return _ambientMap; }
void setAmbientMapNumMips(uint16_t numMips);

View file

@ -25,6 +25,8 @@ typedef glm::vec3 Color;
class Skybox {
public:
typedef gpu::BufferView UniformBufferView;
Skybox();
Skybox& operator= (const Skybox& skybox);
virtual ~Skybox() {};
@ -43,6 +45,8 @@ public:
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
protected:
static const int SKYBOX_SKYMAP_SLOT { 0 };
static const int SKYBOX_CONSTANTS_SLOT { 0 };

View file

@ -241,6 +241,9 @@ void SendQueue::handshakeACK(SequenceNumber initialSequenceNumber) {
std::lock_guard<std::mutex> locker { _handshakeMutex };
_hasReceivedHandshakeACK = true;
}
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
// Notify on the handshake ACK condition
_handshakeACKCondition.notify_one();
}

View file

@ -3,7 +3,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model render)
# pull in the resources.qrc file
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
setup_hifi_library(Widgets OpenGL Network Qml Quick Script)
link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities image)
link_hifi_libraries(shared ktx gpu model model-networking render animation fbx entities image procedural)
if (NOT ANDROID)
target_nsight()

View file

@ -0,0 +1,141 @@
//
// BackgroundStage.cpp
//
// Created by Sam Gateau on 5/9/2017.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "BackgroundStage.h"
#include "DeferredLightingEffect.h"
#include <gpu/Context.h>
BackgroundStage::Index BackgroundStage::findBackground(const BackgroundPointer& background) const {
auto found = _backgroundMap.find(background);
if (found != _backgroundMap.end()) {
return INVALID_INDEX;
} else {
return (*found).second;
}
}
BackgroundStage::Index BackgroundStage::addBackground(const BackgroundPointer& background) {
auto found = _backgroundMap.find(background);
if (found == _backgroundMap.end()) {
auto backgroundId = _backgrounds.newElement(background);
// Avoid failing to allocate a background, just pass
if (backgroundId != INVALID_INDEX) {
// Insert the background and its index in the reverse map
_backgroundMap.insert(BackgroundMap::value_type(background, backgroundId));
}
return backgroundId;
} else {
return (*found).second;
}
}
BackgroundStage::BackgroundPointer BackgroundStage::removeBackground(Index index) {
BackgroundPointer removed = _backgrounds.freeElement(index);
if (removed) {
_backgroundMap.erase(removed);
}
return removed;
}
void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
const auto& lightingModel = inputs;
if (!lightingModel->isBackgroundEnabled()) {
return;
}
// Background rendering decision
auto backgroundStage = DependencyManager::get<DeferredLightingEffect>()->getBackgroundStage();
model::SunSkyStagePointer background;
model::SkyboxPointer skybox;
if (backgroundStage->_currentFrame._backgrounds.size()) {
auto backgroundId = backgroundStage->_currentFrame._backgrounds.front();
auto background = backgroundStage->getBackground(backgroundId);
if (background) {
skybox = background->getSkybox();
}
} else {
skybox = DependencyManager::get<DeferredLightingEffect>()->getDefaultSkybox();
}
/* auto backgroundMode = skyStage->getBackgroundMode();
switch (backgroundMode) {
case model::SunSkyStage::SKY_DEFAULT: {
auto scene = DependencyManager::get<SceneScriptingInterface>()->getStage();
auto sceneKeyLight = scene->getKeyLight();
scene->setSunModelEnable(false);
sceneKeyLight->setColor(ColorUtils::toVec3(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR));
sceneKeyLight->setIntensity(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY);
sceneKeyLight->setAmbientIntensity(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY);
sceneKeyLight->setDirection(KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION);
// fall through: render a skybox (if available), or the defaults (if requested)
}
case model::SunSkyStage::SKY_BOX: {*/
if (skybox && !skybox->empty()) {
PerformanceTimer perfTimer("skybox");
auto args = renderContext->args;
gpu::doInBatch(args->_context, [&](gpu::Batch& batch) {
args->_batch = &batch;
batch.enableSkybox(true);
batch.setViewportTransform(args->_viewport);
batch.setStateScissorRect(args->_viewport);
glm::mat4 projMat;
Transform viewMat;
args->getViewFrustum().evalProjectionMatrix(projMat);
args->getViewFrustum().evalViewTransform(viewMat);
batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat);
skybox->render(batch, args->getViewFrustum());
});
args->_batch = nullptr;
gpu::Batch& batch = *args->_batch;
// break;
}
// 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();
if (defaultSkyboxAmbientTexture) {
sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance());
sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture);
} else {
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(
"Failed to get a valid Default Skybox Ambient Texture ? probably because it couldn't be find during initialization step");
}
// fall through: render defaults skybox
} else {
break;
}
}
*/
}

View file

@ -0,0 +1,80 @@
//
// BackgroundStage.h
// Created by Sam Gateau on 5/9/2017.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_render_utils_BackgroundStage_h
#define hifi_render_utils_BackgroundStage_h
#include <model/Stage.h>
#include <set>
#include <unordered_map>
#include <render/IndexedContainer.h>
#include "LightingModel.h"
// Background stage to set up background-related rendering tasks
class BackgroundStage {
public:
using Index = render::indexed_container::Index;
static const Index INVALID_INDEX { render::indexed_container::INVALID_INDEX };
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using BackgroundPointer = model::SunSkyStagePointer;
using Backgrounds = render::indexed_container::IndexedPointerVector<model::SunSkyStage>;
using BackgroundMap = std::unordered_map<BackgroundPointer, Index>;
using BackgroundIndices = std::vector<Index>;
Index findBackground(const BackgroundPointer& background) const;
Index addBackground(const BackgroundPointer& background);
BackgroundPointer removeBackground(Index index);
bool checkBackgroundId(Index index) const { return _backgrounds.checkIndex(index); }
Index getNumBackgrounds() const { return _backgrounds.getNumElements(); }
Index getNumFreeBackgrounds() const { return _backgrounds.getNumFreeIndices(); }
Index getNumAllocatedBackgrounds() const { return _backgrounds.getNumAllocatedIndices(); }
BackgroundPointer getBackground(Index backgroundId) const {
return _backgrounds.get(backgroundId);
}
Backgrounds _backgrounds;
BackgroundMap _backgroundMap;
class Frame {
public:
Frame() {}
void clear() { _backgrounds.clear(); }
void pushBackground(BackgroundStage::Index index) { _backgrounds.emplace_back(index); }
BackgroundStage::BackgroundIndices _backgrounds;
};
Frame _currentFrame;
};
using BackgroundStagePointer = std::shared_ptr<BackgroundStage>;
class DrawBackgroundStage {
public:
using Inputs = LightingModelPointer;
using JobModel = render::Job::ModelI<DrawBackgroundStage, Inputs>;
void run(const render::RenderContextPointer& renderContext, const Inputs& inputs);
protected:
};
#endif

View file

@ -141,79 +141,69 @@ void DeferredLightingEffect::init() {
_globalLights.push_back(_lightStage->addLight(lp));
_lightStage->addShadow(_globalLights[0]);
}
void DeferredLightingEffect::addLight(const model::LightPointer& light) {
assert(light);
auto lightID = _lightStage->addLight(light);
if (light->getType() == model::Light::POINT) {
_pointLights.push_back(lightID);
} else {
_spotLights.push_back(lightID);
_backgroundStage = std::make_shared<BackgroundStage>();
auto textureCache = DependencyManager::get<TextureCache>();
{
PROFILE_RANGE(render, "Process Default Skybox");
auto textureCache = DependencyManager::get<TextureCache>();
auto skyboxUrl = PathUtils::resourcesPath().toStdString() + "images/Default-Sky-9-cubemap.ktx";
_defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl);
_defaultSkyboxAmbientTexture = _defaultSkyboxTexture;
_defaultSkybox->setCubemap(_defaultSkyboxTexture);
}
}
void DeferredLightingEffect::addPointLight(const glm::vec3& position, float radius, const glm::vec3& color,
float intensity, float falloffRadius) {
addSpotLight(position, radius, color, intensity, falloffRadius);
}
void DeferredLightingEffect::addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color,
float intensity, float falloffRadius, const glm::quat& orientation, float exponent, float cutoff) {
unsigned int lightID = (unsigned int)(_pointLights.size() + _spotLights.size() + _globalLights.size());
if (lightID >= _allocatedLights.size()) {
_allocatedLights.push_back(std::make_shared<model::Light>());
}
model::LightPointer lp = _allocatedLights[lightID];
lp->setPosition(position);
lp->setMaximumRadius(radius);
lp->setColor(color);
lp->setIntensity(intensity);
lp->setFalloffRadius(falloffRadius);
if (exponent == 0.0f && cutoff == PI) {
lp->setType(model::Light::POINT);
_pointLights.push_back(lightID);
} else {
lp->setOrientation(orientation);
lp->setSpotAngle(cutoff);
lp->setSpotExponent(exponent);
lp->setType(model::Light::SPOT);
_spotLights.push_back(lightID);
}
lp->setAmbientIntensity(0.5f);
lp->setAmbientMap(_defaultSkyboxAmbientTexture);
auto irradianceSH = _defaultSkyboxAmbientTexture->getIrradiance();
if (irradianceSH) {
lp->setAmbientSphere((*irradianceSH));
}
}
void DeferredLightingEffect::setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) {
PerformanceTimer perfTimer("DLE->setupBatch()");
auto keyLight = _allocatedLights[_globalLights.front()];
model::LightPointer keySunLight;
if (_lightStage && _lightStage->_currentFrame._sunLights.size()) {
keySunLight = _lightStage->getLight(_lightStage->_currentFrame._sunLights.front());
} else {
keySunLight = _allocatedLights[_globalLights.front()];
}
model::LightPointer keyAmbiLight;
if (_lightStage && _lightStage->_currentFrame._ambientLights.size()) {
keyAmbiLight = _lightStage->getLight(_lightStage->_currentFrame._ambientLights.front());
} else {
keyAmbiLight = _allocatedLights[_globalLights.front()];
}
if (lightBufferUnit >= 0) {
batch.setUniformBuffer(lightBufferUnit, keyLight->getLightSchemaBuffer());
batch.setUniformBuffer(lightBufferUnit, keySunLight->getLightSchemaBuffer());
}
if (keyLight->hasAmbient() && (ambientBufferUnit >= 0)) {
batch.setUniformBuffer(ambientBufferUnit, keyLight->getAmbientSchemaBuffer());
if (ambientBufferUnit >= 0) {
batch.setUniformBuffer(ambientBufferUnit, keyAmbiLight->getAmbientSchemaBuffer());
}
if (keyLight->getAmbientMap() && (skyboxCubemapUnit >= 0)) {
batch.setResourceTexture(skyboxCubemapUnit, keyLight->getAmbientMap());
if (keyAmbiLight->getAmbientMap() && (skyboxCubemapUnit >= 0)) {
batch.setResourceTexture(skyboxCubemapUnit, keyAmbiLight->getAmbientMap());
}
}
void DeferredLightingEffect::unsetKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) {
auto keyLight = _allocatedLights[_globalLights.front()];
if (lightBufferUnit >= 0) {
batch.setUniformBuffer(lightBufferUnit, nullptr);
}
if (keyLight->hasAmbient() && (ambientBufferUnit >= 0)) {
if ((ambientBufferUnit >= 0)) {
batch.setUniformBuffer(ambientBufferUnit, nullptr);
}
if (keyLight->getAmbientMap() && (skyboxCubemapUnit >= 0)) {
if ((skyboxCubemapUnit >= 0)) {
batch.setResourceTexture(skyboxCubemapUnit, nullptr);
}
}
@ -334,15 +324,20 @@ static void loadLightVolumeProgram(const char* vertSource, const char* fragSourc
}
void DeferredLightingEffect::setGlobalLight(const model::LightPointer& light) {
auto globalLight = _allocatedLights.front();
/* auto globalLight = _allocatedLights.front();
globalLight->setDirection(light->getDirection());
globalLight->setColor(light->getColor());
globalLight->setIntensity(light->getIntensity());
globalLight->setAmbientIntensity(light->getAmbientIntensity());
globalLight->setAmbientSphere(light->getAmbientSphere());
globalLight->setAmbientMap(light->getAmbientMap());
globalLight->setAmbientMap(light->getAmbientMap());*/
}
const model::LightPointer& DeferredLightingEffect::getGlobalLight() const {
return _allocatedLights.front();
}
#include <shared/Shapes.h>
model::MeshPointer DeferredLightingEffect::getPointLightMesh() {
@ -772,16 +767,6 @@ void RenderDeferredCleanup::run(const render::RenderContextPointer& renderContex
batch.setUniformBuffer(LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, nullptr);
}
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
// End of the Lighting pass
if (!deferredLightingEffect->_pointLights.empty()) {
deferredLightingEffect->_pointLights.clear();
}
if (!deferredLightingEffect->_spotLights.empty()) {
deferredLightingEffect->_spotLights.clear();
}
}
RenderDeferred::RenderDeferred() {

View file

@ -20,6 +20,8 @@
#include "model/Light.h"
#include "model/Geometry.h"
#include <procedural/ProceduralSkybox.h>
#include <render/CullTask.h>
#include "DeferredFrameTransform.h"
@ -28,11 +30,13 @@
#include "LightStage.h"
#include "LightClusters.h"
#include "BackgroundStage.h"
#include "SurfaceGeometryPass.h"
#include "SubsurfaceScattering.h"
#include "AmbientOcclusionEffect.h"
class RenderArgs;
struct LightLocations;
using LightLocationsPtr = std::shared_ptr<LightLocations>;
@ -43,34 +47,30 @@ class DeferredLightingEffect : public Dependency {
public:
void init();
void addLight(const model::LightPointer& light);
/// Adds a point light to render for the current frame.
void addPointLight(const glm::vec3& position, float radius, const glm::vec3& color = glm::vec3(0.0f, 0.0f, 0.0f),
float intensity = 0.5f, float falloffRadius = 0.01f);
/// Adds a spot light to render for the current frame.
void addSpotLight(const glm::vec3& position, float radius, const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f),
float intensity = 0.5f, float falloffRadius = 0.01f,
const glm::quat& orientation = glm::quat(), float exponent = 0.0f, float cutoff = PI);
void setupKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit);
void unsetKeyLightBatch(gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit);
// update global lighting
void setGlobalLight(const model::LightPointer& light);
const model::LightPointer& getGlobalLight() const;
const LightStagePointer getLightStage() { return _lightStage; }
const LightStagePointer& getLightStage() { return _lightStage; }
const BackgroundStagePointer& getBackgroundStage() { return _backgroundStage; }
void setShadowMapEnabled(bool enable) { _shadowMapEnabled = enable; };
void setAmbientOcclusionEnabled(bool enable) { _ambientOcclusionEnabled = enable; }
bool isAmbientOcclusionEnabled() const { return _ambientOcclusionEnabled; }
model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; }
gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; }
private:
DeferredLightingEffect() = default;
LightStagePointer _lightStage;
BackgroundStagePointer _backgroundStage;
bool _shadowMapEnabled{ false };
bool _ambientOcclusionEnabled{ false };
@ -113,8 +113,10 @@ private:
Lights _allocatedLights;
std::vector<int> _globalLights;
std::vector<int> _pointLights;
std::vector<int> _spotLights;
model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() };
gpu::TexturePointer _defaultSkyboxTexture;
gpu::TexturePointer _defaultSkyboxAmbientTexture;
friend class LightClusteringPass;
friend class RenderDeferredSetup;

View file

@ -81,3 +81,71 @@ void LightPayload::render(RenderArgs* args) {
}
}
namespace render {
template <> const ItemKey payloadGetKey(const KeyLightPayload::Pointer& payload) {
ItemKey::Builder builder;
builder.withTypeLight();
if (!payload || !payload->isVisible()) {
builder.withInvisible();
}
return builder.build();
}
template <> const Item::Bound payloadGetBound(const KeyLightPayload::Pointer& payload) {
if (payload) {
return payload->editBound();
}
return render::Item::Bound();
}
template <> void payloadRender(const KeyLightPayload::Pointer& payload, RenderArgs* args) {
if (args) {
if (payload) {
payload->render(args);
}
}
}
}
KeyLightPayload::KeyLightPayload() :
_light(std::make_shared<model::Light>())
{
}
KeyLightPayload::~KeyLightPayload() {
if (!LightStage::isIndexInvalid(_index)) {
if (_stage) {
_stage->removeLight(_index);
}
}
}
void KeyLightPayload::render(RenderArgs* args) {
if (!_stage) {
_stage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
}
// Do we need to allocate the light in the stage ?
if (LightStage::isIndexInvalid(_index)) {
_index = _stage->addLight(_light);
_needUpdate = false;
}
// Need an update ?
if (_needUpdate) {
_stage->updateLightArrayBuffer(_index);
_needUpdate = false;
}
if (isVisible()) {
// FInally, push the light visible in the frame
_stage->_currentFrame.pushLight(_index, _light->getType());
#ifdef WANT_DEBUG
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter());
DependencyManager::get<GeometryCache>()->renderWireSphere(batch, 0.5f, 15, 15, glm::vec4(color, 1.0f));
#endif
}
}

View file

@ -15,6 +15,7 @@
#include <model/Light.h>
#include <render/Item.h>
#include "LightStage.h"
#include "TextureCache.h"
class LightPayload {
public:
@ -46,4 +47,41 @@ namespace render {
template <> void payloadRender(const LightPayload::Pointer& payload, RenderArgs* args);
}
class KeyLightPayload {
public:
using Payload = render::Payload<KeyLightPayload>;
using Pointer = Payload::DataPointer;
KeyLightPayload();
~KeyLightPayload();
void render(RenderArgs* args);
model::LightPointer editLight() { _needUpdate = true; return _light; }
render::Item::Bound& editBound() { _needUpdate = true; return _bound; }
void setVisible(bool visible) { _isVisible = visible; }
bool isVisible() const { return _isVisible; }
// More attributes used for rendering:
NetworkTexturePointer _ambientTexture;
QString _ambientTextureURL;
bool _pendingAmbientTexture { false };
bool _validAmbientTextureURL { false };
protected:
model::LightPointer _light;
render::Item::Bound _bound;
LightStagePointer _stage;
LightStage::Index _index { LightStage::INVALID_INDEX };
bool _needUpdate { true };
bool _isVisible { true };
};
namespace render {
template <> const ItemKey payloadGetKey(const KeyLightPayload::Pointer& payload);
template <> const Item::Bound payloadGetBound(const KeyLightPayload::Pointer& payload);
template <> void payloadRender(const KeyLightPayload::Pointer& payload, RenderArgs* args);
}
#endif

View file

@ -77,7 +77,6 @@ public:
};
using Descs = std::vector<Desc>;
Index findLight(const LightPointer& light) const;
Index addLight(const LightPointer& light);
@ -118,19 +117,25 @@ public:
public:
Frame() {}
void clear() { _pointLights.clear(); _spotLights.clear(); }
void clear() { _pointLights.clear(); _spotLights.clear(); _sunLights.clear(); _ambientLights.clear(); }
void pushLight(LightStage::Index index, model::Light::Type type) {
switch (type) {
case model::Light::POINT: { pushPointLight(index); break; }
case model::Light::SPOT: { pushSpotLight(index); break; }
case model::Light::SUN: { pushSunLight(index); break; }
case model::Light::AMBIENT: { pushAmbientLight(index); break; }
default: { break; }
}
}
void pushPointLight(LightStage::Index index) { _pointLights.emplace_back(index); }
void pushSpotLight(LightStage::Index index) { _spotLights.emplace_back(index); }
void pushSunLight(LightStage::Index index) { _sunLights.emplace_back(index); }
void pushAmbientLight(LightStage::Index index) { _ambientLights.emplace_back(index); }
LightStage::LightIndices _pointLights;
LightStage::LightIndices _spotLights;
LightStage::LightIndices _sunLights;
LightStage::LightIndices _ambientLights;
};
Frame _currentFrame;

View file

@ -75,7 +75,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
const auto deferredFrameTransform = task.addJob<GenerateDeferredFrameTransform>("DeferredFrameTransform");
const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");
// GPU jobs: Start preparing the primary, deferred and lighting buffer
const auto primaryFramebuffer = task.addJob<PreparePrimaryFramebuffer>("PreparePrimaryBuffer");
@ -124,6 +123,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
// Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now.
task.addJob<DrawLight>("DrawLight", lights);
// Filter zones from the general metas bucket
const auto zones = task.addJob<ZoneRendererTask>("ZoneRenderer", metas);
// Light Clustering
// Create the cluster grid of lights, cpu job for now
const auto lightClusteringPassInputs = LightClusteringPass::Inputs(deferredFrameTransform, lightingModel, linearDepthTarget).hasVarying();
@ -136,11 +138,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
task.addJob<RenderDeferred>("RenderDeferred", deferredLightingInputs);
// Use Stencil and draw background in Lighting buffer to complete filling in the opaque
const auto backgroundInputs = DrawBackgroundDeferred::Inputs(background, lightingModel).hasVarying();
task.addJob<DrawBackgroundDeferred>("DrawBackgroundDeferred", backgroundInputs);
// Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job
task.addJob<DrawBackgroundStage>("DrawBackgroundDeferred", lightingModel);
// Render transparent objects forward in LightingBuffer
const auto transparentsInputs = DrawDeferred::Inputs(transparents, lightingModel).hasVarying();
task.addJob<DrawDeferred>("DrawTransparentDeferred", transparentsInputs, shapePlumber);
@ -162,7 +162,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
task.addJob<DrawBounds>("DrawOpaqueBounds", opaques);
task.addJob<DrawBounds>("DrawTransparentBounds", transparents);
task.addJob<ZoneRendererTask>("ZoneRenderer", opaques);
task.addJob<DrawBounds>("DrawLightBounds", lights);
task.addJob<DrawBounds>("DrawZones", zones);
}
// Overlays
@ -202,6 +203,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
auto statusIconMap = DependencyManager::get<TextureCache>()->getImageTexture(iconMapPath, image::TextureUsage::STRICT_TEXTURE);
task.addJob<DrawStatus>("DrawStatus", opaques, DrawStatus(statusIconMap));
}
task.addJob<DebugZoneLighting>("DrawZoneStack", deferredFrameTransform);
}

View file

@ -10,18 +10,199 @@
//
#include "ZoneRenderer.h"
#include <gpu/Context.h>
#include <gpu/StandardShaderLib.h>
#include <render/FilterTask.h>
#include <render/DrawTask.h>
#include "DeferredLightingEffect.h"
#include "zone_drawKeyLight_frag.h"
#include "zone_drawAmbient_frag.h"
#include "zone_drawSkybox_frag.h"
using namespace render;
class SetupZones {
public:
using Inputs = render::ItemBounds;
using JobModel = render::Job::ModelI<SetupZones, Inputs>;
SetupZones() {}
void run(const RenderContextPointer& context, const Inputs& inputs);
protected:
};
const Selection::Name ZoneRendererTask::ZONES_SELECTION { "RankedZones" };
void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& ouput) {
// Filter out the sorted list of zones
const auto zoneItems = task.addJob<render::SelectSortItems>("FilterZones", input, ZONES_SELECTION.c_str());
// just draw them...
task.addJob<DrawBounds>("DrawZones", zoneItems);
// just setup the current zone env
task.addJob<SetupZones>("SetupZones", zoneItems);
ouput = zoneItems;
}
void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) {
auto backgroundStage = DependencyManager::get<DeferredLightingEffect>()->getBackgroundStage();
backgroundStage->_currentFrame.clear();
// call render in the correct order first...
render::renderItems(context, inputs);
}
const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() {
if (!_keyLightPipeline) {
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
auto ps = gpu::Shader::createPixel(std::string(zone_drawKeyLight_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), ZONE_DEFERRED_TRANSFORM_BUFFER));
slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), ZONE_KEYLIGHT_BUFFER));
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
_keyLightPipeline = gpu::Pipeline::create(program, state);
}
return _keyLightPipeline;
}
const gpu::PipelinePointer& DebugZoneLighting::getAmbientPipeline() {
if (!_ambientPipeline) {
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
auto ps = gpu::Shader::createPixel(std::string(zone_drawAmbient_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), ZONE_DEFERRED_TRANSFORM_BUFFER));
slotBindings.insert(gpu::Shader::Binding(std::string("lightAmbientBuffer"), ZONE_AMBIENT_BUFFER));
slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), ZONE_AMBIENT_MAP));
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
_ambientPipeline = gpu::Pipeline::create(program, state);
}
return _ambientPipeline;
}
const gpu::PipelinePointer& DebugZoneLighting::getBackgroundPipeline() {
if (!_backgroundPipeline) {
auto vs = gpu::StandardShaderLib::getDrawTransformUnitQuadVS();
auto ps = gpu::Shader::createPixel(std::string(zone_drawSkybox_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), ZONE_DEFERRED_TRANSFORM_BUFFER));
slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), ZONE_SKYBOX_MAP));
slotBindings.insert(gpu::Shader::Binding(std::string("skyboxBuffer"), ZONE_SKYBOX_BUFFER));
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA);
_backgroundPipeline = gpu::Pipeline::create(program, state);
}
return _backgroundPipeline;
}
void DebugZoneLighting::run(const render::RenderContextPointer& context, const Inputs& inputs) {
RenderArgs* args = context->args;
auto deferredTransform = inputs;
auto lightStage = DependencyManager::get<DeferredLightingEffect>()->getLightStage();
std::vector<model::LightPointer> keyLightStack;
if (lightStage && lightStage->_currentFrame._sunLights.size()) {
for (auto index : lightStage->_currentFrame._sunLights) {
keyLightStack.push_back(lightStage->getLight(index));
}
}
keyLightStack.push_back(DependencyManager::get<DeferredLightingEffect>()->getGlobalLight());
std::vector<model::LightPointer> ambientLightStack;
if (lightStage && lightStage->_currentFrame._ambientLights.size()) {
for (auto index : lightStage->_currentFrame._ambientLights) {
ambientLightStack.push_back(lightStage->getLight(index));
}
}
ambientLightStack.push_back(DependencyManager::get<DeferredLightingEffect>()->getGlobalLight());
auto backgroundStage = DependencyManager::get<DeferredLightingEffect>()->getBackgroundStage();
std::vector<model::SkyboxPointer> skyboxStack;
if (backgroundStage && backgroundStage->_currentFrame._backgrounds.size()) {
for (auto index : backgroundStage->_currentFrame._backgrounds) {
auto background = backgroundStage->getBackground(index);
if (background) {
skyboxStack.push_back(background->getSkybox());
}
}
}
skyboxStack.push_back(DependencyManager::get<DeferredLightingEffect>()->getDefaultSkybox());
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
batch.setViewportTransform(args->_viewport);
auto viewFrustum = args->getViewFrustum();
batch.setProjectionTransform(viewFrustum.getProjection());
batch.resetViewTransform();
Transform model;
batch.setUniformBuffer(ZONE_DEFERRED_TRANSFORM_BUFFER, deferredTransform->getFrameTransformBuffer());
batch.setPipeline(getKeyLightPipeline());
auto numKeys = keyLightStack.size();
for (int i = numKeys - 1; i >= 0; i--) {
model.setTranslation(glm::vec3(-4.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0)));
batch.setModelTransform(model);
if (keyLightStack[i]) {
batch.setUniformBuffer(ZONE_KEYLIGHT_BUFFER, keyLightStack[i]->getLightSchemaBuffer());
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
}
batch.setPipeline(getAmbientPipeline());
auto numAmbients = ambientLightStack.size();
for (int i = numAmbients - 1; i >= 0; i--) {
model.setTranslation(glm::vec3(0.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0)));
batch.setModelTransform(model);
if (ambientLightStack[i]) {
batch.setUniformBuffer(ZONE_AMBIENT_BUFFER, ambientLightStack[i]->getAmbientSchemaBuffer());
if (ambientLightStack[i]->getAmbientMap()) {
batch.setResourceTexture(ZONE_AMBIENT_MAP, ambientLightStack[i]->getAmbientMap());
}
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
}
batch.setPipeline(getBackgroundPipeline());
auto numBackgrounds = skyboxStack.size();
for (int i = numBackgrounds - 1; i >= 0; i--) {
model.setTranslation(glm::vec3(4.0, -3.0 + (i * 1.0), -10.0 - (i * 3.0)));
batch.setModelTransform(model);
if (skyboxStack[i]) {
batch.setResourceTexture(ZONE_SKYBOX_MAP, skyboxStack[i]->getCubemap());
batch.setUniformBuffer(ZONE_SKYBOX_BUFFER, skyboxStack[i]->getSchemaBuffer());
batch.draw(gpu::TRIANGLE_STRIP, 4);
}
}
});
}

View file

@ -14,12 +14,15 @@
#include "render/Engine.h"
#include "DeferredFrameTransform.h"
class ZoneRendererConfig : public render::Task::Config {
Q_OBJECT
Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty)
public:
ZoneRendererConfig() : render::Task::Config(false) {}
ZoneRendererConfig() : render::Task::Config(
) {}
int maxDrawn { -1 };
@ -49,4 +52,40 @@ protected:
int _maxDrawn; // initialized by Config
};
class DebugZoneLighting {
public:
class Config : public render::JobConfig {
public:
Config(bool enabled = false) : JobConfig(enabled) {}
};
using Inputs = DeferredFrameTransformPointer;
using JobModel = render::Job::ModelI<DebugZoneLighting, Inputs, Config>;
DebugZoneLighting() {}
void configure(const Config& configuration) {}
void run(const render::RenderContextPointer& context, const Inputs& inputs);
protected:
enum Slots {
ZONE_DEFERRED_TRANSFORM_BUFFER = 0,
ZONE_KEYLIGHT_BUFFER,
ZONE_AMBIENT_BUFFER,
ZONE_AMBIENT_MAP,
ZONE_SKYBOX_BUFFER,
ZONE_SKYBOX_MAP,
};
gpu::PipelinePointer _keyLightPipeline;
gpu::PipelinePointer _ambientPipeline;
gpu::PipelinePointer _backgroundPipeline;
const gpu::PipelinePointer& getKeyLightPipeline();
const gpu::PipelinePointer& getAmbientPipeline();
const gpu::PipelinePointer& getBackgroundPipeline();
};
#endif

View file

@ -0,0 +1,37 @@
// Generated on <$_SCRIBE_DATE$>
//
// Created by Sam Gateau on 5/17/17.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredTransform.slh@>
<$declareDeferredFrameTransform()$>
<@func evalGlobeWidget()@>
const float SCOPE_RADIUS = 1.0;
const float SCOPE_RADIUS2 = SCOPE_RADIUS * SCOPE_RADIUS;
const float EDGE_HALFWIDTH = 0.025;
const float EDGE_HALFWIDTH2 = EDGE_HALFWIDTH * EDGE_HALFWIDTH;
const float OUT_RADIUS = SCOPE_RADIUS + EDGE_HALFWIDTH;
vec2 sphereUV = (varTexCoord0.xy * 2.0 - vec2(1.0)) * OUT_RADIUS;
float sphereR2 = dot(sphereUV.xy, sphereUV.xy);
if (sphereR2 > OUT_RADIUS * OUT_RADIUS) {
discard;
}
float sphereR = sqrt(sphereR2);
float edgeFalloff = (SCOPE_RADIUS - sphereR) / (EDGE_HALFWIDTH);
float edgeFalloff2 = min(1.0, edgeFalloff * edgeFalloff);
vec4 base = vec4(0.0, 0.0, 0.0, 1.0 - edgeFalloff2);
if (sphereR2 > SCOPE_RADIUS2) {
_fragColor = base;
return;
}
<@endfunc@>

View file

@ -0,0 +1,54 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// Created by Sam Gateau on 5/16/17.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include zone_draw.slh@>
<@include model/Light.slh@>
<@include LightingModel.slh@>
<$declareLightAmbientBuffer()$>
<@include LightAmbient.slh@>
<$declareLightingAmbient(_SCRIBE_NULL, 1, _SCRIBE_NULL, _SCRIBE_NULL)$>
in vec2 varTexCoord0;
out vec4 _fragColor;
void main(void) {
<$evalGlobeWidget()$>
vec3 spherePos = normalize(vec3(sphereUV, sqrt(1.0 - sphereR2)));
vec3 fragNormal = vec3(getViewInverse() * vec4(spherePos, 0.0));
LightAmbient lightAmbient = getLightAmbient();
float roughness = 0.1;
float levels = getLightAmbientMapNumMips(lightAmbient);
float lod = min(((roughness)* levels), levels);
vec3 ambientMap = evalSkyboxLight(fragNormal, lod).xyz;
vec3 ambientSH = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(lightAmbient), fragNormal).xyz;
// vec3 ambient = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(lightAmbient), fragNormal).xyz;
// _fragColor = vec4( 0.5 * (fragNormal + vec3(1.0)), 1.0);
vec3 color = (sphereUV.x > 0 ? ambientMap : ambientSH);
color = color * 1.0 - base.w + base.xyz * base.w;
const float INV_GAMMA_22 = 1.0 / 2.2;
_fragColor = vec4(pow(color, vec3(INV_GAMMA_22)), 1.0);
}

View file

@ -0,0 +1,57 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// Created by Sam Gateau on 5/16/17.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include zone_draw.slh@>
<@include model/Light.slh@>
<@include LightingModel.slh@>
<$declareLightBuffer()$>
<@include LightDirectional.slh@>
<$declareLightingDirectional(_SCRIBE_NULL)$>
in vec2 varTexCoord0;
out vec4 _fragColor;
void main(void) {
<$evalGlobeWidget()$>
Light light = getLight();
vec3 lightDirection = normalize(getLightDirection(light));
vec3 lightIrradiance = getLightIrradiance(light);
vec3 color = vec3(0.0);
const float INOUT_RATIO = 0.4;
const float SUN_THRESHOLD = 0.99;
vec3 outSpherePos = normalize(vec3(sphereUV, -sqrt(1.0 - sphereR2)));
vec3 outNormal = vec3(getViewInverse() * vec4(outSpherePos, 0.0));
float val = step(SUN_THRESHOLD, dot(-lightDirection, outNormal));
color = lightIrradiance * vec3(val);
if (sphereR2 < INOUT_RATIO * INOUT_RATIO * SCOPE_RADIUS2) {
vec2 inSphereUV = sphereUV / INOUT_RATIO;
vec3 inSpherePos = normalize(vec3(inSphereUV, sqrt(1.0 - dot(inSphereUV.xy, inSphereUV.xy))));
vec3 inNormal = vec3(getViewInverse() * vec4(inSpherePos, 0.0));
vec3 marbleColor = max(lightIrradiance * vec3(dot(-lightDirection, inNormal)), vec3(0.01));
color += marbleColor;
}
color = color * 1.0 - base.w + base.xyz * base.w;
const float INV_GAMMA_22 = 1.0 / 2.2;
_fragColor = vec4(pow(color, vec3(INV_GAMMA_22)), 1.0);
}

View file

@ -0,0 +1,48 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// Created by Sam Gateau on 5/16/17.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include zone_draw.slh@>
uniform samplerCube skyboxMap;
struct Skybox {
vec4 color;
};
uniform skyboxBuffer {
Skybox skybox;
};
in vec2 varTexCoord0;
out vec4 _fragColor;
void main(void) {
<$evalGlobeWidget()$>
vec3 spherePos = normalize(vec3(sphereUV, -sqrt(1.0 - sphereR2)));
vec3 direction = vec3(getViewInverse() * vec4(spherePos, 0.0));
vec3 color = skybox.color.rgb;
// blend is only set if there is a cubemap
if (skybox.color.a > 0.0) {
color = texture(skyboxMap, direction).rgb;
if (skybox.color.a < 1.0) {
color *= skybox.color.rgb;
}
}
color = color * 1.0 - base.w + base.xyz * base.w;
const float INV_GAMMA_22 = 1.0 / 2.2;
_fragColor = vec4(pow(color, vec3(INV_GAMMA_22)), 1.0);
}

View file

@ -13,7 +13,7 @@ if (WIN32)
setup_hifi_plugin(OpenGL Script Qml Widgets)
link_hifi_libraries(shared gl networking controllers ui
plugins display-plugins ui-plugins input-plugins script-engine
render-utils model gpu gpu-gl render model-networking fbx ktx image)
render-utils model gpu gpu-gl render model-networking fbx ktx image procedural)
include_hifi_library_headers(octree)

View file

@ -64,6 +64,7 @@ Column {
"Point:LightingModel:enablePointLight",
"Spot:LightingModel:enableSpotLight",
"Light Contour:LightingModel:showLightContour",
"Zone Stack:DrawZoneStack:enabled",
"Shadow:RenderShadowTask:enabled"
]
CheckBox {
@ -162,13 +163,9 @@ Column {
}
}
Row {
Column {
id: metas
CheckBox {
text: "Metas"
checked: Render.getConfig("DrawMetaBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawMetaBounds")["enabled"] = checked }
}
CheckBox {
text: "Opaques"
checked: Render.getConfig("DrawOpaqueBounds")["enabled"]
@ -189,11 +186,24 @@ Column {
checked: Render.getConfig("DrawOverlayTransparentBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawOverlayTransparentBounds")["enabled"] = checked }
}
}
Column {
CheckBox {
text: "Metas"
checked: Render.getConfig("DrawMetaBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawMetaBounds")["enabled"] = checked }
}
CheckBox {
text: "Lights"
checked: Render.getConfig("DrawLightBounds")["enabled"]
onCheckedChanged: { Render.getConfig("DrawLightBounds")["enabled"] = checked; }
}
CheckBox {
text: "Zones"
checked: Render.getConfig("DrawZones")["enabled"]
onCheckedChanged: { Render.getConfig("ZoneRenderer")["enabled"] = checked; Render.getConfig("DrawZones")["enabled"] = checked; }
}
}
}
}

View file

@ -20,9 +20,9 @@ Item {
id: stats
spacing: 8
anchors.fill:parent
property var config: Render.getConfig("Stats")
function evalEvenHeight() {
// Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ?
return (height - spacing * (children.length - 1)) / children.length
@ -81,7 +81,7 @@ Item {
color: "#1AC567"
},
{
prop: "textureGPUTransferCount",
prop: "texturePendingGPUTransferCount",
label: "Transfer",
color: "#9495FF"
}
@ -158,7 +158,7 @@ Item {
}
]
}
PlotPerf {
title: "State Changes"
height: parent.evalEvenHeight()
@ -180,7 +180,7 @@ Item {
color: "#1AC567"
}
]
}
}
property var drawOpaqueConfig: Render.getConfig("DrawOpaqueDeferred")
property var drawTransparentConfig: Render.getConfig("DrawTransparentDeferred")
@ -211,7 +211,7 @@ Item {
color: "#FED959"
}
]
}
}
PlotPerf {
title: "Timing"
@ -250,4 +250,3 @@ Item {
}
}

View file

@ -261,7 +261,7 @@
}
}
gpuTextures = Render.getConfig("Stats").textureGPUTransferCount;
gpuTextures = Render.getConfig("Stats").texturePendingGPUTransferCount;
// Update state
if (!visible) { // Not visible because no recent downloads
@ -290,7 +290,7 @@
}, FADE_OUT_WAIT);
}
} else {
if (displayProgress < 100 || gpuTextures > 0) { // Was finished and waiting to fade out but have resumed so
if (displayProgress < 100 || gpuTextures > 0) { // Was finished and waiting to fade out but have resumed so
// don't fade out
Script.clearInterval(fadeWaitTimer);
fadeWaitTimer = null;

View file

@ -10,7 +10,7 @@ setup_hifi_project(Quick Gui OpenGL)
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
# link in the shared libraries
link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image)
link_hifi_libraries(shared octree ktx gl gpu gpu-gl render model model-networking networking render-utils fbx entities entities-renderer animation audio avatars script-engine physics image procedural)
package_libraries_for_deployment()