mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 22:07:03 +02:00
Merge branch 'interstitial-final-page' of https://github.com/danteruiz/hifi into interstitalMerged
This commit is contained in:
commit
ef3449f96d
105 changed files with 2161 additions and 778 deletions
1
BUILD.md
1
BUILD.md
|
@ -46,6 +46,7 @@ This can either be entered directly into your shell session before you build or
|
|||
|
||||
The path it needs to be set to will depend on where and how Qt5 was installed. e.g.
|
||||
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Qt5.10.1/5.10.1/gcc_64/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.10.1/clang_64/lib/cmake/
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.10.1/lib/cmake
|
||||
export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake
|
||||
|
|
|
@ -6,13 +6,20 @@ Please read the [general build guide](BUILD.md) for information on 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
|
||||
libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev zlib1g-dev
|
||||
|
||||
## Ubuntu 16.04 specific build guide
|
||||
## Ubuntu 16.04/18.04 specific build guide
|
||||
|
||||
### Ubuntu 18.04 only
|
||||
Add the universe repository:
|
||||
_(This is not enabled by default on the server edition)_
|
||||
```bash
|
||||
sudo add-apt-repository universe
|
||||
sudo apt-get update
|
||||
```
|
||||
|
||||
### Prepare environment
|
||||
hifiqt5.10.1
|
||||
Install qt:
|
||||
Install Qt 5.10.1:
|
||||
```bash
|
||||
wget http://debian.highfidelity.com/pool/h/hi/hifiqt5.10.1_5.10.1_amd64.deb
|
||||
sudo dpkg -i hifiqt5.10.1_5.10.1_amd64.deb
|
||||
|
@ -20,19 +27,20 @@ sudo dpkg -i hifiqt5.10.1_5.10.1_amd64.deb
|
|||
|
||||
Install build dependencies:
|
||||
```bash
|
||||
sudo apt-get install libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev
|
||||
sudo apt-get install libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack0 libjack-dev libxrandr-dev libudev-dev libssl-dev zlib1g-dev
|
||||
```
|
||||
|
||||
To compile interface in a server you must install:
|
||||
```bash
|
||||
sudo apt -y install libpulse0 libnss3 libnspr4 libfontconfig1 libxcursor1 libxcomposite1 libxtst6 libxslt1.1
|
||||
sudo apt-get -y install libpulse0 libnss3 libnspr4 libfontconfig1 libxcursor1 libxcomposite1 libxtst6 libxslt1.1
|
||||
```
|
||||
|
||||
Install build tools:
|
||||
```bash
|
||||
sudo apt install cmake
|
||||
sudo apt-get install cmake
|
||||
```
|
||||
|
||||
|
||||
### Get code and checkout the tag you need
|
||||
|
||||
Clone this repository:
|
||||
|
@ -48,12 +56,7 @@ git tags
|
|||
|
||||
Then checkout last tag with:
|
||||
```bash
|
||||
git checkout tags/RELEASE-6819
|
||||
```
|
||||
|
||||
Or go to the highfidelity download page (https://highfidelity.com/download) to get the release version. For example, if there is a BETA 6731 type:
|
||||
```bash
|
||||
git checkout tags/RELEASE-6731
|
||||
git checkout tags/v0.71.0
|
||||
```
|
||||
|
||||
### Compiling
|
||||
|
@ -66,15 +69,20 @@ cd hifi/build
|
|||
|
||||
Prepare makefiles:
|
||||
```bash
|
||||
cmake -DQT_CMAKE_PREFIX_PATH=/usr/local/Qt5.10.1/5.10/gcc_64/lib/cmake ..
|
||||
cmake -DQT_CMAKE_PREFIX_PATH=/usr/local/Qt5.10.1/5.10.1/gcc_64/lib/cmake ..
|
||||
```
|
||||
|
||||
Start compilation and get a cup of coffee:
|
||||
Start compilation of the server and get a cup of coffee:
|
||||
```bash
|
||||
make domain-server assignment-client interface
|
||||
make domain-server assignment-client
|
||||
```
|
||||
|
||||
In a server does not make sense to compile interface
|
||||
To compile interface:
|
||||
```bash
|
||||
make interface
|
||||
```
|
||||
|
||||
In a server, it does not make sense to compile interface
|
||||
|
||||
### Running the software
|
||||
|
||||
|
@ -93,4 +101,4 @@ Running interface:
|
|||
./interface/interface
|
||||
```
|
||||
|
||||
Go to localhost in running interface.
|
||||
Go to localhost in the running interface.
|
||||
|
|
|
@ -329,6 +329,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
AvatarData::_avatarSortCoefficientSize,
|
||||
AvatarData::_avatarSortCoefficientCenter,
|
||||
AvatarData::_avatarSortCoefficientAge);
|
||||
sortedAvatars.reserve(avatarsToSort.size());
|
||||
|
||||
// ignore or sort
|
||||
const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer();
|
||||
|
@ -429,9 +430,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|
||||
int remainingAvatars = (int)sortedAvatars.size();
|
||||
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
|
||||
while (!sortedAvatars.empty()) {
|
||||
const auto avatarData = sortedAvatars.top().getAvatar();
|
||||
sortedAvatars.pop();
|
||||
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
|
||||
for (const auto& sortedAvatar : sortedAvatarVector) {
|
||||
const auto& avatarData = sortedAvatar.getAvatar();
|
||||
remainingAvatars--;
|
||||
|
||||
auto otherNode = avatarDataToNodes[avatarData];
|
||||
|
|
|
@ -4,8 +4,8 @@ set(EXTERNAL_NAME serverless-content)
|
|||
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC72.zip
|
||||
URL_MD5 b1d8faf9266bfbff88274a484911eb99
|
||||
URL http://cdn.highfidelity.com/content-sets/serverless-tutorial-RC73.zip
|
||||
URL_MD5 0c5edfb63cafb042311d3cf25261fbf2
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
|
|
|
@ -264,20 +264,27 @@ Item {
|
|||
StatText {
|
||||
text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms"
|
||||
}
|
||||
StatText {
|
||||
text: "Drawcalls: " + root.drawcalls
|
||||
}
|
||||
StatText {
|
||||
text: "Triangles: " + root.triangles +
|
||||
" / Material Switches: " + root.materialSwitches
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "GPU Free Memory: " + root.gpuFreeMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "GPU Textures: ";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Count: " + root.gpuTextures;
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Pressure State: " + root.gpuTextureMemoryPressureState;
|
||||
}
|
||||
StatText {
|
||||
|
@ -287,27 +294,35 @@ Item {
|
|||
text: " " + root.gpuTextureResourceMemory + " / " + root.gpuTextureResourcePopulatedMemory + " / " + root.texturePendingTransfers + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Resident Memory: " + root.gpuTextureResidentMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Framebuffer Memory: " + root.gpuTextureFramebufferMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " External Memory: " + root.gpuTextureExternalMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "GPU Buffers: "
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Count: " + root.gpuBuffers;
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Memory: " + root.gpuBufferMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "GL Swapchain Memory: " + root.glContextSwapchainMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "QML Texture Memory: " + root.qmlTextureMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
|
|
|
@ -304,52 +304,69 @@ Item {
|
|||
StatText {
|
||||
text: "GPU frame size: " + root.gpuFrameSize.x + " x " + root.gpuFrameSize.y
|
||||
}
|
||||
StatText {
|
||||
text: "Drawcalls: " + root.drawcalls
|
||||
}
|
||||
StatText {
|
||||
text: "Triangles: " + root.triangles +
|
||||
" / Material Switches: " + root.materialSwitches
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "GPU Free Memory: " + root.gpuFreeMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "GPU Textures: ";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Count: " + root.gpuTextures;
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Pressure State: " + root.gpuTextureMemoryPressureState;
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
property bool showIdeal: (root.gpuTextureResourceIdealMemory != root.gpuTextureResourceMemory);
|
||||
text: " Resource Allocated " + (showIdeal ? "(Ideal)" : "") + " / Populated / Pending: ";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
property bool showIdeal: (root.gpuTextureResourceIdealMemory != root.gpuTextureResourceMemory);
|
||||
text: " " + root.gpuTextureResourceMemory + (showIdeal ? ("(" + root.gpuTextureResourceIdealMemory + ")") : "") + " / " + root.gpuTextureResourcePopulatedMemory + " / " + root.texturePendingTransfers + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Resident Memory: " + root.gpuTextureResidentMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Framebuffer Memory: " + root.gpuTextureFramebufferMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " External Memory: " + root.gpuTextureExternalMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "GPU Buffers: "
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Count: " + root.gpuBuffers;
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: " Memory: " + root.gpuBufferMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "GL Swapchain Memory: " + root.glContextSwapchainMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
visible: root.expanded;
|
||||
text: "QML Texture Memory: " + root.qmlTextureMemory + " MB";
|
||||
}
|
||||
StatText {
|
||||
|
|
|
@ -3494,17 +3494,12 @@ bool Application::isServerlessMode() const {
|
|||
}
|
||||
|
||||
bool Application::isInterstitialMode() const {
|
||||
bool interstitialModeEnabled = Menu::getInstance()->isOptionChecked("Enable Interstitial");
|
||||
return interstitialModeEnabled ? _interstitialMode : false;
|
||||
return _interstitialMode;
|
||||
}
|
||||
|
||||
void Application::setIsInterstitialMode(bool interstitialMode) {
|
||||
auto menu = Menu::getInstance();
|
||||
bool interstitialModeEnabled = menu->isOptionChecked("Enable Interstitial");
|
||||
if (_interstitialMode != interstitialMode && interstitialModeEnabled) {
|
||||
if (_interstitialMode != interstitialMode) {
|
||||
_interstitialMode = interstitialMode;
|
||||
|
||||
DependencyManager::get<OffscreenUi>()->setPinned(_interstitialMode);
|
||||
emit interstitialModeChanged(_interstitialMode);
|
||||
}
|
||||
}
|
||||
|
@ -5109,8 +5104,9 @@ void Application::updateLOD(float deltaTime) const {
|
|||
float presentTime = getActiveDisplayPlugin()->getAveragePresentTime();
|
||||
float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime());
|
||||
float gpuTime = getGPUContext()->getFrameTimerGPUAverage();
|
||||
float batchTime = getGPUContext()->getFrameTimerBatchAverage();
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
lodManager->setRenderTimes(presentTime, engineRunTime, gpuTime);
|
||||
lodManager->setRenderTimes(presentTime, engineRunTime, batchTime, gpuTime);
|
||||
lodManager->autoAdjustLOD(deltaTime);
|
||||
} else {
|
||||
DependencyManager::get<LODManager>()->resetLODAdjust();
|
||||
|
@ -6070,7 +6066,7 @@ void Application::updateRenderArgs(float deltaTime) {
|
|||
_viewFrustum.calculate();
|
||||
}
|
||||
appRenderArgs._renderArgs = RenderArgs(_gpuContext, lodManager->getOctreeSizeScale(),
|
||||
lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE,
|
||||
lodManager->getBoundaryLevelAdjust(), lodManager->getLODAngleHalfTan(), RenderArgs::DEFAULT_RENDER_MODE,
|
||||
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
|
||||
appRenderArgs._renderArgs._scene = getMain3DScene();
|
||||
|
||||
|
@ -6419,7 +6415,6 @@ void Application::clearDomainOctreeDetails() {
|
|||
}
|
||||
|
||||
void Application::clearDomainAvatars() {
|
||||
getMyAvatar()->setAvatarEntityDataChanged(true); // to recreate worn entities
|
||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,27 +19,15 @@
|
|||
#include "ui/DialogsManager.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
const float LODManager::DEFAULT_DESKTOP_LOD_DOWN_FPS = LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_DESKTOP_FPS;
|
||||
const float LODManager::DEFAULT_HMD_LOD_DOWN_FPS = LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_HMD_FPS;
|
||||
|
||||
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", DEFAULT_DESKTOP_LOD_DOWN_FPS);
|
||||
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DOWN_FPS);
|
||||
Setting::Handle<float> desktopLODDecreaseFPS("desktopLODDecreaseFPS", LODManager::DEFAULT_DESKTOP_LOD_DOWN_FPS);
|
||||
Setting::Handle<float> hmdLODDecreaseFPS("hmdLODDecreaseFPS", LODManager::DEFAULT_HMD_LOD_DOWN_FPS);
|
||||
|
||||
LODManager::LODManager() {
|
||||
}
|
||||
|
||||
float LODManager::getLODDecreaseFPS() const {
|
||||
if (qApp->isHMDMode()) {
|
||||
return getHMDLODDecreaseFPS();
|
||||
}
|
||||
return getDesktopLODDecreaseFPS();
|
||||
}
|
||||
|
||||
float LODManager::getLODIncreaseFPS() const {
|
||||
if (qApp->isHMDMode()) {
|
||||
return getHMDLODIncreaseFPS();
|
||||
}
|
||||
return getDesktopLODIncreaseFPS();
|
||||
}
|
||||
|
||||
// We use a "time-weighted running average" of the maxRenderTime and compare it against min/max thresholds
|
||||
// to determine if we should adjust the level of detail (LOD).
|
||||
//
|
||||
|
@ -48,79 +36,118 @@ float LODManager::getLODIncreaseFPS() const {
|
|||
// faster than the runningAverage is computed, the error between the value and its runningAverage will be
|
||||
// reduced by 1/e every timescale of real-time that passes.
|
||||
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.08f; // sec
|
||||
//
|
||||
// Assuming the measured value is affected by logic invoked by the runningAverage bumping up against its
|
||||
// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage to settle
|
||||
// to the new value BEFORE we test it aginst its thresholds again. Hence we test on a period that is a few
|
||||
// multiples of the running average timescale:
|
||||
const uint64_t LOD_AUTO_ADJUST_PERIOD = 4 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec
|
||||
|
||||
const float LOD_AUTO_ADJUST_DECREMENT_FACTOR = 0.8f;
|
||||
const float LOD_AUTO_ADJUST_INCREMENT_FACTOR = 1.2f;
|
||||
// batchTIme is always contained in presentTime.
|
||||
// We favor using batchTime instead of presentTime as a representative value for rendering duration (on present thread)
|
||||
// if batchTime + cushionTime < presentTime.
|
||||
// since we are shooting for fps around 60, 90Hz, the ideal frames are around 10ms
|
||||
// so we are picking a cushion time of 3ms
|
||||
const float LOD_BATCH_TO_PRESENT_CUSHION_TIME = 3.0f; // msec
|
||||
|
||||
void LODManager::setRenderTimes(float presentTime, float engineRunTime, float gpuTime) {
|
||||
void LODManager::setRenderTimes(float presentTime, float engineRunTime, float batchTime, float gpuTime) {
|
||||
_presentTime = presentTime;
|
||||
_engineRunTime = engineRunTime;
|
||||
_batchTime = batchTime;
|
||||
_gpuTime = gpuTime;
|
||||
}
|
||||
|
||||
void LODManager::autoAdjustLOD(float realTimeDelta) {
|
||||
float maxRenderTime = glm::max(glm::max(_presentTime, _engineRunTime), _gpuTime);
|
||||
|
||||
// The "render time" is the worse of:
|
||||
// - engineRunTime: Time spent in the render thread in the engine producing the gpu::Frame N
|
||||
// - batchTime: Time spent in the present thread processing the batches of gpu::Frame N+1
|
||||
// - presentTime: Time spent in the present thread between the last 2 swap buffers considered the total time to submit gpu::Frame N+1
|
||||
// - gpuTime: Time spent in the GPU executing the gpu::Frame N + 2
|
||||
|
||||
// But Present time is in reality synched with the monitor/display refresh rate, it s always longer than batchTime.
|
||||
// So if batchTime is fast enough relative to presentTime we are using it, otherwise we are using presentTime. got it ?
|
||||
auto presentTime = (_presentTime > _batchTime + LOD_BATCH_TO_PRESENT_CUSHION_TIME ? _batchTime + LOD_BATCH_TO_PRESENT_CUSHION_TIME : _presentTime);
|
||||
float maxRenderTime = glm::max(glm::max(presentTime, _engineRunTime), _gpuTime);
|
||||
|
||||
// compute time-weighted running average maxRenderTime
|
||||
// Note: we MUST clamp the blend to 1.0 for stability
|
||||
float blend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f;
|
||||
_avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxRenderTime; // msec
|
||||
if (!_automaticLODAdjust || _avgRenderTime == 0.0f) {
|
||||
float nowBlend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f;
|
||||
_nowRenderTime = (1.0f - nowBlend) * _nowRenderTime + nowBlend * maxRenderTime; // msec
|
||||
|
||||
float smoothBlend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE * _smoothScale) ? realTimeDelta / (LOD_ADJUST_RUNNING_AVG_TIMESCALE * _smoothScale) : 1.0f;
|
||||
_smoothRenderTime = (1.0f - smoothBlend) * _smoothRenderTime + smoothBlend * maxRenderTime; // msec
|
||||
|
||||
if (!_automaticLODAdjust || _nowRenderTime == 0.0f || _smoothRenderTime == 0.0f) {
|
||||
// early exit
|
||||
return;
|
||||
}
|
||||
|
||||
float oldOctreeSizeScale = _octreeSizeScale;
|
||||
float currentFPS = (float)MSECS_PER_SECOND / _avgRenderTime;
|
||||
uint64_t now = usecTimestampNow();
|
||||
if (currentFPS < getLODDecreaseFPS()) {
|
||||
if (now > _decreaseFPSExpiry) {
|
||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
if (_octreeSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
_octreeSizeScale *= LOD_AUTO_ADJUST_DECREMENT_FACTOR;
|
||||
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
||||
}
|
||||
emit LODDecreased();
|
||||
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||
// to provide an FPS just above the decrease threshold. It will drift close to its
|
||||
// true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
|
||||
_avgRenderTime = (float)MSECS_PER_SECOND / (getLODDecreaseFPS() + 1.0f);
|
||||
}
|
||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
} else if (currentFPS > getLODIncreaseFPS()) {
|
||||
if (now > _increaseFPSExpiry) {
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
if (_octreeSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) {
|
||||
if (_octreeSizeScale < ADJUST_LOD_MIN_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MIN_SIZE_SCALE;
|
||||
} else {
|
||||
_octreeSizeScale *= LOD_AUTO_ADJUST_INCREMENT_FACTOR;
|
||||
}
|
||||
if (_octreeSizeScale > ADJUST_LOD_MAX_SIZE_SCALE) {
|
||||
_octreeSizeScale = ADJUST_LOD_MAX_SIZE_SCALE;
|
||||
}
|
||||
emit LODIncreased();
|
||||
// Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime
|
||||
// to provide an FPS just below the increase threshold. It will drift close to its
|
||||
// true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary.
|
||||
_avgRenderTime = (float)MSECS_PER_SECOND / (getLODIncreaseFPS() - 1.0f);
|
||||
}
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
_decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
} else {
|
||||
_increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD;
|
||||
_decreaseFPSExpiry = _increaseFPSExpiry;
|
||||
// Previous values for output
|
||||
float oldOctreeSizeScale = getOctreeSizeScale();
|
||||
float oldLODAngle = getLODAngleDeg();
|
||||
|
||||
// Target fps is slightly overshooted by 5hz
|
||||
float targetFPS = getLODTargetFPS() + LOD_OFFSET_FPS;
|
||||
|
||||
// Current fps based on latest measurments
|
||||
float currentNowFPS = (float)MSECS_PER_SECOND / _nowRenderTime;
|
||||
float currentSmoothFPS = (float)MSECS_PER_SECOND / _smoothRenderTime;
|
||||
|
||||
// Compute the Variance of the FPS signal (FPS - smouthFPS)^2
|
||||
// Also scale it by a percentage for fine tuning (default is 100%)
|
||||
float currentVarianceFPS = (currentSmoothFPS - currentNowFPS);
|
||||
currentVarianceFPS *= currentVarianceFPS;
|
||||
currentVarianceFPS *= _pidCoefs.w;
|
||||
|
||||
// evaluate current error between the current smoothFPS and target FPS
|
||||
// and the sqaure of the error to compare against the Variance
|
||||
auto currentErrorFPS = (targetFPS - currentSmoothFPS);
|
||||
auto currentErrorFPSSquare = currentErrorFPS * currentErrorFPS;
|
||||
|
||||
// Define a noiseCoef that is trying to adjust the error to the FPS target value based on its strength
|
||||
// relative to the current Variance of the FPS signal.
|
||||
// If the error is within the variance, just set to 0.
|
||||
// if its within 2x the variance scale the control
|
||||
// and full control if error is bigger than 2x variance
|
||||
auto noiseCoef = 1.0f;
|
||||
if (currentErrorFPSSquare < currentVarianceFPS) {
|
||||
noiseCoef = 0.0f;
|
||||
} else if (currentErrorFPSSquare < 2.0f * currentVarianceFPS) {
|
||||
noiseCoef = (currentErrorFPSSquare - currentVarianceFPS) / currentVarianceFPS;
|
||||
}
|
||||
|
||||
// The final normalized error is the the error to the FPS target, weighted by the noiseCoef, then normailzed by the target FPS.
|
||||
// it s also clamped in the [-1, 1] range
|
||||
auto error = noiseCoef * currentErrorFPS / targetFPS;
|
||||
error = glm::clamp(error, -1.0f, 1.0f);
|
||||
|
||||
// Now we are getting into the P.I.D. controler code
|
||||
// retreive the history of pid error and integral
|
||||
auto previous_error = _pidHistory.x;
|
||||
auto previous_integral = _pidHistory.y;
|
||||
|
||||
// The dt used for temporal values of the controller is the current realTimedelta
|
||||
// clamped to a reasonable granularity to make sure we are not over reacting
|
||||
auto dt = std::min(realTimeDelta, LOD_ADJUST_RUNNING_AVG_TIMESCALE);
|
||||
|
||||
// Compute the current integral and clamp to avoid accumulation
|
||||
auto integral = previous_integral + error * dt;
|
||||
glm::clamp(integral, -1.0f, 1.0f);
|
||||
|
||||
// Compute derivative
|
||||
auto derivative = (error - previous_error) / dt;
|
||||
|
||||
// remember history
|
||||
_pidHistory.x = error;
|
||||
_pidHistory.y = integral;
|
||||
_pidHistory.z = derivative;
|
||||
|
||||
// Compute the output of the PID and record intermediate results for tuning
|
||||
_pidOutputs.x = _pidCoefs.x * error; // Kp * error
|
||||
_pidOutputs.y = _pidCoefs.y * integral; // Ki * integral
|
||||
_pidOutputs.z = _pidCoefs.z * derivative; // Kd * derivative
|
||||
|
||||
auto output = _pidOutputs.x + _pidOutputs.y + _pidOutputs.z;
|
||||
_pidOutputs.w = output;
|
||||
|
||||
// And now add the output of the controller to the LODAngle where we will guarantee it is in the proper range
|
||||
setLODAngleDeg(oldLODAngle + output);
|
||||
|
||||
if (oldOctreeSizeScale != _octreeSizeScale) {
|
||||
auto lodToolsDialog = DependencyManager::get<DialogsManager>()->getLodToolsDialog();
|
||||
if (lodToolsDialog) {
|
||||
|
@ -129,97 +156,96 @@ void LODManager::autoAdjustLOD(float realTimeDelta) {
|
|||
}
|
||||
}
|
||||
|
||||
float LODManager::getLODAngleHalfTan() const {
|
||||
return getPerspectiveAccuracyAngleTan(_octreeSizeScale, _boundaryLevelAdjust);
|
||||
}
|
||||
float LODManager::getLODAngle() const {
|
||||
return 2.0f * atanf(getLODAngleHalfTan());
|
||||
}
|
||||
float LODManager::getLODAngleDeg() const {
|
||||
return glm::degrees(getLODAngle());
|
||||
}
|
||||
|
||||
void LODManager::setLODAngleDeg(float lodAngle) {
|
||||
auto newSolidAngle = std::max(0.5f, std::min(lodAngle, 90.f));
|
||||
auto halTan = glm::tan(glm::radians(newSolidAngle * 0.5f));
|
||||
auto octreeSizeScale = TREE_SCALE * OCTREE_TO_MESH_RATIO / halTan;
|
||||
setOctreeSizeScale(octreeSizeScale);
|
||||
}
|
||||
|
||||
void LODManager::setSmoothScale(float t) {
|
||||
_smoothScale = glm::max(1.0f, t);
|
||||
}
|
||||
|
||||
float LODManager::getPidKp() const {
|
||||
return _pidCoefs.x;
|
||||
}
|
||||
float LODManager::getPidKi() const {
|
||||
return _pidCoefs.y;
|
||||
}
|
||||
float LODManager::getPidKd() const {
|
||||
return _pidCoefs.z;
|
||||
}
|
||||
float LODManager::getPidKv() const {
|
||||
return _pidCoefs.w;
|
||||
}
|
||||
void LODManager::setPidKp(float k) {
|
||||
_pidCoefs.x = k;
|
||||
}
|
||||
void LODManager::setPidKi(float k) {
|
||||
_pidCoefs.y = k;
|
||||
}
|
||||
void LODManager::setPidKd(float k) {
|
||||
_pidCoefs.z = k;
|
||||
}
|
||||
void LODManager::setPidKv(float t) {
|
||||
_pidCoefs.w = t;
|
||||
}
|
||||
|
||||
float LODManager::getPidOp() const {
|
||||
return _pidOutputs.x;
|
||||
}
|
||||
float LODManager::getPidOi() const {
|
||||
return _pidOutputs.y;
|
||||
}
|
||||
float LODManager::getPidOd() const {
|
||||
return _pidOutputs.z;
|
||||
}
|
||||
float LODManager::getPidO() const {
|
||||
return _pidOutputs.w;
|
||||
}
|
||||
|
||||
void LODManager::resetLODAdjust() {
|
||||
_decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD;
|
||||
}
|
||||
|
||||
float LODManager::getLODLevel() const {
|
||||
// simpleLOD is a linearized and normalized number that represents how much LOD is being applied.
|
||||
// It ranges from:
|
||||
// 1.0 = normal (max) level of detail
|
||||
// 0.0 = min level of detail
|
||||
// In other words: as LOD "drops" the value of simpleLOD will also "drop", and it cannot go lower than 0.0.
|
||||
const float LOG_MIN_LOD_RATIO = logf(ADJUST_LOD_MIN_SIZE_SCALE / ADJUST_LOD_MAX_SIZE_SCALE);
|
||||
float power = logf(_octreeSizeScale / ADJUST_LOD_MAX_SIZE_SCALE);
|
||||
float simpleLOD = (LOG_MIN_LOD_RATIO - power) / LOG_MIN_LOD_RATIO;
|
||||
return simpleLOD;
|
||||
}
|
||||
|
||||
const float MIN_DECREASE_FPS = 0.5f;
|
||||
|
||||
void LODManager::setDesktopLODDecreaseFPS(float fps) {
|
||||
if (fps < MIN_DECREASE_FPS) {
|
||||
// avoid divide by zero
|
||||
fps = MIN_DECREASE_FPS;
|
||||
}
|
||||
_desktopMaxRenderTime = (float)MSECS_PER_SECOND / fps;
|
||||
}
|
||||
|
||||
float LODManager::getDesktopLODDecreaseFPS() const {
|
||||
return (float)MSECS_PER_SECOND / _desktopMaxRenderTime;
|
||||
}
|
||||
|
||||
float LODManager::getDesktopLODIncreaseFPS() const {
|
||||
return glm::min(((float)MSECS_PER_SECOND / _desktopMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_DESKTOP_FPS);
|
||||
}
|
||||
|
||||
void LODManager::setHMDLODDecreaseFPS(float fps) {
|
||||
if (fps < MIN_DECREASE_FPS) {
|
||||
// avoid divide by zero
|
||||
fps = MIN_DECREASE_FPS;
|
||||
}
|
||||
_hmdMaxRenderTime = (float)MSECS_PER_SECOND / fps;
|
||||
}
|
||||
|
||||
float LODManager::getHMDLODDecreaseFPS() const {
|
||||
return (float)MSECS_PER_SECOND / _hmdMaxRenderTime;
|
||||
}
|
||||
|
||||
float LODManager::getHMDLODIncreaseFPS() const {
|
||||
return glm::min(((float)MSECS_PER_SECOND / _hmdMaxRenderTime) + INCREASE_LOD_GAP_FPS, MAX_LIKELY_HMD_FPS);
|
||||
}
|
||||
|
||||
QString LODManager::getLODFeedbackText() {
|
||||
// determine granularity feedback
|
||||
int boundaryLevelAdjust = getBoundaryLevelAdjust();
|
||||
QString granularityFeedback;
|
||||
switch (boundaryLevelAdjust) {
|
||||
case 0: {
|
||||
granularityFeedback = QString(".");
|
||||
} break;
|
||||
case 1: {
|
||||
granularityFeedback = QString(" at half of standard granularity.");
|
||||
} break;
|
||||
case 2: {
|
||||
granularityFeedback = QString(" at a third of standard granularity.");
|
||||
} break;
|
||||
default: {
|
||||
granularityFeedback = QString(" at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1);
|
||||
} break;
|
||||
}
|
||||
// distance feedback
|
||||
float octreeSizeScale = getOctreeSizeScale();
|
||||
float relativeToDefault = octreeSizeScale / DEFAULT_OCTREE_SIZE_SCALE;
|
||||
int relativeToTwentyTwenty = 20 / relativeToDefault;
|
||||
|
||||
QString result;
|
||||
if (relativeToDefault > 1.01f) {
|
||||
result = QString("20:%1 or %2 times further than average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',2).arg(granularityFeedback);
|
||||
} else if (relativeToDefault > 0.99f) {
|
||||
result = QString("20:20 or the default distance for average vision%1").arg(granularityFeedback);
|
||||
} else if (relativeToDefault > 0.01f) {
|
||||
result = QString("20:%1 or %2 of default distance for average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault,0,'f',3).arg(granularityFeedback);
|
||||
} else {
|
||||
result = QString("%2 of default distance for average vision%3").arg(relativeToDefault,0,'f',3).arg(granularityFeedback);
|
||||
}
|
||||
return result;
|
||||
void LODManager::setAutomaticLODAdjust(bool value) {
|
||||
_automaticLODAdjust = value;
|
||||
emit autoLODChanged();
|
||||
}
|
||||
|
||||
bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) {
|
||||
// FIXME - eventually we want to use the render accuracy as an indicator for the level of detail
|
||||
// to use in rendering.
|
||||
float renderAccuracy = calculateRenderAccuracy(args->getViewFrustum().getPosition(), bounds, args->_sizeScale, args->_boundaryLevelAdjust);
|
||||
return (renderAccuracy > 0.0f);
|
||||
// To decide if the bound should be rendered or not at the specified Args->lodAngle,
|
||||
// we need to compute the apparent angle of the bound from the frustum origin,
|
||||
// and compare it against the lodAngle, if it is greater or equal we should render the content of that bound.
|
||||
// we abstract the bound as a sphere centered on the bound center and of radius half diagonal of the bound.
|
||||
|
||||
// Instead of comparing angles, we are comparing the tangent of the half angle which are more efficient to compute:
|
||||
// we are comparing the square of the half tangent apparent angle for the bound against the LODAngle Half tangent square
|
||||
// if smaller, the bound is too small and we should NOT render it, return true otherwise.
|
||||
|
||||
// Tangent Adjacent side is eye to bound center vector length
|
||||
auto pos = args->getViewFrustum().getPosition() - bounds.calcCenter();
|
||||
auto halfTanAdjacentSq = glm::dot(pos, pos);
|
||||
|
||||
// Tangent Opposite side is the half length of the dimensions vector of the bound
|
||||
auto dim = bounds.getDimensions();
|
||||
auto halfTanOppositeSq = 0.25f * glm::dot(dim, dim);
|
||||
|
||||
// The test is:
|
||||
// isVisible = halfTanSq >= lodHalfTanSq = (halfTanOppositeSq / halfTanAdjacentSq) >= lodHalfTanSq
|
||||
// which we express as below to avoid division
|
||||
// (halfTanOppositeSq) >= lodHalfTanSq * halfTanAdjacentSq
|
||||
return (halfTanOppositeSq >= args->_lodAngleHalfTanSq * halfTanAdjacentSq);
|
||||
};
|
||||
|
||||
void LODManager::setOctreeSizeScale(float sizeScale) {
|
||||
|
@ -230,13 +256,140 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
|
|||
_boundaryLevelAdjust = boundaryLevelAdjust;
|
||||
}
|
||||
|
||||
QString LODManager::getLODFeedbackText() {
|
||||
// determine granularity feedback
|
||||
int boundaryLevelAdjust = getBoundaryLevelAdjust();
|
||||
QString granularityFeedback;
|
||||
switch (boundaryLevelAdjust) {
|
||||
case 0: {
|
||||
granularityFeedback = QString(".");
|
||||
} break;
|
||||
case 1: {
|
||||
granularityFeedback = QString(" at half of standard granularity.");
|
||||
} break;
|
||||
case 2: {
|
||||
granularityFeedback = QString(" at a third of standard granularity.");
|
||||
} break;
|
||||
default: {
|
||||
granularityFeedback = QString(" at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1);
|
||||
} break;
|
||||
}
|
||||
// distance feedback
|
||||
float octreeSizeScale = getOctreeSizeScale();
|
||||
float relativeToDefault = octreeSizeScale / DEFAULT_OCTREE_SIZE_SCALE;
|
||||
int relativeToTwentyTwenty = 20 / relativeToDefault;
|
||||
|
||||
QString result;
|
||||
if (relativeToDefault > 1.01f) {
|
||||
result = QString("20:%1 or %2 times further than average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault, 0, 'f', 2).arg(granularityFeedback);
|
||||
} else if (relativeToDefault > 0.99f) {
|
||||
result = QString("20:20 or the default distance for average vision%1").arg(granularityFeedback);
|
||||
} else if (relativeToDefault > 0.01f) {
|
||||
result = QString("20:%1 or %2 of default distance for average vision%3").arg(relativeToTwentyTwenty).arg(relativeToDefault, 0, 'f', 3).arg(granularityFeedback);
|
||||
} else {
|
||||
result = QString("%2 of default distance for average vision%3").arg(relativeToDefault, 0, 'f', 3).arg(granularityFeedback);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void LODManager::loadSettings() {
|
||||
setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get());
|
||||
setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get());
|
||||
setDesktopLODTargetFPS(desktopLODDecreaseFPS.get());
|
||||
setHMDLODTargetFPS(hmdLODDecreaseFPS.get());
|
||||
}
|
||||
|
||||
void LODManager::saveSettings() {
|
||||
desktopLODDecreaseFPS.set(getDesktopLODDecreaseFPS());
|
||||
hmdLODDecreaseFPS.set(getHMDLODDecreaseFPS());
|
||||
desktopLODDecreaseFPS.set(getDesktopLODTargetFPS());
|
||||
hmdLODDecreaseFPS.set(getHMDLODTargetFPS());
|
||||
}
|
||||
|
||||
const float MIN_DECREASE_FPS = 0.5f;
|
||||
|
||||
void LODManager::setDesktopLODTargetFPS(float fps) {
|
||||
if (fps < MIN_DECREASE_FPS) {
|
||||
// avoid divide by zero
|
||||
fps = MIN_DECREASE_FPS;
|
||||
}
|
||||
_desktopTargetFPS = fps;
|
||||
}
|
||||
|
||||
float LODManager::getDesktopLODTargetFPS() const {
|
||||
return _desktopTargetFPS;
|
||||
}
|
||||
|
||||
void LODManager::setHMDLODTargetFPS(float fps) {
|
||||
if (fps < MIN_DECREASE_FPS) {
|
||||
// avoid divide by zero
|
||||
fps = MIN_DECREASE_FPS;
|
||||
}
|
||||
_hmdTargetFPS = fps;
|
||||
}
|
||||
|
||||
float LODManager::getHMDLODTargetFPS() const {
|
||||
return _hmdTargetFPS;
|
||||
}
|
||||
|
||||
float LODManager::getLODTargetFPS() const {
|
||||
if (qApp->isHMDMode()) {
|
||||
return getHMDLODTargetFPS();
|
||||
}
|
||||
return getDesktopLODTargetFPS();
|
||||
}
|
||||
|
||||
void LODManager::setWorldDetailQuality(float quality) {
|
||||
static const float MIN_FPS = 10;
|
||||
static const float LOW = 0.25f;
|
||||
|
||||
bool isLowestValue = quality == LOW;
|
||||
bool isHMDMode = qApp->isHMDMode();
|
||||
|
||||
float maxFPS = isHMDMode ? LOD_MAX_LIKELY_HMD_FPS : LOD_MAX_LIKELY_DESKTOP_FPS;
|
||||
float desiredFPS = maxFPS;
|
||||
|
||||
if (!isLowestValue) {
|
||||
float calculatedFPS = (maxFPS - (maxFPS * quality));
|
||||
desiredFPS = calculatedFPS < MIN_FPS ? MIN_FPS : calculatedFPS;
|
||||
}
|
||||
|
||||
if (isHMDMode) {
|
||||
setHMDLODTargetFPS(desiredFPS);
|
||||
} else {
|
||||
setDesktopLODTargetFPS(desiredFPS);
|
||||
}
|
||||
|
||||
emit worldDetailQualityChanged();
|
||||
}
|
||||
|
||||
float LODManager::getWorldDetailQuality() const {
|
||||
|
||||
static const float LOW = 0.25f;
|
||||
static const float MEDIUM = 0.5f;
|
||||
static const float HIGH = 0.75f;
|
||||
|
||||
bool inHMD = qApp->isHMDMode();
|
||||
|
||||
float targetFPS = 0.0f;
|
||||
if (inHMD) {
|
||||
targetFPS = getHMDLODTargetFPS();
|
||||
} else {
|
||||
targetFPS = getDesktopLODTargetFPS();
|
||||
}
|
||||
float maxFPS = inHMD ? LOD_MAX_LIKELY_HMD_FPS : LOD_MAX_LIKELY_DESKTOP_FPS;
|
||||
float percentage = 1.0f - targetFPS / maxFPS;
|
||||
|
||||
if (percentage <= LOW) {
|
||||
return LOW;
|
||||
} else if (percentage <= MEDIUM) {
|
||||
return MEDIUM;
|
||||
}
|
||||
|
||||
return HIGH;
|
||||
}
|
||||
|
||||
|
||||
void LODManager::setLODQualityLevel(float quality) {
|
||||
_lodQualityLevel = quality;
|
||||
}
|
||||
|
||||
float LODManager::getLODQualityLevel() const {
|
||||
return _lodQualityLevel;
|
||||
}
|
||||
|
|
|
@ -19,18 +19,11 @@
|
|||
#include <SimpleMovingAverage.h>
|
||||
#include <render/Args.h>
|
||||
|
||||
const float DEFAULT_DESKTOP_LOD_DOWN_FPS = 30.0f;
|
||||
const float DEFAULT_HMD_LOD_DOWN_FPS = 34.0f;
|
||||
const float DEFAULT_DESKTOP_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_DESKTOP_LOD_DOWN_FPS; // msec
|
||||
const float DEFAULT_HMD_MAX_RENDER_TIME = (float)MSECS_PER_SECOND / DEFAULT_HMD_LOD_DOWN_FPS; // msec
|
||||
const float MAX_LIKELY_DESKTOP_FPS = 59.0f; // this is essentially, V-synch - 1 fps
|
||||
const float MAX_LIKELY_HMD_FPS = 74.0f; // this is essentially, V-synch - 1 fps
|
||||
const float INCREASE_LOD_GAP_FPS = 10.0f; // fps
|
||||
|
||||
// The default value DEFAULT_OCTREE_SIZE_SCALE means you can be 400 meters away from a 1 meter object in order to see it (which is ~20:20 vision).
|
||||
const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE;
|
||||
// This controls how low the auto-adjust LOD will go. We want a minimum vision of ~20:500 or 0.04 of default
|
||||
const float ADJUST_LOD_MIN_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE * 0.04f;
|
||||
const float LOD_DEFAULT_QUALITY_LEVEL = 0.5f; // default quality level setting is Mid
|
||||
const float LOD_MAX_LIKELY_DESKTOP_FPS = 60.0f; // this is essentially, V-synch fps
|
||||
const float LOD_MAX_LIKELY_HMD_FPS = 90.0f; // this is essentially, V-synch fps
|
||||
const float LOD_OFFSET_FPS = 5.0f; // offset of FPS to add for computing the target framerate
|
||||
|
||||
class AABox;
|
||||
|
||||
|
@ -53,24 +46,47 @@ class AABox;
|
|||
|
||||
class LODManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
Q_PROPERTY(float presentTime READ getPresentTime)
|
||||
Q_PROPERTY(float engineRunTime READ getEngineRunTime)
|
||||
Q_PROPERTY(float gpuTime READ getGPUTime)
|
||||
Q_PROPERTY(float avgRenderTime READ getAverageRenderTime)
|
||||
Q_PROPERTY(float fps READ getMaxTheoreticalFPS)
|
||||
Q_PROPERTY(float lodLevel READ getLODLevel)
|
||||
Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS)
|
||||
Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS)
|
||||
Q_PROPERTY(float worldDetailQuality READ getWorldDetailQuality WRITE setWorldDetailQuality NOTIFY worldDetailQualityChanged)
|
||||
|
||||
Q_PROPERTY(float lodQualityLevel READ getLODQualityLevel WRITE setLODQualityLevel NOTIFY lodQualityLevelChanged)
|
||||
|
||||
Q_PROPERTY(bool automaticLODAdjust READ getAutomaticLODAdjust WRITE setAutomaticLODAdjust NOTIFY autoLODChanged)
|
||||
|
||||
Q_PROPERTY(float presentTime READ getPresentTime)
|
||||
Q_PROPERTY(float engineRunTime READ getEngineRunTime)
|
||||
Q_PROPERTY(float batchTime READ getBatchTime)
|
||||
Q_PROPERTY(float gpuTime READ getGPUTime)
|
||||
|
||||
Q_PROPERTY(float nowRenderTime READ getNowRenderTime)
|
||||
Q_PROPERTY(float nowRenderFPS READ getNowRenderFPS)
|
||||
|
||||
Q_PROPERTY(float smoothScale READ getSmoothScale WRITE setSmoothScale)
|
||||
Q_PROPERTY(float smoothRenderTime READ getSmoothRenderTime)
|
||||
Q_PROPERTY(float smoothRenderFPS READ getSmoothRenderFPS)
|
||||
|
||||
Q_PROPERTY(float lodTargetFPS READ getLODTargetFPS)
|
||||
|
||||
Q_PROPERTY(float lodAngleDeg READ getLODAngleDeg WRITE setLODAngleDeg)
|
||||
|
||||
Q_PROPERTY(float pidKp READ getPidKp WRITE setPidKp)
|
||||
Q_PROPERTY(float pidKi READ getPidKi WRITE setPidKi)
|
||||
Q_PROPERTY(float pidKd READ getPidKd WRITE setPidKd)
|
||||
Q_PROPERTY(float pidKv READ getPidKv WRITE setPidKv)
|
||||
|
||||
Q_PROPERTY(float pidOp READ getPidOp)
|
||||
Q_PROPERTY(float pidOi READ getPidOi)
|
||||
Q_PROPERTY(float pidOd READ getPidOd)
|
||||
Q_PROPERTY(float pidO READ getPidO)
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.setAutomaticLODAdjust
|
||||
* @param {boolean} value
|
||||
*/
|
||||
Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; }
|
||||
Q_INVOKABLE void setAutomaticLODAdjust(bool value);
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getAutomaticLODAdjust
|
||||
|
@ -79,42 +95,31 @@ public:
|
|||
Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; }
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.setDesktopLODDecreaseFPS
|
||||
* @function LODManager.setDesktopLODTargetFPS
|
||||
* @param {number} value
|
||||
*/
|
||||
Q_INVOKABLE void setDesktopLODDecreaseFPS(float value);
|
||||
Q_INVOKABLE void setDesktopLODTargetFPS(float value);
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getDesktopLODDecreaseFPS
|
||||
* @function LODManager.getDesktopLODTargetFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
|
||||
Q_INVOKABLE float getDesktopLODDecreaseFPS() const;
|
||||
Q_INVOKABLE float getDesktopLODTargetFPS() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getDesktopLODIncreaseFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getDesktopLODIncreaseFPS() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.setHMDLODDecreaseFPS
|
||||
* @function LODManager.setHMDLODTargetFPS
|
||||
* @param {number} value
|
||||
*/
|
||||
|
||||
Q_INVOKABLE void setHMDLODDecreaseFPS(float value);
|
||||
|
||||
Q_INVOKABLE void setHMDLODTargetFPS(float value);
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getHMDLODDecreaseFPS
|
||||
* @function LODManager.getHMDLODTargetFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getHMDLODDecreaseFPS() const;
|
||||
Q_INVOKABLE float getHMDLODTargetFPS() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getHMDLODIncreaseFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getHMDLODIncreaseFPS() const;
|
||||
|
||||
// User Tweakable LOD Items
|
||||
/**jsdoc
|
||||
|
@ -148,32 +153,61 @@ public:
|
|||
Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getLODDecreaseFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getLODDecreaseFPS() const;
|
||||
* @function LODManager.getLODTargetFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getLODTargetFPS() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function LODManager.getLODIncreaseFPS
|
||||
* @returns {number}
|
||||
*/
|
||||
Q_INVOKABLE float getLODIncreaseFPS() const;
|
||||
|
||||
float getPresentTime() const { return _presentTime; }
|
||||
float getEngineRunTime() const { return _engineRunTime; }
|
||||
float getBatchTime() const { return _batchTime; }
|
||||
float getGPUTime() const { return _gpuTime; }
|
||||
|
||||
static bool shouldRender(const RenderArgs* args, const AABox& bounds);
|
||||
void setRenderTimes(float presentTime, float engineRunTime, float gpuTime);
|
||||
void setRenderTimes(float presentTime, float engineRunTime, float batchTime, float gpuTime);
|
||||
void autoAdjustLOD(float realTimeDelta);
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
void resetLODAdjust();
|
||||
|
||||
float getAverageRenderTime() const { return _avgRenderTime; };
|
||||
float getMaxTheoreticalFPS() const { return (float)MSECS_PER_SECOND / _avgRenderTime; };
|
||||
float getLODLevel() const;
|
||||
float getNowRenderTime() const { return _nowRenderTime; };
|
||||
float getNowRenderFPS() const { return (_nowRenderTime > 0.0f ? (float)MSECS_PER_SECOND / _nowRenderTime : 0.0f); };
|
||||
|
||||
void setSmoothScale(float t);
|
||||
float getSmoothScale() const { return _smoothScale; }
|
||||
|
||||
float getSmoothRenderTime() const { return _smoothRenderTime; };
|
||||
float getSmoothRenderFPS() const { return (_smoothRenderTime > 0.0f ? (float)MSECS_PER_SECOND / _smoothRenderTime : 0.0f); };
|
||||
|
||||
void setWorldDetailQuality(float quality);
|
||||
float getWorldDetailQuality() const;
|
||||
|
||||
void setLODQualityLevel(float quality);
|
||||
float getLODQualityLevel() const;
|
||||
|
||||
float getLODAngleDeg() const;
|
||||
void setLODAngleDeg(float lodAngle);
|
||||
float getLODAngleHalfTan() const;
|
||||
float getLODAngle() const;
|
||||
|
||||
float getPidKp() const;
|
||||
float getPidKi() const;
|
||||
float getPidKd() const;
|
||||
float getPidKv() const;
|
||||
void setPidKp(float k);
|
||||
void setPidKi(float k);
|
||||
void setPidKd(float k);
|
||||
void setPidKv(float t);
|
||||
|
||||
float getPidOp() const;
|
||||
float getPidOi() const;
|
||||
float getPidOd() const;
|
||||
float getPidO() const;
|
||||
|
||||
static const float DEFAULT_DESKTOP_LOD_DOWN_FPS;
|
||||
static const float DEFAULT_HMD_LOD_DOWN_FPS;
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -189,22 +223,35 @@ signals:
|
|||
*/
|
||||
void LODDecreased();
|
||||
|
||||
void autoLODChanged();
|
||||
void lodQualityLevelChanged();
|
||||
void worldDetailQualityChanged();
|
||||
|
||||
private:
|
||||
LODManager();
|
||||
|
||||
bool _automaticLODAdjust = true;
|
||||
float _presentTime { 0.0f }; // msec
|
||||
float _engineRunTime { 0.0f }; // msec
|
||||
float _gpuTime { 0.0f }; // msec
|
||||
float _avgRenderTime { 0.0f }; // msec
|
||||
float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME };
|
||||
float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME };
|
||||
|
||||
float _presentTime{ 0.0f }; // msec
|
||||
float _engineRunTime{ 0.0f }; // msec
|
||||
float _batchTime{ 0.0f }; // msec
|
||||
float _gpuTime{ 0.0f }; // msec
|
||||
|
||||
float _nowRenderTime{ 0.0f }; // msec
|
||||
float _smoothScale{ 10.0f }; // smooth is evaluated over 10 times longer than now
|
||||
float _smoothRenderTime{ 0.0f }; // msec
|
||||
|
||||
float _lodQualityLevel{ LOD_DEFAULT_QUALITY_LEVEL };
|
||||
|
||||
float _desktopTargetFPS { LOD_OFFSET_FPS + LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_DESKTOP_FPS };
|
||||
float _hmdTargetFPS { LOD_OFFSET_FPS + LOD_DEFAULT_QUALITY_LEVEL * LOD_MAX_LIKELY_HMD_FPS };
|
||||
|
||||
float _octreeSizeScale = DEFAULT_OCTREE_SIZE_SCALE;
|
||||
int _boundaryLevelAdjust = 0;
|
||||
|
||||
uint64_t _decreaseFPSExpiry { 0 };
|
||||
uint64_t _increaseFPSExpiry { 0 };
|
||||
glm::vec4 _pidCoefs{ 1.0f, 0.0f, 0.0f, 1.0f }; // Kp, Ki, Kd, Kv
|
||||
glm::vec4 _pidHistory{ 0.0f };
|
||||
glm::vec4 _pidOutputs{ 0.0f };
|
||||
};
|
||||
|
||||
#endif // hifi_LODManager_h
|
||||
|
|
|
@ -787,7 +787,6 @@ Menu::Menu() {
|
|||
|
||||
// Developer > Show Overlays
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Overlays, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, "Enable Interstitial", 0, false);
|
||||
|
||||
#if 0 /// -------------- REMOVED FOR NOW --------------
|
||||
addDisabledActionAndSeparator(navigateMenu, "History");
|
||||
|
|
|
@ -187,16 +187,17 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
AvatarSharedPointer _avatar;
|
||||
};
|
||||
|
||||
auto avatarMap = getHashCopy();
|
||||
AvatarHash::iterator itr = avatarMap.begin();
|
||||
|
||||
const auto& views = qApp->getConicalViews();
|
||||
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(views,
|
||||
AvatarData::_avatarSortCoefficientSize,
|
||||
AvatarData::_avatarSortCoefficientCenter,
|
||||
AvatarData::_avatarSortCoefficientAge);
|
||||
sortedAvatars.reserve(avatarMap.size() - 1); // don't include MyAvatar
|
||||
|
||||
// sort
|
||||
auto avatarMap = getHashCopy();
|
||||
AvatarHash::iterator itr = avatarMap.begin();
|
||||
while (itr != avatarMap.end()) {
|
||||
const auto& avatar = std::static_pointer_cast<Avatar>(*itr);
|
||||
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
||||
|
@ -206,6 +207,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
}
|
||||
++itr;
|
||||
}
|
||||
const auto& sortedAvatarVector = sortedAvatars.getSortedVector();
|
||||
|
||||
// process in sorted order
|
||||
uint64_t startTime = usecTimestampNow();
|
||||
|
@ -216,8 +218,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
render::Transaction renderTransaction;
|
||||
workload::Transaction workloadTransaction;
|
||||
while (!sortedAvatars.empty()) {
|
||||
const SortableAvatar& sortData = sortedAvatars.top();
|
||||
for (auto it = sortedAvatarVector.begin(); it != sortedAvatarVector.end(); ++it) {
|
||||
const SortableAvatar& sortData = *it;
|
||||
const auto avatar = std::static_pointer_cast<OtherAvatar>(sortData.getAvatar());
|
||||
|
||||
// TODO: to help us scale to more avatars it would be nice to not have to poll orb state here
|
||||
|
@ -231,7 +233,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
|
||||
bool ignoring = DependencyManager::get<NodeList>()->isPersonalMutingNode(avatar->getID());
|
||||
if (ignoring) {
|
||||
sortedAvatars.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -260,26 +261,19 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
// --> some avatar velocity measurements may be a little off
|
||||
|
||||
// no time to simulate, but we take the time to count how many were tragically missed
|
||||
bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||
if (!inView) {
|
||||
break;
|
||||
}
|
||||
if (inView && avatar->hasNewJointData()) {
|
||||
numAVatarsNotUpdated++;
|
||||
}
|
||||
sortedAvatars.pop();
|
||||
while (inView && !sortedAvatars.empty()) {
|
||||
const SortableAvatar& newSortData = sortedAvatars.top();
|
||||
while (it != sortedAvatarVector.end()) {
|
||||
const SortableAvatar& newSortData = *it;
|
||||
const auto newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
|
||||
inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||
if (inView && newAvatar->hasNewJointData()) {
|
||||
numAVatarsNotUpdated++;
|
||||
bool inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||
// Once we reach an avatar that's not in view, all avatars after it will also be out of view
|
||||
if (!inView) {
|
||||
break;
|
||||
}
|
||||
sortedAvatars.pop();
|
||||
numAVatarsNotUpdated += (int)(newAvatar->hasNewJointData());
|
||||
++it;
|
||||
}
|
||||
break;
|
||||
}
|
||||
sortedAvatars.pop();
|
||||
}
|
||||
|
||||
if (_shouldRender) {
|
||||
|
|
|
@ -4313,7 +4313,8 @@ glm::mat4 MyAvatar::getCenterEyeCalibrationMat() const {
|
|||
auto centerEyeRot = Quaternions::Y_180;
|
||||
return createMatFromQuatAndPos(centerEyeRot, centerEyePos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_ROT, DEFAULT_AVATAR_MIDDLE_EYE_POS / getSensorToWorldScale());
|
||||
glm::mat4 headMat = getHeadCalibrationMat();
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_MIDDLE_EYE_ROT, extractTranslation(headMat) + DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4323,9 +4324,10 @@ glm::mat4 MyAvatar::getHeadCalibrationMat() const {
|
|||
if (headIndex >= 0) {
|
||||
auto headPos = getAbsoluteDefaultJointTranslationInObjectFrame(headIndex);
|
||||
auto headRot = getAbsoluteDefaultJointRotationInObjectFrame(headIndex);
|
||||
|
||||
return createMatFromQuatAndPos(headRot, headPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_ROT, DEFAULT_AVATAR_HEAD_POS / getSensorToWorldScale());
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_HEAD_ROT, DEFAULT_AVATAR_HEAD_POS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4337,7 +4339,7 @@ glm::mat4 MyAvatar::getSpine2CalibrationMat() const {
|
|||
auto spine2Rot = getAbsoluteDefaultJointRotationInObjectFrame(spine2Index);
|
||||
return createMatFromQuatAndPos(spine2Rot, spine2Pos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_ROT, DEFAULT_AVATAR_SPINE2_POS / getSensorToWorldScale());
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_SPINE2_ROT, DEFAULT_AVATAR_SPINE2_POS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4349,7 +4351,7 @@ glm::mat4 MyAvatar::getHipsCalibrationMat() const {
|
|||
auto hipsRot = getAbsoluteDefaultJointRotationInObjectFrame(hipsIndex);
|
||||
return createMatFromQuatAndPos(hipsRot, hipsPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_ROT, DEFAULT_AVATAR_HIPS_POS / getSensorToWorldScale());
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_HIPS_ROT, DEFAULT_AVATAR_HIPS_POS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4361,7 +4363,7 @@ glm::mat4 MyAvatar::getLeftFootCalibrationMat() const {
|
|||
auto leftFootRot = getAbsoluteDefaultJointRotationInObjectFrame(leftFootIndex);
|
||||
return createMatFromQuatAndPos(leftFootRot, leftFootPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_ROT, DEFAULT_AVATAR_LEFTFOOT_POS / getSensorToWorldScale());
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTFOOT_ROT, DEFAULT_AVATAR_LEFTFOOT_POS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4373,11 +4375,10 @@ glm::mat4 MyAvatar::getRightFootCalibrationMat() const {
|
|||
auto rightFootRot = getAbsoluteDefaultJointRotationInObjectFrame(rightFootIndex);
|
||||
return createMatFromQuatAndPos(rightFootRot, rightFootPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_ROT, DEFAULT_AVATAR_RIGHTFOOT_POS / getSensorToWorldScale());
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTFOOT_ROT, DEFAULT_AVATAR_RIGHTFOOT_POS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 MyAvatar::getRightArmCalibrationMat() const {
|
||||
int rightArmIndex = _skeletonModel->getRig().indexOfJoint("RightArm");
|
||||
if (rightArmIndex >= 0) {
|
||||
|
@ -4385,7 +4386,7 @@ glm::mat4 MyAvatar::getRightArmCalibrationMat() const {
|
|||
auto rightArmRot = getAbsoluteDefaultJointRotationInObjectFrame(rightArmIndex);
|
||||
return createMatFromQuatAndPos(rightArmRot, rightArmPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTARM_ROT, DEFAULT_AVATAR_RIGHTARM_POS / getSensorToWorldScale());
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTARM_ROT, DEFAULT_AVATAR_RIGHTARM_POS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4396,7 +4397,7 @@ glm::mat4 MyAvatar::getLeftArmCalibrationMat() const {
|
|||
auto leftArmRot = getAbsoluteDefaultJointRotationInObjectFrame(leftArmIndex);
|
||||
return createMatFromQuatAndPos(leftArmRot, leftArmPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_LEFTARM_POS / getSensorToWorldScale());
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTARM_ROT, DEFAULT_AVATAR_LEFTARM_POS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4407,7 +4408,7 @@ glm::mat4 MyAvatar::getRightHandCalibrationMat() const {
|
|||
auto rightHandRot = getAbsoluteDefaultJointRotationInObjectFrame(rightHandIndex);
|
||||
return createMatFromQuatAndPos(rightHandRot, rightHandPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTHAND_ROT, DEFAULT_AVATAR_RIGHTHAND_POS / getSensorToWorldScale());
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_RIGHTHAND_ROT, DEFAULT_AVATAR_RIGHTHAND_POS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4418,7 +4419,7 @@ glm::mat4 MyAvatar::getLeftHandCalibrationMat() const {
|
|||
auto leftHandRot = getAbsoluteDefaultJointRotationInObjectFrame(leftHandIndex);
|
||||
return createMatFromQuatAndPos(leftHandRot, leftHandPos / getSensorToWorldScale());
|
||||
} else {
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTHAND_ROT, DEFAULT_AVATAR_LEFTHAND_POS / getSensorToWorldScale());
|
||||
return createMatFromQuatAndPos(DEFAULT_AVATAR_LEFTHAND_ROT, DEFAULT_AVATAR_LEFTHAND_POS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1034,7 +1034,7 @@ public:
|
|||
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
|
||||
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
|
||||
|
||||
// all calibration matrices are in absolute avatar space.
|
||||
// all calibration matrices are in absolute sensor space.
|
||||
glm::mat4 getCenterEyeCalibrationMat() const;
|
||||
glm::mat4 getHeadCalibrationMat() const;
|
||||
glm::mat4 getSpine2CalibrationMat() const;
|
||||
|
|
23
interface/src/avatar/MyAvatarHeadTransformNode.cpp
Normal file
23
interface/src/avatar/MyAvatarHeadTransformNode.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Created by Sabrina Shanman 8/14/2018
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "MyAvatarHeadTransformNode.h"
|
||||
|
||||
#include "DependencyManager.h"
|
||||
#include "AvatarManager.h"
|
||||
#include "MyAvatar.h"
|
||||
|
||||
Transform MyAvatarHeadTransformNode::getTransform() {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
glm::vec3 pos = myAvatar->getHeadPosition();
|
||||
glm::quat headOri = myAvatar->getHeadOrientation();
|
||||
glm::quat ori = headOri * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT);
|
||||
|
||||
return Transform(ori, glm::vec3(1.0f), pos);
|
||||
}
|
19
interface/src/avatar/MyAvatarHeadTransformNode.h
Normal file
19
interface/src/avatar/MyAvatarHeadTransformNode.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Created by Sabrina Shanman 8/14/2018
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#ifndef hifi_MyAvatarHeadTransformNode_h
|
||||
#define hifi_MyAvatarHeadTransformNode_h
|
||||
|
||||
#include "TransformNode.h"
|
||||
|
||||
class MyAvatarHeadTransformNode : public TransformNode {
|
||||
public:
|
||||
MyAvatarHeadTransformNode() { }
|
||||
Transform getTransform() override;
|
||||
};
|
||||
|
||||
#endif // hifi_MyAvatarHeadTransformNode_h
|
|
@ -29,20 +29,23 @@ OtherAvatar::~OtherAvatar() {
|
|||
}
|
||||
|
||||
void OtherAvatar::removeOrb() {
|
||||
if (qApp->getOverlays().isAddedOverlay(_otherAvatarOrbMeshPlaceholderID)) {
|
||||
if (!_otherAvatarOrbMeshPlaceholderID.isNull()) {
|
||||
qApp->getOverlays().deleteOverlay(_otherAvatarOrbMeshPlaceholderID);
|
||||
_otherAvatarOrbMeshPlaceholderID = UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::updateOrbPosition() {
|
||||
if (_otherAvatarOrbMeshPlaceholder != nullptr) {
|
||||
_otherAvatarOrbMeshPlaceholder->setWorldPosition(getHead()->getPosition());
|
||||
if (_otherAvatarOrbMeshPlaceholderID.isNull()) {
|
||||
_otherAvatarOrbMeshPlaceholderID = qApp->getOverlays().addOverlay(_otherAvatarOrbMeshPlaceholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::createOrb() {
|
||||
if (_otherAvatarOrbMeshPlaceholderID == UNKNOWN_OVERLAY_ID ||
|
||||
!qApp->getOverlays().isAddedOverlay(_otherAvatarOrbMeshPlaceholderID)) {
|
||||
if (_otherAvatarOrbMeshPlaceholderID.isNull()) {
|
||||
_otherAvatarOrbMeshPlaceholder = std::make_shared<Sphere3DOverlay>();
|
||||
_otherAvatarOrbMeshPlaceholder->setAlpha(1.0f);
|
||||
_otherAvatarOrbMeshPlaceholder->setColor({ 0xFF, 0x00, 0xFF });
|
||||
|
|
|
@ -40,11 +40,9 @@ QmlCommerce::QmlCommerce() {
|
|||
connect(ledger.data(), &Ledger::transferAssetToUsernameResult, this, &QmlCommerce::transferAssetToUsernameResult);
|
||||
connect(ledger.data(), &Ledger::availableUpdatesResult, this, &QmlCommerce::availableUpdatesResult);
|
||||
connect(ledger.data(), &Ledger::updateItemResult, this, &QmlCommerce::updateItemResult);
|
||||
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
|
||||
setPassphrase("");
|
||||
});
|
||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { setPassphrase(""); });
|
||||
|
||||
_appsPath = PathUtils::getAppDataPath() + "Apps/";
|
||||
}
|
||||
|
@ -105,7 +103,11 @@ void QmlCommerce::balance() {
|
|||
}
|
||||
}
|
||||
|
||||
void QmlCommerce::inventory(const QString& editionFilter, const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage) {
|
||||
void QmlCommerce::inventory(const QString& editionFilter,
|
||||
const QString& typeFilter,
|
||||
const QString& titleFilter,
|
||||
const int& page,
|
||||
const int& perPage) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
QStringList cachedPublicKeys = wallet->listPublicKeys();
|
||||
|
@ -166,24 +168,30 @@ void QmlCommerce::certificateInfo(const QString& certificateId) {
|
|||
ledger->certificateInfo(certificateId);
|
||||
}
|
||||
|
||||
void QmlCommerce::transferAssetToNode(const QString& nodeID, const QString& certificateID, const int& amount, const QString& optionalMessage) {
|
||||
void QmlCommerce::transferAssetToNode(const QString& nodeID,
|
||||
const QString& certificateID,
|
||||
const int& amount,
|
||||
const QString& optionalMessage) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
QStringList keys = wallet->listPublicKeys();
|
||||
if (keys.count() == 0) {
|
||||
QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } };
|
||||
QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } };
|
||||
return emit transferAssetToNodeResult(result);
|
||||
}
|
||||
QString key = keys[0];
|
||||
ledger->transferAssetToNode(key, nodeID, certificateID, amount, optionalMessage);
|
||||
}
|
||||
|
||||
void QmlCommerce::transferAssetToUsername(const QString& username, const QString& certificateID, const int& amount, const QString& optionalMessage) {
|
||||
void QmlCommerce::transferAssetToUsername(const QString& username,
|
||||
const QString& certificateID,
|
||||
const int& amount,
|
||||
const QString& optionalMessage) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
QStringList keys = wallet->listPublicKeys();
|
||||
if (keys.count() == 0) {
|
||||
QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } };
|
||||
QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } };
|
||||
return emit transferAssetToUsernameResult(result);
|
||||
}
|
||||
QString key = keys[0];
|
||||
|
@ -194,10 +202,7 @@ void QmlCommerce::replaceContentSet(const QString& itemHref, const QString& cert
|
|||
auto ledger = DependencyManager::get<Ledger>();
|
||||
ledger->updateLocation(certificateID, DependencyManager::get<AddressManager>()->getPlaceName(), true);
|
||||
qApp->replaceDomainContent(itemHref);
|
||||
QJsonObject messageProperties = {
|
||||
{ "status", "SuccessfulRequestToReplaceContent" },
|
||||
{ "content_set_url", itemHref }
|
||||
};
|
||||
QJsonObject messageProperties = { { "status", "SuccessfulRequestToReplaceContent" }, { "content_set_url", itemHref } };
|
||||
UserActivityLogger::getInstance().logAction("replace_domain_content", messageProperties);
|
||||
|
||||
emit contentSetChanged(itemHref);
|
||||
|
@ -214,10 +219,7 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) {
|
|||
|
||||
QDir directory(_appsPath);
|
||||
QStringList apps = directory.entryList(QStringList("*.app.json"));
|
||||
foreach(QString appFileName, apps) {
|
||||
installedAppsFromMarketplace += appFileName;
|
||||
installedAppsFromMarketplace += ",";
|
||||
|
||||
foreach (QString appFileName, apps) {
|
||||
// If we were supplied a "justInstalledAppID" argument, that means we're entering this function
|
||||
// to get the new list of installed apps immediately after installing an app.
|
||||
// In that case, the app we installed may not yet have its associated script running -
|
||||
|
@ -243,10 +245,12 @@ QString QmlCommerce::getInstalledApps(const QString& justInstalledAppID) {
|
|||
// delete the .app.json from the user's local disk.
|
||||
if (!runningScripts.contains(scriptURL)) {
|
||||
if (!appFile.remove()) {
|
||||
qCWarning(commerce)
|
||||
<< "Couldn't delete local .app.json file (app's script isn't running). App filename is:"
|
||||
<< appFileName;
|
||||
qCWarning(commerce) << "Couldn't delete local .app.json file (app's script isn't running). App filename is:"
|
||||
<< appFileName;
|
||||
}
|
||||
} else {
|
||||
installedAppsFromMarketplace += appFileName;
|
||||
installedAppsFromMarketplace += ",";
|
||||
}
|
||||
} else {
|
||||
qCDebug(commerce) << "Couldn't open local .app.json file for reading.";
|
||||
|
@ -317,7 +321,9 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) {
|
|||
// Read from the file to know what .js script to stop
|
||||
QFile appFile(_appsPath + "/" + appHref.fileName());
|
||||
if (!appFile.open(QIODevice::ReadOnly)) {
|
||||
qCDebug(commerce) << "Couldn't open local .app.json file for deletion. Cannot continue with app uninstallation. App filename is:" << appHref.fileName();
|
||||
qCDebug(commerce)
|
||||
<< "Couldn't open local .app.json file for deletion. Cannot continue with app uninstallation. App filename is:"
|
||||
<< appHref.fileName();
|
||||
return false;
|
||||
}
|
||||
QJsonDocument appFileJsonDocument = QJsonDocument::fromJson(appFile.readAll());
|
||||
|
@ -325,13 +331,15 @@ bool QmlCommerce::uninstallApp(const QString& itemHref) {
|
|||
QString scriptUrl = appFileJsonObject["scriptURL"].toString();
|
||||
|
||||
if (!DependencyManager::get<ScriptEngines>()->stopScript(scriptUrl.trimmed(), false)) {
|
||||
qCWarning(commerce) << "Couldn't stop script during app uninstall. Continuing anyway. ScriptURL is:" << scriptUrl.trimmed();
|
||||
qCWarning(commerce) << "Couldn't stop script during app uninstall. Continuing anyway. ScriptURL is:"
|
||||
<< scriptUrl.trimmed();
|
||||
}
|
||||
|
||||
// Delete the .app.json from the filesystem
|
||||
// remove() closes the file first.
|
||||
if (!appFile.remove()) {
|
||||
qCWarning(commerce) << "Couldn't delete local .app.json file during app uninstall. Continuing anyway. App filename is:" << appHref.fileName();
|
||||
qCWarning(commerce) << "Couldn't delete local .app.json file during app uninstall. Continuing anyway. App filename is:"
|
||||
<< appHref.fileName();
|
||||
}
|
||||
|
||||
QFileInfo appFileInfo(appFile);
|
||||
|
@ -352,7 +360,8 @@ bool QmlCommerce::openApp(const QString& itemHref) {
|
|||
QJsonObject appFileJsonObject = appFileJsonDocument.object();
|
||||
QString homeUrl = appFileJsonObject["homeURL"].toString();
|
||||
|
||||
auto tablet = dynamic_cast<TabletProxy*>(DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
auto tablet = dynamic_cast<TabletProxy*>(
|
||||
DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
if (homeUrl.contains(".qml", Qt::CaseInsensitive)) {
|
||||
tablet->loadQMLSource(homeUrl);
|
||||
} else if (homeUrl.contains(".html", Qt::CaseInsensitive)) {
|
||||
|
@ -377,7 +386,7 @@ void QmlCommerce::updateItem(const QString& certificateId) {
|
|||
auto wallet = DependencyManager::get<Wallet>();
|
||||
QStringList keys = wallet->listPublicKeys();
|
||||
if (keys.count() == 0) {
|
||||
QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } };
|
||||
QJsonObject result{ { "status", "fail" }, { "message", "Uninitialized Wallet." } };
|
||||
return emit updateItemResult(result);
|
||||
}
|
||||
QString key = keys[0];
|
||||
|
|
|
@ -32,9 +32,6 @@ PickResultPointer CollisionPickResult::compareAndProcessNewResult(const PickResu
|
|||
}
|
||||
|
||||
intersects = entityIntersections.size() || avatarIntersections.size();
|
||||
if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) {
|
||||
loadState = (LoadState)newCollisionResult->loadState;
|
||||
}
|
||||
|
||||
return std::make_shared<CollisionPickResult>(*this);
|
||||
}
|
||||
|
@ -80,23 +77,42 @@ QVariantMap CollisionPickResult::toVariantMap() const {
|
|||
}
|
||||
|
||||
variantMap["intersectingObjects"] = qIntersectingObjects;
|
||||
variantMap["loaded"] = (loadState == LOAD_STATE_LOADED);
|
||||
variantMap["collisionRegion"] = pickVariant;
|
||||
|
||||
return variantMap;
|
||||
}
|
||||
|
||||
bool CollisionPick::isShapeInfoReady() {
|
||||
bool CollisionPick::isLoaded() const {
|
||||
return !_mathPick.shouldComputeShapeInfo() || (_cachedResource && _cachedResource->isLoaded());
|
||||
}
|
||||
|
||||
bool CollisionPick::getShapeInfoReady() {
|
||||
if (_mathPick.shouldComputeShapeInfo()) {
|
||||
if (_cachedResource && _cachedResource->isLoaded()) {
|
||||
computeShapeInfo(_mathPick, *_mathPick.shapeInfo, _cachedResource);
|
||||
return true;
|
||||
_mathPick.loaded = true;
|
||||
} else {
|
||||
_mathPick.loaded = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
computeShapeInfoDimensionsOnly(_mathPick, *_mathPick.shapeInfo, _cachedResource);
|
||||
_mathPick.loaded = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return _mathPick.loaded;
|
||||
}
|
||||
|
||||
void CollisionPick::computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) {
|
||||
ShapeType type = shapeInfo.getType();
|
||||
glm::vec3 dimensions = pick.transform.getScale();
|
||||
QString modelURL = (resource ? resource->getURL().toString() : "");
|
||||
if (type == SHAPE_TYPE_COMPOUND) {
|
||||
shapeInfo.setParams(type, dimensions, modelURL);
|
||||
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
|
||||
shapeInfo.setParams(type, 0.5f * dimensions, modelURL);
|
||||
} else {
|
||||
shapeInfo.setParams(type, 0.5f * dimensions, modelURL);
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource) {
|
||||
|
@ -328,8 +344,25 @@ void CollisionPick::computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo
|
|||
}
|
||||
}
|
||||
|
||||
CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) :
|
||||
Pick(filter, maxDistance, enabled),
|
||||
_mathPick(collisionRegion),
|
||||
_physicsEngine(physicsEngine) {
|
||||
if (collisionRegion.shouldComputeShapeInfo()) {
|
||||
_cachedResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(collisionRegion.modelURL);
|
||||
}
|
||||
_mathPick.loaded = isLoaded();
|
||||
}
|
||||
|
||||
CollisionRegion CollisionPick::getMathematicalPick() const {
|
||||
return _mathPick;
|
||||
CollisionRegion mathPick = _mathPick;
|
||||
mathPick.loaded = isLoaded();
|
||||
if (!parentTransform) {
|
||||
return mathPick;
|
||||
} else {
|
||||
mathPick.transform = parentTransform->getTransform().worldTransform(mathPick.transform);
|
||||
return mathPick;
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersections) const {
|
||||
|
@ -356,31 +389,37 @@ void CollisionPick::filterIntersections(std::vector<ContactTestResult>& intersec
|
|||
}
|
||||
|
||||
PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) {
|
||||
if (!isShapeInfoReady()) {
|
||||
if (!pick.loaded) {
|
||||
// Cannot compute result
|
||||
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
}
|
||||
getShapeInfoReady();
|
||||
|
||||
auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform);
|
||||
auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold);
|
||||
filterIntersections(entityIntersections);
|
||||
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector<ContactTestResult>());
|
||||
return std::make_shared<CollisionPickResult>(pick, entityIntersections, std::vector<ContactTestResult>());
|
||||
}
|
||||
|
||||
PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) {
|
||||
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
}
|
||||
|
||||
PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) {
|
||||
if (!isShapeInfoReady()) {
|
||||
if (!pick.loaded) {
|
||||
// Cannot compute result
|
||||
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
}
|
||||
getShapeInfoReady();
|
||||
|
||||
auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform);
|
||||
auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform, USER_COLLISION_GROUP_DYNAMIC, pick.threshold);
|
||||
filterIntersections(avatarIntersections);
|
||||
return std::make_shared<CollisionPickResult>(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector<ContactTestResult>(), avatarIntersections);
|
||||
return std::make_shared<CollisionPickResult>(pick, std::vector<ContactTestResult>(), avatarIntersections);
|
||||
}
|
||||
|
||||
PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) {
|
||||
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
return std::make_shared<CollisionPickResult>(pick.toVariantMap(), std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
}
|
||||
|
||||
Transform CollisionPick::getResultTransform() const {
|
||||
return Transform(getMathematicalPick().transform);
|
||||
}
|
|
@ -11,35 +11,28 @@
|
|||
#include <PhysicsEngine.h>
|
||||
#include <model-networking/ModelCache.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <TransformNode.h>
|
||||
#include <Pick.h>
|
||||
|
||||
class CollisionPickResult : public PickResult {
|
||||
public:
|
||||
enum LoadState {
|
||||
LOAD_STATE_UNKNOWN,
|
||||
LOAD_STATE_NOT_LOADED,
|
||||
LOAD_STATE_LOADED
|
||||
};
|
||||
|
||||
CollisionPickResult() {}
|
||||
CollisionPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {}
|
||||
|
||||
CollisionPickResult(const CollisionRegion& searchRegion, LoadState loadState, const std::vector<ContactTestResult>& entityIntersections, const std::vector<ContactTestResult>& avatarIntersections) :
|
||||
CollisionPickResult(const CollisionRegion& searchRegion, const std::vector<ContactTestResult>& entityIntersections, const std::vector<ContactTestResult>& avatarIntersections) :
|
||||
PickResult(searchRegion.toVariantMap()),
|
||||
loadState(loadState),
|
||||
intersects(entityIntersections.size() || avatarIntersections.size()),
|
||||
entityIntersections(entityIntersections),
|
||||
avatarIntersections(avatarIntersections) {
|
||||
avatarIntersections(avatarIntersections)
|
||||
{
|
||||
}
|
||||
|
||||
CollisionPickResult(const CollisionPickResult& collisionPickResult) : PickResult(collisionPickResult.pickVariant) {
|
||||
avatarIntersections = collisionPickResult.avatarIntersections;
|
||||
entityIntersections = collisionPickResult.entityIntersections;
|
||||
intersects = collisionPickResult.intersects;
|
||||
loadState = collisionPickResult.loadState;
|
||||
}
|
||||
|
||||
LoadState loadState { LOAD_STATE_UNKNOWN };
|
||||
bool intersects { false };
|
||||
std::vector<ContactTestResult> entityIntersections;
|
||||
std::vector<ContactTestResult> avatarIntersections;
|
||||
|
@ -54,28 +47,24 @@ public:
|
|||
|
||||
class CollisionPick : public Pick<CollisionRegion> {
|
||||
public:
|
||||
CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) :
|
||||
Pick(filter, maxDistance, enabled),
|
||||
_mathPick(collisionRegion),
|
||||
_physicsEngine(physicsEngine) {
|
||||
if (collisionRegion.shouldComputeShapeInfo()) {
|
||||
_cachedResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(collisionRegion.modelURL);
|
||||
}
|
||||
}
|
||||
CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine);
|
||||
|
||||
CollisionRegion getMathematicalPick() const override;
|
||||
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override {
|
||||
return std::make_shared<CollisionPickResult>(pickVariant, CollisionPickResult::LOAD_STATE_UNKNOWN, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
return std::make_shared<CollisionPickResult>(pickVariant, std::vector<ContactTestResult>(), std::vector<ContactTestResult>());
|
||||
}
|
||||
PickResultPointer getEntityIntersection(const CollisionRegion& pick) override;
|
||||
PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override;
|
||||
PickResultPointer getAvatarIntersection(const CollisionRegion& pick) override;
|
||||
PickResultPointer getHUDIntersection(const CollisionRegion& pick) override;
|
||||
|
||||
Transform getResultTransform() const override;
|
||||
protected:
|
||||
// Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use.
|
||||
bool isShapeInfoReady();
|
||||
// Returns true if the resource for _mathPick.shapeInfo is loaded or if a resource is not needed.
|
||||
bool isLoaded() const;
|
||||
// Returns true if _mathPick.shapeInfo is valid. Otherwise, attempts to get the _mathPick ready for use.
|
||||
bool getShapeInfoReady();
|
||||
void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
|
||||
void computeShapeInfoDimensionsOnly(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
|
||||
void filterIntersections(std::vector<ContactTestResult>& intersections) const;
|
||||
|
||||
CollisionRegion _mathPick;
|
||||
|
|
|
@ -35,6 +35,37 @@ void LaserPointer::editRenderStatePath(const std::string& state, const QVariant&
|
|||
}
|
||||
}
|
||||
|
||||
QVariantMap LaserPointer::toVariantMap() const {
|
||||
QVariantMap qVariantMap;
|
||||
|
||||
QVariantMap qRenderStates;
|
||||
for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) {
|
||||
auto renderState = iter->second;
|
||||
QVariantMap qRenderState;
|
||||
qRenderState["start"] = renderState->getStartID();
|
||||
qRenderState["path"] = std::static_pointer_cast<RenderState>(renderState)->getPathID();
|
||||
qRenderState["end"] = renderState->getEndID();
|
||||
qRenderStates[iter->first.c_str()] = qRenderState;
|
||||
}
|
||||
qVariantMap["renderStates"] = qRenderStates;
|
||||
|
||||
QVariantMap qDefaultRenderStates;
|
||||
for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) {
|
||||
float distance = iter->second.first;
|
||||
auto defaultRenderState = iter->second.second;
|
||||
QVariantMap qDefaultRenderState;
|
||||
|
||||
qDefaultRenderState["distance"] = distance;
|
||||
qDefaultRenderState["start"] = defaultRenderState->getStartID();
|
||||
qDefaultRenderState["path"] = std::static_pointer_cast<RenderState>(defaultRenderState)->getPathID();
|
||||
qDefaultRenderState["end"] = defaultRenderState->getEndID();
|
||||
qDefaultRenderStates[iter->first.c_str()] = qDefaultRenderState;
|
||||
}
|
||||
qVariantMap["defaultRenderStates"] = qDefaultRenderStates;
|
||||
|
||||
return qVariantMap;
|
||||
}
|
||||
|
||||
glm::vec3 LaserPointer::getPickOrigin(const PickResultPointer& pickResult) const {
|
||||
auto rayPickResult = std::static_pointer_cast<RayPickResult>(pickResult);
|
||||
return (rayPickResult ? vec3FromVariant(rayPickResult->pickVariant["origin"]) : glm::vec3(0.0f));
|
||||
|
|
|
@ -42,6 +42,8 @@ public:
|
|||
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
|
||||
bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
|
||||
|
||||
QVariantMap toVariantMap() const override;
|
||||
|
||||
static std::shared_ptr<StartEndRenderState> buildRenderState(const QVariantMap& propMap);
|
||||
|
||||
protected:
|
||||
|
|
27
interface/src/raypick/MouseTransformNode.cpp
Normal file
27
interface/src/raypick/MouseTransformNode.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Created by Sabrina Shanman 8/14/2018
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "MouseTransformNode.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "display-plugins/CompositorHelper.h"
|
||||
#include "RayPick.h"
|
||||
|
||||
Transform MouseTransformNode::getTransform() {
|
||||
QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition();
|
||||
if (position.isValid()) {
|
||||
Transform transform;
|
||||
QVariantMap posMap = position.toMap();
|
||||
PickRay pickRay = qApp->getCamera().computePickRay(posMap["x"].toFloat(), posMap["y"].toFloat());
|
||||
transform.setTranslation(pickRay.origin);
|
||||
transform.setRotation(rotationBetween(Vectors::UP, pickRay.direction));
|
||||
return transform;
|
||||
}
|
||||
|
||||
return Transform();
|
||||
}
|
18
interface/src/raypick/MouseTransformNode.h
Normal file
18
interface/src/raypick/MouseTransformNode.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// Created by Sabrina Shanman 8/14/2018
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#ifndef hifi_MouseTransformNode_h
|
||||
#define hifi_MouseTransformNode_h
|
||||
|
||||
#include "TransformNode.h"
|
||||
|
||||
class MouseTransformNode : public TransformNode {
|
||||
public:
|
||||
Transform getTransform() override;
|
||||
};
|
||||
|
||||
#endif // hifi_MouseTransformNode_h
|
|
@ -70,4 +70,16 @@ glm::vec3 ParabolaPick::getAcceleration() const {
|
|||
return scale * (DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * _accelerationAxis);
|
||||
}
|
||||
return scale * _accelerationAxis;
|
||||
}
|
||||
|
||||
Transform ParabolaPick::getResultTransform() const {
|
||||
PickResultPointer result = getPrevPickResult();
|
||||
if (!result) {
|
||||
return Transform();
|
||||
}
|
||||
|
||||
auto parabolaResult = std::static_pointer_cast<ParabolaPickResult>(result);
|
||||
Transform transform;
|
||||
transform.setTranslation(parabolaResult->intersection);
|
||||
return transform;
|
||||
}
|
|
@ -83,6 +83,7 @@ public:
|
|||
PickResultPointer getOverlayIntersection(const PickParabola& pick) override;
|
||||
PickResultPointer getAvatarIntersection(const PickParabola& pick) override;
|
||||
PickResultPointer getHUDIntersection(const PickParabola& pick) override;
|
||||
Transform getResultTransform() const override;
|
||||
|
||||
protected:
|
||||
float _speed;
|
||||
|
|
|
@ -60,6 +60,35 @@ void ParabolaPointer::editRenderStatePath(const std::string& state, const QVaria
|
|||
}
|
||||
}
|
||||
|
||||
QVariantMap ParabolaPointer::toVariantMap() const {
|
||||
QVariantMap qVariantMap;
|
||||
|
||||
QVariantMap qRenderStates;
|
||||
for (auto iter = _renderStates.cbegin(); iter != _renderStates.cend(); iter++) {
|
||||
auto renderState = iter->second;
|
||||
QVariantMap qRenderState;
|
||||
qRenderState["start"] = renderState->getStartID();
|
||||
qRenderState["end"] = renderState->getEndID();
|
||||
qRenderStates[iter->first.c_str()] = qRenderState;
|
||||
}
|
||||
qVariantMap["renderStates"] = qRenderStates;
|
||||
|
||||
QVariantMap qDefaultRenderStates;
|
||||
for (auto iter = _defaultRenderStates.cbegin(); iter != _defaultRenderStates.cend(); iter++) {
|
||||
float distance = iter->second.first;
|
||||
auto defaultRenderState = iter->second.second;
|
||||
QVariantMap qDefaultRenderState;
|
||||
|
||||
qDefaultRenderState["distance"] = distance;
|
||||
qDefaultRenderState["start"] = defaultRenderState->getStartID();
|
||||
qDefaultRenderState["end"] = defaultRenderState->getEndID();
|
||||
qDefaultRenderStates[iter->first.c_str()] = qDefaultRenderState;
|
||||
}
|
||||
qVariantMap["defaultRenderStates"] = qDefaultRenderStates;
|
||||
|
||||
return qVariantMap;
|
||||
}
|
||||
|
||||
glm::vec3 ParabolaPointer::getPickOrigin(const PickResultPointer& pickResult) const {
|
||||
auto parabolaPickResult = std::static_pointer_cast<ParabolaPickResult>(pickResult);
|
||||
return (parabolaPickResult ? vec3FromVariant(parabolaPickResult->pickVariant["origin"]) : glm::vec3(0.0f));
|
||||
|
|
|
@ -97,6 +97,8 @@ public:
|
|||
ParabolaPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
|
||||
bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
|
||||
|
||||
QVariantMap toVariantMap() const override;
|
||||
|
||||
static std::shared_ptr<StartEndRenderState> buildRenderState(const QVariantMap& propMap);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -23,6 +23,13 @@
|
|||
#include "MouseParabolaPick.h"
|
||||
#include "CollisionPick.h"
|
||||
|
||||
#include "SpatialParentFinder.h"
|
||||
#include "NestableTransformNode.h"
|
||||
#include "PickTransformNode.h"
|
||||
#include "MouseTransformNode.h"
|
||||
#include "avatar/MyAvatarHeadTransformNode.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
||||
#include <ScriptEngine.h>
|
||||
|
||||
unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) {
|
||||
|
@ -276,8 +283,10 @@ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& propert
|
|||
}
|
||||
|
||||
CollisionRegion collisionRegion(propMap);
|
||||
auto collisionPick = std::make_shared<CollisionPick>(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine());
|
||||
collisionPick->parentTransform = createTransformNode(propMap);
|
||||
|
||||
return DependencyManager::get<PickManager>()->addPick(PickQuery::Collision, std::make_shared<CollisionPick>(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine()));
|
||||
return DependencyManager::get<PickManager>()->addPick(PickQuery::Collision, collisionPick);
|
||||
}
|
||||
|
||||
void PickScriptingInterface::enablePick(unsigned int uid) {
|
||||
|
@ -351,3 +360,43 @@ unsigned int PickScriptingInterface::getPerFrameTimeBudget() const {
|
|||
void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) {
|
||||
DependencyManager::get<PickManager>()->setPerFrameTimeBudget(numUsecs);
|
||||
}
|
||||
|
||||
std::shared_ptr<TransformNode> PickScriptingInterface::createTransformNode(const QVariantMap& propMap) {
|
||||
if (propMap["parentID"].isValid()) {
|
||||
QUuid parentUuid = propMap["parentID"].toUuid();
|
||||
if (!parentUuid.isNull()) {
|
||||
// Infer object type from parentID
|
||||
// For now, assume a QUuuid is a SpatiallyNestable. This should change when picks are converted over to QUuids.
|
||||
bool success;
|
||||
std::weak_ptr<SpatiallyNestable> nestablePointer = DependencyManager::get<SpatialParentFinder>()->find(parentUuid, success, nullptr);
|
||||
int parentJointIndex = 0;
|
||||
if (propMap["parentJointIndex"].isValid()) {
|
||||
parentJointIndex = propMap["parentJointIndex"].toInt();
|
||||
}
|
||||
auto sharedNestablePointer = nestablePointer.lock();
|
||||
if (success && sharedNestablePointer) {
|
||||
return std::make_shared<NestableTransformNode>(nestablePointer, parentJointIndex);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int pickID = propMap["parentID"].toUInt();
|
||||
if (pickID != 0) {
|
||||
return std::make_shared<PickTransformNode>(pickID);
|
||||
}
|
||||
}
|
||||
|
||||
if (propMap["joint"].isValid()) {
|
||||
QString joint = propMap["joint"].toString();
|
||||
if (joint == "Mouse") {
|
||||
return std::make_shared<MouseTransformNode>();
|
||||
} else if (joint == "Avatar") {
|
||||
return std::make_shared<MyAvatarHeadTransformNode>();
|
||||
} else if (!joint.isNull()) {
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
int jointIndex = myAvatar->getJointIndex(joint);
|
||||
return std::make_shared<NestableTransformNode>(myAvatar, jointIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return std::shared_ptr<TransformNode>();
|
||||
}
|
|
@ -152,9 +152,6 @@ public:
|
|||
* @property {CollisionRegion} collisionRegion The CollisionRegion that was used. Valid even if there was no intersection.
|
||||
*/
|
||||
|
||||
// TODO: Add this to the CollisionPickResult jsdoc once model collision picks are working
|
||||
//* @property {boolean} loaded If the CollisionRegion was successfully loaded (may be false if a model was used)
|
||||
|
||||
/**jsdoc
|
||||
* Information about the Collision Pick's intersection with an object
|
||||
*
|
||||
|
@ -320,6 +317,9 @@ public slots:
|
|||
* @returns {number}
|
||||
*/
|
||||
static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; }
|
||||
|
||||
protected:
|
||||
static std::shared_ptr<TransformNode> createTransformNode(const QVariantMap& propMap);
|
||||
};
|
||||
|
||||
#endif // hifi_PickScriptingInterface_h
|
||||
|
|
|
@ -76,16 +76,19 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
|
|||
* @property {number} distance The distance at which to render the end of this Ray Pointer, if one is defined.
|
||||
*/
|
||||
/**jsdoc
|
||||
* A set of properties used to define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something.
|
||||
* A set of properties which define the visual aspect of a Ray Pointer in the case that the Pointer is intersecting something.
|
||||
*
|
||||
* @typedef {object} Pointers.RayPointerRenderState
|
||||
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
|
||||
* @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the beginning of the Ray Pointer, if desired.
|
||||
* @property {Overlays.OverlayProperties} [path] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field), which <b>must</b> be <code>"line3d"</code>.
|
||||
* An overlay to represent the path of the Ray Pointer, if desired.
|
||||
* @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the end of the Ray Pointer, if desired.
|
||||
* @property {string} name When using {@link Pointers.createPointer}, the name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
|
||||
* @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Ray Pointer,
|
||||
* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
|
||||
* @property {Overlays.OverlayProperties|QUuid} [path] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the path of the Ray Pointer,
|
||||
* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field), which <b>must</b> be <code>"line3d"</code>.
|
||||
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
|
||||
* @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the end of the Ray Pointer,
|
||||
* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
|
||||
*/
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick.
|
||||
|
@ -99,8 +102,12 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
|
|||
* @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. <code>0-1</code>. If 0 or 1,
|
||||
* the normal will follow exactly.
|
||||
* @property {boolean} [enabled=false]
|
||||
* @property {Pointers.RayPointerRenderState[]} [renderStates] A list of different visual states to switch between.
|
||||
* @property {Pointers.DefaultRayPointerRenderState[]} [defaultRenderStates] A list of different visual states to use if there is no intersection.
|
||||
* @property {Pointers.RayPointerRenderState[]|Object.<string, Pointers.RayPointerRenderState>} [renderStates] A collection of different visual states to switch between.
|
||||
* When using {@link Pointers.createPointer}, a list of RayPointerRenderStates.
|
||||
* When returned from {@link Pointers.getPointerProperties}, a map between render state names and RayPointRenderStates.
|
||||
* @property {Pointers.DefaultRayPointerRenderState[]|Object.<string, Pointers.DefaultRayPointerRenderState>} [defaultRenderStates] A collection of different visual states to use if there is no intersection.
|
||||
* When using {@link Pointers.createPointer}, a list of DefaultRayPointerRenderStates.
|
||||
* When returned from {@link Pointers.getPointerProperties}, a map between render state names and DefaultRayPointRenderStates.
|
||||
* @property {boolean} [hover=false] If this Pointer should generate hover events.
|
||||
* @property {Pointers.Trigger[]} [triggers] A list of different triggers mechanisms that control this Pointer's click event generation.
|
||||
*/
|
||||
|
@ -224,12 +231,15 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
|
|||
* A set of properties used to define the visual aspect of a Parabola Pointer in the case that the Pointer is intersecting something.
|
||||
*
|
||||
* @typedef {object} Pointers.ParabolaPointerRenderState
|
||||
* @property {string} name The name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
|
||||
* @property {Overlays.OverlayProperties} [start] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the beginning of the Parabola Pointer, if desired.
|
||||
* @property {Pointers.ParabolaProperties} [path] The rendering properties of the parabolic path defined by the Parabola Pointer.
|
||||
* @property {Overlays.OverlayProperties} [end] All of the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* An overlay to represent the end of the Parabola Pointer, if desired.
|
||||
* @property {string} name When using {@link Pointers.createPointer}, the name of this render state, used by {@link Pointers.setRenderState} and {@link Pointers.editRenderState}
|
||||
* @property {Overlays.OverlayProperties|QUuid} [start] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the beginning of the Parabola Pointer,
|
||||
* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
|
||||
* @property {Pointers.ParabolaProperties} [path] When using {@link Pointers.createPointer}, the optionally defined rendering properties of the parabolic path defined by the Parabola Pointer.
|
||||
* Not defined in {@link Pointers.getPointerProperties}.
|
||||
* @property {Overlays.OverlayProperties|QUuid} [end] When using {@link Pointers.createPointer}, an optionally defined overlay to represent the end of the Parabola Pointer,
|
||||
* using the properties you would normally pass to {@link Overlays.addOverlay}, plus the type (as a <code>type</code> field).
|
||||
* When returned from {@link Pointers.getPointerProperties}, the ID of the created overlay if it exists, or a null ID otherwise.
|
||||
*/
|
||||
/**jsdoc
|
||||
* A set of properties that can be passed to {@link Pointers.createPointer} to create a new Pointer. Contains the relevant {@link Picks.PickProperties} to define the underlying Pick.
|
||||
|
@ -243,8 +253,12 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
|
|||
* @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. <code>0-1</code>. If 0 or 1,
|
||||
* the normal will follow exactly.
|
||||
* @property {boolean} [enabled=false]
|
||||
* @property {Pointers.ParabolaPointerRenderState[]} [renderStates] A list of different visual states to switch between.
|
||||
* @property {Pointers.DefaultParabolaPointerRenderState[]} [defaultRenderStates] A list of different visual states to use if there is no intersection.
|
||||
* @property {Pointers.ParabolaPointerRenderState[]|Object.<string, Pointers.ParabolaPointerRenderState>} [renderStates] A collection of different visual states to switch between.
|
||||
* When using {@link Pointers.createPointer}, a list of ParabolaPointerRenderStates.
|
||||
* When returned from {@link Pointers.getPointerProperties}, a map between render state names and ParabolaPointerRenderStates.
|
||||
* @property {Pointers.DefaultParabolaPointerRenderState[]|Object.<string, Pointers.DefaultParabolaPointerRenderState>} [defaultRenderStates] A collection of different visual states to use if there is no intersection.
|
||||
* When using {@link Pointers.createPointer}, a list of DefaultParabolaPointerRenderStates.
|
||||
* When returned from {@link Pointers.getPointerProperties}, a map between render state names and DefaultParabolaPointerRenderStates.
|
||||
* @property {boolean} [hover=false] If this Pointer should generate hover events.
|
||||
* @property {Pointers.Trigger[]} [triggers] A list of different triggers mechanisms that control this Pointer's click event generation.
|
||||
*/
|
||||
|
@ -375,4 +389,8 @@ QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const
|
|||
result = pickResult->toVariantMap();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantMap PointerScriptingInterface::getPointerProperties(unsigned int uid) const {
|
||||
return DependencyManager::get<PointerManager>()->getPointerProperties(uid);
|
||||
}
|
|
@ -203,6 +203,14 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get<PointerManager>()->isMouse(uid); }
|
||||
|
||||
/**jsdoc
|
||||
* Returns information about an existing Pointer
|
||||
* @function Pointers.getPointerState
|
||||
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
|
||||
* @returns {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties|Pointers.ParabolaPointerProperties} The information about the Pointer.
|
||||
* Currently only includes renderStates and defaultRenderStates with associated overlay IDs.
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getPointerProperties(unsigned int uid) const;
|
||||
};
|
||||
|
||||
#endif // hifi_PointerScriptingInterface_h
|
||||
|
|
|
@ -53,6 +53,18 @@ PickResultPointer RayPick::getHUDIntersection(const PickRay& pick) {
|
|||
return std::make_shared<RayPickResult>(IntersectionType::HUD, QUuid(), glm::distance(pick.origin, hudRes), hudRes, pick);
|
||||
}
|
||||
|
||||
Transform RayPick::getResultTransform() const {
|
||||
PickResultPointer result = getPrevPickResult();
|
||||
if (!result) {
|
||||
return Transform();
|
||||
}
|
||||
|
||||
auto rayResult = std::static_pointer_cast<RayPickResult>(result);
|
||||
Transform transform;
|
||||
transform.setTranslation(rayResult->intersection);
|
||||
return transform;
|
||||
}
|
||||
|
||||
glm::vec3 RayPick::intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat& rotation, const glm::vec3& registration) {
|
||||
// TODO: take into account registration
|
||||
glm::vec3 n = rotation * Vectors::FRONT;
|
||||
|
|
|
@ -77,6 +77,7 @@ public:
|
|||
PickResultPointer getOverlayIntersection(const PickRay& pick) override;
|
||||
PickResultPointer getAvatarIntersection(const PickRay& pick) override;
|
||||
PickResultPointer getHUDIntersection(const PickRay& pick) override;
|
||||
Transform getResultTransform() const override;
|
||||
|
||||
// These are helper functions for projecting and intersecting rays
|
||||
static glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction);
|
||||
|
|
|
@ -225,4 +225,16 @@ PickResultPointer StylusPick::getAvatarIntersection(const StylusTip& pick) {
|
|||
|
||||
PickResultPointer StylusPick::getHUDIntersection(const StylusTip& pick) {
|
||||
return std::make_shared<StylusPickResult>(pick.toVariantMap());
|
||||
}
|
||||
|
||||
Transform StylusPick::getResultTransform() const {
|
||||
PickResultPointer result = getPrevPickResult();
|
||||
if (!result) {
|
||||
return Transform();
|
||||
}
|
||||
|
||||
auto stylusResult = std::static_pointer_cast<StylusPickResult>(result);
|
||||
Transform transform;
|
||||
transform.setTranslation(stylusResult->intersection);
|
||||
return transform;
|
||||
}
|
|
@ -66,6 +66,7 @@ public:
|
|||
PickResultPointer getOverlayIntersection(const StylusTip& pick) override;
|
||||
PickResultPointer getAvatarIntersection(const StylusTip& pick) override;
|
||||
PickResultPointer getHUDIntersection(const StylusTip& pick) override;
|
||||
Transform getResultTransform() const override;
|
||||
|
||||
bool isLeftHand() const override { return _side == Side::Left; }
|
||||
bool isRightHand() const override { return _side == Side::Right; }
|
||||
|
|
|
@ -207,6 +207,10 @@ void StylusPointer::setRenderState(const std::string& state) {
|
|||
}
|
||||
}
|
||||
|
||||
QVariantMap StylusPointer::toVariantMap() const {
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
glm::vec3 StylusPointer::findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction) {
|
||||
switch (pickedObject.type) {
|
||||
case ENTITY:
|
||||
|
|
|
@ -33,6 +33,8 @@ public:
|
|||
void setRenderState(const std::string& state) override;
|
||||
void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override {}
|
||||
|
||||
QVariantMap toVariantMap() const override;
|
||||
|
||||
static OverlayID buildStylusOverlay(const QVariantMap& properties);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -55,53 +55,12 @@ void setupPreferences() {
|
|||
// Graphics quality
|
||||
static const QString GRAPHICS_QUALITY { "Graphics Quality" };
|
||||
{
|
||||
static const float MAX_DESKTOP_FPS = 60;
|
||||
static const float MAX_HMD_FPS = 90;
|
||||
static const float MIN_FPS = 10;
|
||||
static const float LOW = 0.25f;
|
||||
static const float MEDIUM = 0.5f;
|
||||
static const float HIGH = 0.75f;
|
||||
auto getter = []()->float {
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
bool inHMD = qApp->isHMDMode();
|
||||
|
||||
float increaseFPS = 0;
|
||||
if (inHMD) {
|
||||
increaseFPS = lodManager->getHMDLODDecreaseFPS();
|
||||
} else {
|
||||
increaseFPS = lodManager->getDesktopLODDecreaseFPS();
|
||||
}
|
||||
float maxFPS = inHMD ? MAX_HMD_FPS : MAX_DESKTOP_FPS;
|
||||
float percentage = increaseFPS / maxFPS;
|
||||
|
||||
if (percentage >= HIGH) {
|
||||
return LOW;
|
||||
} else if (percentage >= LOW) {
|
||||
return MEDIUM;
|
||||
}
|
||||
return HIGH;
|
||||
return DependencyManager::get<LODManager>()->getWorldDetailQuality();
|
||||
};
|
||||
|
||||
auto setter = [](float value) {
|
||||
static const float THRASHING_DIFFERENCE = 10;
|
||||
auto lodManager = DependencyManager::get<LODManager>();
|
||||
|
||||
bool isLowestValue = value == LOW;
|
||||
bool isHMDMode = qApp->isHMDMode();
|
||||
|
||||
float maxFPS = isHMDMode ? MAX_HMD_FPS : MAX_DESKTOP_FPS;
|
||||
float desiredFPS = maxFPS - THRASHING_DIFFERENCE;
|
||||
|
||||
if (!isLowestValue) {
|
||||
float calculatedFPS = (maxFPS - (maxFPS * value)) - THRASHING_DIFFERENCE;
|
||||
desiredFPS = calculatedFPS < MIN_FPS ? MIN_FPS : calculatedFPS;
|
||||
}
|
||||
|
||||
if (isHMDMode) {
|
||||
lodManager->setHMDLODDecreaseFPS(desiredFPS);
|
||||
} else {
|
||||
lodManager->setDesktopLODDecreaseFPS(desiredFPS);
|
||||
}
|
||||
DependencyManager::get<LODManager>()->setWorldDetailQuality(value);
|
||||
};
|
||||
|
||||
auto wodSlider = new SliderPreference(GRAPHICS_QUALITY, "World Detail", getter, setter);
|
||||
|
|
|
@ -374,27 +374,35 @@ void Stats::updateStats(bool force) {
|
|||
STAT_UPDATE(engineFrameTime, (float) config->getCPURunTime());
|
||||
STAT_UPDATE(avatarSimulationTime, (float)avatarManager->getAvatarSimulationTime());
|
||||
|
||||
STAT_UPDATE(gpuBuffers, (int)gpu::Context::getBufferGPUCount());
|
||||
STAT_UPDATE(gpuBufferMemory, (int)BYTES_TO_MB(gpu::Context::getBufferGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextures, (int)gpu::Context::getTextureGPUCount());
|
||||
if (_expanded) {
|
||||
STAT_UPDATE(gpuBuffers, (int)gpu::Context::getBufferGPUCount());
|
||||
STAT_UPDATE(gpuBufferMemory, (int)BYTES_TO_MB(gpu::Context::getBufferGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextures, (int)gpu::Context::getTextureGPUCount());
|
||||
|
||||
STAT_UPDATE(glContextSwapchainMemory, (int)BYTES_TO_MB(gl::Context::getSwapchainMemoryUsage()));
|
||||
STAT_UPDATE(glContextSwapchainMemory, (int)BYTES_TO_MB(gl::Context::getSwapchainMemoryUsage()));
|
||||
|
||||
STAT_UPDATE(qmlTextureMemory, (int)BYTES_TO_MB(OffscreenQmlSurface::getUsedTextureMemory()));
|
||||
STAT_UPDATE(texturePendingTransfers, (int)BYTES_TO_MB(gpu::Context::getTexturePendingGPUTransferMemSize()));
|
||||
STAT_UPDATE(gpuTextureMemory, (int)BYTES_TO_MB(gpu::Context::getTextureGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureResidentMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResidentGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureFramebufferMemory, (int)BYTES_TO_MB(gpu::Context::getTextureFramebufferGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureResourceMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourceGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureResourceIdealMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourceIdealGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureResourcePopulatedMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourcePopulatedGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureExternalMemory, (int)BYTES_TO_MB(gpu::Context::getTextureExternalGPUMemSize()));
|
||||
STAT_UPDATE(qmlTextureMemory, (int)BYTES_TO_MB(OffscreenQmlSurface::getUsedTextureMemory()));
|
||||
STAT_UPDATE(texturePendingTransfers, (int)BYTES_TO_MB(gpu::Context::getTexturePendingGPUTransferMemSize()));
|
||||
STAT_UPDATE(gpuTextureMemory, (int)BYTES_TO_MB(gpu::Context::getTextureGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureResidentMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResidentGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureFramebufferMemory, (int)BYTES_TO_MB(gpu::Context::getTextureFramebufferGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureResourceMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourceGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureResourceIdealMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourceIdealGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureResourcePopulatedMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourcePopulatedGPUMemSize()));
|
||||
STAT_UPDATE(gpuTextureExternalMemory, (int)BYTES_TO_MB(gpu::Context::getTextureExternalGPUMemSize()));
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
STAT_UPDATE(gpuTextureMemoryPressureState, getTextureMemoryPressureModeString());
|
||||
STAT_UPDATE(gpuTextureMemoryPressureState, getTextureMemoryPressureModeString());
|
||||
#endif
|
||||
STAT_UPDATE(gpuFreeMemory, (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemSize()));
|
||||
STAT_UPDATE(rectifiedTextureCount, (int)RECTIFIED_TEXTURE_COUNT.load());
|
||||
STAT_UPDATE(decimatedTextureCount, (int)DECIMATED_TEXTURE_COUNT.load());
|
||||
STAT_UPDATE(gpuFreeMemory, (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemSize()));
|
||||
STAT_UPDATE(rectifiedTextureCount, (int)RECTIFIED_TEXTURE_COUNT.load());
|
||||
STAT_UPDATE(decimatedTextureCount, (int)DECIMATED_TEXTURE_COUNT.load());
|
||||
}
|
||||
|
||||
gpu::ContextStats gpuFrameStats;
|
||||
gpuContext->getFrameStats(gpuFrameStats);
|
||||
|
||||
STAT_UPDATE(drawcalls, gpuFrameStats._DSNumDrawcalls);
|
||||
|
||||
|
||||
// Incoming packets
|
||||
QLocale locale(QLocale::English);
|
||||
|
|
|
@ -93,7 +93,6 @@ private: \
|
|||
* @property {number} processing - <em>Read-only.</em>
|
||||
* @property {number} processingPending - <em>Read-only.</em>
|
||||
* @property {number} triangles - <em>Read-only.</em>
|
||||
* @property {number} quads - <em>Read-only.</em>
|
||||
* @property {number} materialSwitches - <em>Read-only.</em>
|
||||
* @property {number} itemConsidered - <em>Read-only.</em>
|
||||
* @property {number} itemOutOfView - <em>Read-only.</em>
|
||||
|
@ -249,7 +248,7 @@ class Stats : public QQuickItem {
|
|||
STATS_PROPERTY(int, processing, 0)
|
||||
STATS_PROPERTY(int, processingPending, 0)
|
||||
STATS_PROPERTY(int, triangles, 0)
|
||||
STATS_PROPERTY(int, quads, 0)
|
||||
STATS_PROPERTY(int, drawcalls, 0)
|
||||
STATS_PROPERTY(int, materialSwitches, 0)
|
||||
STATS_PROPERTY(int, itemConsidered, 0)
|
||||
STATS_PROPERTY(int, itemOutOfView, 0)
|
||||
|
@ -735,11 +734,12 @@ signals:
|
|||
void trianglesChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the value of the <code>quads</code> property changes.
|
||||
* @function Stats.quadsChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void quadsChanged();
|
||||
* Triggered when the value of the <code>drawcalls</code> property changes.
|
||||
* This
|
||||
* @function Stats.drawcallsChanged
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void drawcallsChanged();
|
||||
|
||||
/**jsdoc
|
||||
* Triggered when the value of the <code>materialSwitches</code> property changes.
|
||||
|
|
|
@ -926,7 +926,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
|
||||
// compute blend based on velocity
|
||||
const float JUMP_SPEED = 3.5f;
|
||||
float alpha = glm::clamp(-_lastVelocity.y / JUMP_SPEED, -1.0f, 1.0f) + 1.0f;
|
||||
float alpha = glm::clamp(-workingVelocity.y / JUMP_SPEED, -1.0f, 1.0f) + 1.0f;
|
||||
_animVars.set("inAirAlpha", alpha);
|
||||
}
|
||||
|
||||
|
|
|
@ -240,7 +240,7 @@ static void FIR_1x4_SSE(float* src, float* dst0, float* dst1, float* dst2, float
|
|||
|
||||
float* ps = &src[i - HRTF_TAPS + 1]; // process forwards
|
||||
|
||||
assert(HRTF_TAPS % 4 == 0);
|
||||
static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4");
|
||||
|
||||
for (int k = 0; k < HRTF_TAPS; k += 4) {
|
||||
|
||||
|
@ -276,23 +276,8 @@ static void FIR_1x4_SSE(float* src, float* dst0, float* dst1, float* dst2, float
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Runtime CPU dispatch
|
||||
//
|
||||
|
||||
#include "CPUDetect.h"
|
||||
|
||||
void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
|
||||
void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
|
||||
|
||||
static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) {
|
||||
|
||||
static auto f = cpuSupportsAVX512() ? FIR_1x4_AVX512 : (cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE);
|
||||
(*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch
|
||||
}
|
||||
|
||||
// 4 channel planar to interleaved
|
||||
static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) {
|
||||
static void interleave_4x4_SSE(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) {
|
||||
|
||||
assert(numFrames % 4 == 0);
|
||||
|
||||
|
@ -323,7 +308,7 @@ static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, f
|
|||
|
||||
// process 2 cascaded biquads on 4 channels (interleaved)
|
||||
// biquads computed in parallel, by adding one sample of delay
|
||||
static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) {
|
||||
static void biquad2_4x4_SSE(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) {
|
||||
|
||||
// enable flush-to-zero mode to prevent denormals
|
||||
unsigned int ftz = _MM_GET_FLUSH_ZERO_MODE();
|
||||
|
@ -388,7 +373,7 @@ static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3]
|
|||
}
|
||||
|
||||
// crossfade 4 inputs into 2 outputs with accumulation (interleaved)
|
||||
static void crossfade_4x2(float* src, float* dst, const float* win, int numFrames) {
|
||||
static void crossfade_4x2_SSE(float* src, float* dst, const float* win, int numFrames) {
|
||||
|
||||
assert(numFrames % 4 == 0);
|
||||
|
||||
|
@ -435,12 +420,12 @@ static void crossfade_4x2(float* src, float* dst, const float* win, int numFrame
|
|||
}
|
||||
|
||||
// linear interpolation with gain
|
||||
static void interpolate(float* dst, const float* src0, const float* src1, float frac, float gain) {
|
||||
static void interpolate_SSE(const float* src0, const float* src1, float* dst, float frac, float gain) {
|
||||
|
||||
__m128 f0 = _mm_set1_ps(gain * (1.0f - frac));
|
||||
__m128 f1 = _mm_set1_ps(gain * frac);
|
||||
|
||||
assert(HRTF_TAPS % 4 == 0);
|
||||
static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4");
|
||||
|
||||
for (int k = 0; k < HRTF_TAPS; k += 4) {
|
||||
|
||||
|
@ -453,6 +438,44 @@ static void interpolate(float* dst, const float* src0, const float* src1, float
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Runtime CPU dispatch
|
||||
//
|
||||
|
||||
#include "CPUDetect.h"
|
||||
|
||||
void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
|
||||
void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
|
||||
void interleave_4x4_AVX2(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames);
|
||||
void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames);
|
||||
void crossfade_4x2_AVX2(float* src, float* dst, const float* win, int numFrames);
|
||||
void interpolate_AVX2(const float* src0, const float* src1, float* dst, float frac, float gain);
|
||||
|
||||
static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) {
|
||||
static auto f = cpuSupportsAVX512() ? FIR_1x4_AVX512 : (cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE);
|
||||
(*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch
|
||||
}
|
||||
|
||||
static void interleave_4x4(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) {
|
||||
static auto f = cpuSupportsAVX2() ? interleave_4x4_AVX2 : interleave_4x4_SSE;
|
||||
(*f)(src0, src1, src2, src3, dst, numFrames); // dispatch
|
||||
}
|
||||
|
||||
static void biquad2_4x4(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) {
|
||||
static auto f = cpuSupportsAVX2() ? biquad2_4x4_AVX2 : biquad2_4x4_SSE;
|
||||
(*f)(src, dst, coef, state, numFrames); // dispatch
|
||||
}
|
||||
|
||||
static void crossfade_4x2(float* src, float* dst, const float* win, int numFrames) {
|
||||
static auto f = cpuSupportsAVX2() ? crossfade_4x2_AVX2 : crossfade_4x2_SSE;
|
||||
(*f)(src, dst, win, numFrames); // dispatch
|
||||
}
|
||||
|
||||
static void interpolate(const float* src0, const float* src1, float* dst, float frac, float gain) {
|
||||
static auto f = cpuSupportsAVX2() ? interpolate_AVX2 : interpolate_SSE;
|
||||
(*f)(src0, src1, dst, frac, gain); // dispatch
|
||||
}
|
||||
|
||||
#else // portable reference code
|
||||
|
||||
// 1 channel input, 4 channel output
|
||||
|
@ -489,7 +512,7 @@ static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* ds
|
|||
|
||||
float* ps = &src[i - HRTF_TAPS + 1]; // process forwards
|
||||
|
||||
assert(HRTF_TAPS % 4 == 0);
|
||||
static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4");
|
||||
|
||||
for (int k = 0; k < HRTF_TAPS; k += 4) {
|
||||
|
||||
|
@ -715,7 +738,7 @@ static void crossfade_4x2(float* src, float* dst, const float* win, int numFrame
|
|||
}
|
||||
|
||||
// linear interpolation with gain
|
||||
static void interpolate(float* dst, const float* src0, const float* src1, float frac, float gain) {
|
||||
static void interpolate(const float* src0, const float* src1, float* dst, float frac, float gain) {
|
||||
|
||||
float f0 = gain * (1.0f - frac);
|
||||
float f1 = gain * frac;
|
||||
|
@ -967,8 +990,8 @@ static void setFilters(float firCoef[4][HRTF_TAPS], float bqCoef[5][8], int dela
|
|||
azimuthToIndex(azimuth, az0, az1, frac);
|
||||
|
||||
// interpolate FIR
|
||||
interpolate(firCoef[channel+0], ir_table_table[index][azL0][0], ir_table_table[index][azL1][0], fracL, gain * gainL);
|
||||
interpolate(firCoef[channel+1], ir_table_table[index][azR0][1], ir_table_table[index][azR1][1], fracR, gain * gainR);
|
||||
interpolate(ir_table_table[index][azL0][0], ir_table_table[index][azL1][0], firCoef[channel+0], fracL, gain * gainL);
|
||||
interpolate(ir_table_table[index][azR0][1], ir_table_table[index][azR1][1], firCoef[channel+1], fracR, gain * gainR);
|
||||
|
||||
// interpolate ITD
|
||||
float itd = (1.0f - frac) * itd_table_table[index][az0] + frac * itd_table_table[index][az1];
|
||||
|
|
|
@ -44,7 +44,7 @@ void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3
|
|||
|
||||
float* ps = &src[i - HRTF_TAPS + 1]; // process forwards
|
||||
|
||||
assert(HRTF_TAPS % 4 == 0);
|
||||
static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4");
|
||||
|
||||
for (int k = 0; k < HRTF_TAPS; k += 4) {
|
||||
|
||||
|
@ -87,4 +87,165 @@ void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3
|
|||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
// 4 channel planar to interleaved
|
||||
void interleave_4x4_AVX2(float* src0, float* src1, float* src2, float* src3, float* dst, int numFrames) {
|
||||
|
||||
assert(numFrames % 8 == 0);
|
||||
|
||||
for (int i = 0; i < numFrames; i += 8) {
|
||||
|
||||
__m256 x0 = _mm256_loadu_ps(&src0[i]);
|
||||
__m256 x1 = _mm256_loadu_ps(&src1[i]);
|
||||
__m256 x2 = _mm256_loadu_ps(&src2[i]);
|
||||
__m256 x3 = _mm256_loadu_ps(&src3[i]);
|
||||
|
||||
// interleave (4x4 matrix transpose)
|
||||
__m256 t0 = _mm256_unpacklo_ps(x0, x1);
|
||||
__m256 t1 = _mm256_unpackhi_ps(x0, x1);
|
||||
__m256 t2 = _mm256_unpacklo_ps(x2, x3);
|
||||
__m256 t3 = _mm256_unpackhi_ps(x2, x3);
|
||||
|
||||
x0 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(1,0,1,0));
|
||||
x1 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(3,2,3,2));
|
||||
x2 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(1,0,1,0));
|
||||
x3 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(3,2,3,2));
|
||||
|
||||
t0 = _mm256_permute2f128_ps(x0, x1, 0x20);
|
||||
t1 = _mm256_permute2f128_ps(x2, x3, 0x20);
|
||||
t2 = _mm256_permute2f128_ps(x0, x1, 0x31);
|
||||
t3 = _mm256_permute2f128_ps(x2, x3, 0x31);
|
||||
|
||||
_mm256_storeu_ps(&dst[4*i+0], t0);
|
||||
_mm256_storeu_ps(&dst[4*i+8], t1);
|
||||
_mm256_storeu_ps(&dst[4*i+16], t2);
|
||||
_mm256_storeu_ps(&dst[4*i+24], t3);
|
||||
}
|
||||
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
// process 2 cascaded biquads on 4 channels (interleaved)
|
||||
// biquads are computed in parallel, by adding one sample of delay
|
||||
void biquad2_4x4_AVX2(float* src, float* dst, float coef[5][8], float state[3][8], int numFrames) {
|
||||
|
||||
// enable flush-to-zero mode to prevent denormals
|
||||
unsigned int ftz = _MM_GET_FLUSH_ZERO_MODE();
|
||||
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
|
||||
|
||||
// restore state
|
||||
__m256 x0 = _mm256_setzero_ps();
|
||||
__m256 y0 = _mm256_loadu_ps(state[0]);
|
||||
__m256 w1 = _mm256_loadu_ps(state[1]);
|
||||
__m256 w2 = _mm256_loadu_ps(state[2]);
|
||||
|
||||
// biquad coefs
|
||||
__m256 b0 = _mm256_loadu_ps(coef[0]);
|
||||
__m256 b1 = _mm256_loadu_ps(coef[1]);
|
||||
__m256 b2 = _mm256_loadu_ps(coef[2]);
|
||||
__m256 a1 = _mm256_loadu_ps(coef[3]);
|
||||
__m256 a2 = _mm256_loadu_ps(coef[4]);
|
||||
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
|
||||
// x0 = (first biquad output << 128) | input
|
||||
x0 = _mm256_insertf128_ps(_mm256_permute2f128_ps(y0, y0, 0x01), _mm_loadu_ps(&src[4*i]), 0);
|
||||
|
||||
// transposed Direct Form II
|
||||
y0 = _mm256_fmadd_ps(x0, b0, w1);
|
||||
w1 = _mm256_fmadd_ps(x0, b1, w2);
|
||||
w2 = _mm256_mul_ps(x0, b2);
|
||||
w1 = _mm256_fnmadd_ps(y0, a1, w1);
|
||||
w2 = _mm256_fnmadd_ps(y0, a2, w2);
|
||||
|
||||
_mm_storeu_ps(&dst[4*i], _mm256_extractf128_ps(y0, 1)); // second biquad output
|
||||
}
|
||||
|
||||
// save state
|
||||
_mm256_storeu_ps(state[0], y0);
|
||||
_mm256_storeu_ps(state[1], w1);
|
||||
_mm256_storeu_ps(state[2], w2);
|
||||
|
||||
_MM_SET_FLUSH_ZERO_MODE(ftz);
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
// crossfade 4 inputs into 2 outputs with accumulation (interleaved)
|
||||
void crossfade_4x2_AVX2(float* src, float* dst, const float* win, int numFrames) {
|
||||
|
||||
assert(numFrames % 8 == 0);
|
||||
|
||||
for (int i = 0; i < numFrames; i += 8) {
|
||||
|
||||
__m256 f0 = _mm256_loadu_ps(&win[i]);
|
||||
|
||||
__m256 x0 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+0]));
|
||||
__m256 x1 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+4]));
|
||||
__m256 x2 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+8]));
|
||||
__m256 x3 = _mm256_castps128_ps256(_mm_loadu_ps(&src[4*i+12]));
|
||||
|
||||
x0 = _mm256_insertf128_ps(x0, _mm_loadu_ps(&src[4*i+16]), 1);
|
||||
x1 = _mm256_insertf128_ps(x1, _mm_loadu_ps(&src[4*i+20]), 1);
|
||||
x2 = _mm256_insertf128_ps(x2, _mm_loadu_ps(&src[4*i+24]), 1);
|
||||
x3 = _mm256_insertf128_ps(x3, _mm_loadu_ps(&src[4*i+28]), 1);
|
||||
|
||||
__m256 y0 = _mm256_loadu_ps(&dst[2*i+0]);
|
||||
__m256 y1 = _mm256_loadu_ps(&dst[2*i+8]);
|
||||
|
||||
// deinterleave (4x4 matrix transpose)
|
||||
__m256 t0 = _mm256_unpacklo_ps(x0, x1);
|
||||
__m256 t1 = _mm256_unpackhi_ps(x0, x1);
|
||||
__m256 t2 = _mm256_unpacklo_ps(x2, x3);
|
||||
__m256 t3 = _mm256_unpackhi_ps(x2, x3);
|
||||
|
||||
x0 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(1,0,1,0));
|
||||
x1 = _mm256_shuffle_ps(t0, t2, _MM_SHUFFLE(3,2,3,2));
|
||||
x2 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(1,0,1,0));
|
||||
x3 = _mm256_shuffle_ps(t1, t3, _MM_SHUFFLE(3,2,3,2));
|
||||
|
||||
// crossfade
|
||||
x0 = _mm256_sub_ps(x0, x2);
|
||||
x1 = _mm256_sub_ps(x1, x3);
|
||||
x2 = _mm256_fmadd_ps(f0, x0, x2);
|
||||
x3 = _mm256_fmadd_ps(f0, x1, x3);
|
||||
|
||||
// interleave
|
||||
t0 = _mm256_unpacklo_ps(x2, x3);
|
||||
t1 = _mm256_unpackhi_ps(x2, x3);
|
||||
|
||||
x0 = _mm256_permute2f128_ps(t0, t1, 0x20);
|
||||
x1 = _mm256_permute2f128_ps(t0, t1, 0x31);
|
||||
|
||||
// accumulate
|
||||
y0 = _mm256_add_ps(y0, x0);
|
||||
y1 = _mm256_add_ps(y1, x1);
|
||||
|
||||
_mm256_storeu_ps(&dst[2*i+0], y0);
|
||||
_mm256_storeu_ps(&dst[2*i+8], y1);
|
||||
}
|
||||
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
// linear interpolation with gain
|
||||
void interpolate_AVX2(const float* src0, const float* src1, float* dst, float frac, float gain) {
|
||||
|
||||
__m256 f0 = _mm256_set1_ps(gain * (1.0f - frac));
|
||||
__m256 f1 = _mm256_set1_ps(gain * frac);
|
||||
|
||||
static_assert(HRTF_TAPS % 8 == 0, "HRTF_TAPS must be a multiple of 8");
|
||||
|
||||
for (int k = 0; k < HRTF_TAPS; k += 8) {
|
||||
|
||||
__m256 x0 = _mm256_loadu_ps(&src0[k]);
|
||||
__m256 x1 = _mm256_loadu_ps(&src1[k]);
|
||||
|
||||
x0 = _mm256_mul_ps(f0, x0);
|
||||
x0 = _mm256_fmadd_ps(f1, x1, x0);
|
||||
|
||||
_mm256_storeu_ps(&dst[k], x0);
|
||||
}
|
||||
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -44,7 +44,7 @@ void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* ds
|
|||
|
||||
float* ps = &src[i - HRTF_TAPS + 1]; // process forwards
|
||||
|
||||
assert(HRTF_TAPS % 4 == 0);
|
||||
static_assert(HRTF_TAPS % 4 == 0, "HRTF_TAPS must be a multiple of 4");
|
||||
|
||||
for (int k = 0; k < HRTF_TAPS; k += 4) {
|
||||
|
||||
|
|
|
@ -139,7 +139,6 @@ Avatar::~Avatar() {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
geometryCache->releaseID(_nameRectGeometryID);
|
||||
|
|
|
@ -1861,7 +1861,9 @@ qint64 AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice
|
|||
}
|
||||
|
||||
qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID,
|
||||
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) {
|
||||
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion,
|
||||
AvatarTraits::TraitInstanceID wireInstanceID) {
|
||||
|
||||
qint64 bytesWritten = 0;
|
||||
|
||||
bytesWritten += destination.writePrimitive(traitType);
|
||||
|
@ -1870,7 +1872,11 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr
|
|||
bytesWritten += destination.writePrimitive(traitVersion);
|
||||
}
|
||||
|
||||
bytesWritten += destination.write(traitInstanceID.toRfc4122());
|
||||
if (!wireInstanceID.isNull()) {
|
||||
bytesWritten += destination.write(wireInstanceID.toRfc4122());
|
||||
} else {
|
||||
bytesWritten += destination.write(traitInstanceID.toRfc4122());
|
||||
}
|
||||
|
||||
if (traitType == AvatarTraits::AvatarEntity) {
|
||||
// grab a read lock on the avatar entities and check for entity data for the given ID
|
||||
|
@ -1895,6 +1901,16 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr
|
|||
return bytesWritten;
|
||||
}
|
||||
|
||||
void AvatarData::prepareResetTraitInstances() {
|
||||
if (_clientTraitsHandler) {
|
||||
_avatarEntitiesLock.withReadLock([this]{
|
||||
foreach (auto entityID, _avatarEntityData.keys()) {
|
||||
_clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) {
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
// get the URL from the binary data
|
||||
|
@ -2792,7 +2808,7 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) {
|
|||
if (_clientTraitsHandler) {
|
||||
// if we have a client traits handler, flag any updated or created entities
|
||||
// so that we send changes for them next frame
|
||||
foreach (auto entityID, _avatarEntityData) {
|
||||
foreach (auto entityID, _avatarEntityData.keys()) {
|
||||
_clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -962,7 +962,10 @@ public:
|
|||
qint64 packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination,
|
||||
AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION);
|
||||
qint64 packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID,
|
||||
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION);
|
||||
ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION,
|
||||
AvatarTraits::TraitInstanceID wireInstanceID = AvatarTraits::TraitInstanceID());
|
||||
|
||||
void prepareResetTraitInstances();
|
||||
|
||||
void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData);
|
||||
void processTraitInstance(AvatarTraits::TraitType traitType,
|
||||
|
@ -1190,6 +1193,9 @@ public:
|
|||
void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; }
|
||||
int getReplicaIndex() { return _replicaIndex; }
|
||||
|
||||
const AvatarTraits::TraitInstanceID getTraitInstanceXORID() const { return _traitInstanceXORID; }
|
||||
void cycleTraitInstanceXORID() { _traitInstanceXORID = QUuid::createUuid(); }
|
||||
|
||||
signals:
|
||||
|
||||
/**jsdoc
|
||||
|
@ -1496,6 +1502,8 @@ private:
|
|||
// privatize the copy constructor and assignment operator so they cannot be called
|
||||
AvatarData(const AvatarData&);
|
||||
AvatarData& operator= (const AvatarData&);
|
||||
|
||||
AvatarTraits::TraitInstanceID _traitInstanceXORID { QUuid::createUuid() };
|
||||
};
|
||||
Q_DECLARE_METATYPE(AvatarData*)
|
||||
|
||||
|
|
|
@ -85,7 +85,8 @@ void AvatarReplicas::processDeletedTraitInstance(const QUuid& parentID, AvatarTr
|
|||
if (_replicasMap.find(parentID) != _replicasMap.end()) {
|
||||
auto &replicas = _replicasMap[parentID];
|
||||
for (auto avatar : replicas) {
|
||||
avatar->processDeletedTraitInstance(traitType, instanceID);
|
||||
avatar->processDeletedTraitInstance(traitType,
|
||||
AvatarTraits::xoredInstanceID(instanceID, avatar->getTraitInstanceXORID()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +95,9 @@ void AvatarReplicas::processTraitInstance(const QUuid& parentID, AvatarTraits::T
|
|||
if (_replicasMap.find(parentID) != _replicasMap.end()) {
|
||||
auto &replicas = _replicasMap[parentID];
|
||||
for (auto avatar : replicas) {
|
||||
avatar->processTraitInstance(traitType, instanceID, traitBinaryData);
|
||||
avatar->processTraitInstance(traitType,
|
||||
AvatarTraits::xoredInstanceID(instanceID, avatar->getTraitInstanceXORID()),
|
||||
traitBinaryData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -335,16 +338,28 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
|
|||
AvatarTraits::TraitInstanceID traitInstanceID =
|
||||
QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||
|
||||
// XOR the incoming trait instance ID with this avatar object's personal XOR ID
|
||||
|
||||
// this ensures that we have separate entity instances in the local tree
|
||||
// if we briefly end up with two Avatar objects for this node
|
||||
|
||||
// (which can occur if the shared pointer for the
|
||||
// previous instance of an avatar hasn't yet gone out of scope before the
|
||||
// new instance is created)
|
||||
|
||||
auto xoredInstanceID = AvatarTraits::xoredInstanceID(traitInstanceID, avatar->getTraitInstanceXORID());
|
||||
|
||||
message->readPrimitive(&traitBinarySize);
|
||||
|
||||
auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID);
|
||||
if (packetTraitVersion > processedInstanceVersion) {
|
||||
// in order to handle re-connections to the avatar mixer when the other
|
||||
if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) {
|
||||
avatar->processDeletedTraitInstance(traitType, traitInstanceID);
|
||||
avatar->processDeletedTraitInstance(traitType, xoredInstanceID);
|
||||
_replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID);
|
||||
} else {
|
||||
auto traitData = message->read(traitBinarySize);
|
||||
avatar->processTraitInstance(traitType, traitInstanceID, traitData);
|
||||
avatar->processTraitInstance(traitType, xoredInstanceID, traitData);
|
||||
_replicas.processTraitInstance(avatarID, traitType, traitInstanceID, traitData);
|
||||
}
|
||||
processedInstanceVersion = packetTraitVersion;
|
||||
|
|
|
@ -41,7 +41,8 @@ namespace AvatarTraits {
|
|||
const TraitWireSize DELETED_TRAIT_SIZE = -1;
|
||||
|
||||
inline qint64 packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination,
|
||||
TraitVersion traitVersion = NULL_TRAIT_VERSION) {
|
||||
TraitVersion traitVersion = NULL_TRAIT_VERSION,
|
||||
TraitInstanceID xoredInstanceID = TraitInstanceID()) {
|
||||
qint64 bytesWritten = 0;
|
||||
|
||||
bytesWritten += destination.writePrimitive(traitType);
|
||||
|
@ -50,12 +51,28 @@ namespace AvatarTraits {
|
|||
bytesWritten += destination.writePrimitive(traitVersion);
|
||||
}
|
||||
|
||||
bytesWritten += destination.write(instanceID.toRfc4122());
|
||||
if (xoredInstanceID.isNull()) {
|
||||
bytesWritten += destination.write(instanceID.toRfc4122());
|
||||
} else {
|
||||
bytesWritten += destination.write(xoredInstanceID.toRfc4122());
|
||||
}
|
||||
|
||||
bytesWritten += destination.writePrimitive(DELETED_TRAIT_SIZE);
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
inline TraitInstanceID xoredInstanceID(TraitInstanceID localInstanceID, TraitInstanceID xorKeyID) {
|
||||
QByteArray xoredInstanceID { NUM_BYTES_RFC4122_UUID, 0 };
|
||||
auto xorKeyIDBytes = xorKeyID.toRfc4122();
|
||||
auto localInstanceIDBytes = localInstanceID.toRfc4122();
|
||||
|
||||
for (auto i = 0; i < localInstanceIDBytes.size(); ++i) {
|
||||
xoredInstanceID[i] = localInstanceIDBytes[i] ^ xorKeyIDBytes[i];
|
||||
}
|
||||
|
||||
return QUuid::fromRfc4122(xoredInstanceID);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarTraits_h
|
||||
|
|
|
@ -37,6 +37,15 @@ void ClientTraitsHandler::resetForNewMixer() {
|
|||
|
||||
// mark that all traits should be sent next time
|
||||
_shouldPerformInitialSend = true;
|
||||
|
||||
// reset the trait statuses
|
||||
_traitStatuses.reset();
|
||||
|
||||
// pre-fill the instanced statuses that we will need to send next frame
|
||||
_owningAvatar->prepareResetTraitInstances();
|
||||
|
||||
// reset the trait XOR ID since we're resetting for a new avatar mixer
|
||||
_owningAvatar->cycleTraitInstanceXORID();
|
||||
}
|
||||
|
||||
void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
||||
|
@ -87,11 +96,19 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
|||
|| instanceIDValuePair.value == Updated) {
|
||||
// this is a changed trait we need to send or we haven't send out trait information yet
|
||||
// ask the owning avatar to pack it
|
||||
_owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList);
|
||||
|
||||
// since this is going to the mixer, use the XORed instance ID (to anonymize trait instance IDs
|
||||
// that would typically persist across sessions)
|
||||
_owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList,
|
||||
AvatarTraits::NULL_TRAIT_VERSION,
|
||||
AvatarTraits::xoredInstanceID(instanceIDValuePair.id,
|
||||
_owningAvatar->getTraitInstanceXORID()));
|
||||
} else if (!_shouldPerformInitialSend && instanceIDValuePair.value == Deleted) {
|
||||
// pack delete for this trait instance
|
||||
AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id,
|
||||
*traitsPacketList);
|
||||
*traitsPacketList, AvatarTraits::NULL_TRAIT_VERSION,
|
||||
AvatarTraits::xoredInstanceID(instanceIDValuePair.id,
|
||||
_owningAvatar->getTraitInstanceXORID()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ public:
|
|||
|
||||
void markTraitUpdated(AvatarTraits::TraitType updatedTrait)
|
||||
{ _traitStatuses[updatedTrait] = Updated; _hasChangedTraits = true; }
|
||||
void markInstancedTraitUpdated(AvatarTraits::TraitType traitType, QUuid updatedInstanceID)
|
||||
void markInstancedTraitUpdated(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID updatedInstanceID)
|
||||
{ _traitStatuses.instanceInsert(traitType, updatedInstanceID, Updated); _hasChangedTraits = true; }
|
||||
void markInstancedTraitDeleted(AvatarTraits::TraitType traitType, QUuid deleteInstanceID)
|
||||
void markInstancedTraitDeleted(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID deleteInstanceID)
|
||||
{ _traitStatuses.instanceInsert(traitType, deleteInstanceID, Deleted); _hasChangedTraits = true; }
|
||||
|
||||
void resetForNewMixer();
|
||||
|
|
|
@ -19,16 +19,16 @@ struct InputCalibrationData {
|
|||
glm::mat4 sensorToWorldMat; // sensor to world
|
||||
glm::mat4 avatarMat; // avatar to world
|
||||
glm::mat4 hmdSensorMat; // hmd pos and orientation in sensor space
|
||||
glm::mat4 defaultCenterEyeMat; // default pose for the center of the eyes in avatar space.
|
||||
glm::mat4 defaultHeadMat; // default pose for head joint in avatar space
|
||||
glm::mat4 defaultSpine2; // default pose for spine2 joint in avatar space
|
||||
glm::mat4 defaultHips; // default pose for hips joint in avatar space
|
||||
glm::mat4 defaultLeftFoot; // default pose for leftFoot joint in avatar space
|
||||
glm::mat4 defaultRightFoot; // default pose for rightFoot joint in avatar space
|
||||
glm::mat4 defaultRightArm; // default pose for rightArm joint in avatar space
|
||||
glm::mat4 defaultLeftArm; // default pose for leftArm joint in avatar space
|
||||
glm::mat4 defaultRightHand; // default pose for rightHand joint in avatar space
|
||||
glm::mat4 defaultLeftHand; // default pose for leftHand joint in avatar space
|
||||
glm::mat4 defaultCenterEyeMat; // default pose for the center of the eyes in sensor space.
|
||||
glm::mat4 defaultHeadMat; // default pose for head joint in sensor space
|
||||
glm::mat4 defaultSpine2; // default pose for spine2 joint in sensor space
|
||||
glm::mat4 defaultHips; // default pose for hips joint in sensor space
|
||||
glm::mat4 defaultLeftFoot; // default pose for leftFoot joint in sensor space
|
||||
glm::mat4 defaultRightFoot; // default pose for rightFoot joint in sensor space
|
||||
glm::mat4 defaultRightArm; // default pose for rightArm joint in sensor space
|
||||
glm::mat4 defaultLeftArm; // default pose for leftArm joint in sensor space
|
||||
glm::mat4 defaultRightHand; // default pose for rightHand joint in sensor space
|
||||
glm::mat4 defaultLeftHand; // default pose for leftHand joint in sensor space
|
||||
};
|
||||
|
||||
enum class ChannelType {
|
||||
|
|
|
@ -527,8 +527,8 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) {
|
|||
}
|
||||
|
||||
// If the source hasn't been written yet, defer processing of this route
|
||||
auto source = route->source;
|
||||
auto sourceInput = source->getInput();
|
||||
auto& source = route->source;
|
||||
auto& sourceInput = source->getInput();
|
||||
if (sourceInput.device == STANDARD_DEVICE && !force && source->writeable()) {
|
||||
if (debugRoutes && route->debug) {
|
||||
qCDebug(controllers) << "Source not yet written, deferring";
|
||||
|
@ -559,7 +559,7 @@ bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) {
|
|||
return true;
|
||||
}
|
||||
|
||||
auto destination = route->destination;
|
||||
auto& destination = route->destination;
|
||||
// THis could happen if the route destination failed to create
|
||||
// FIXME: Maybe do not create the route if the destination failed and avoid this case ?
|
||||
if (!destination) {
|
||||
|
|
|
@ -382,6 +382,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
|
||||
const auto& views = _viewState->getConicalViews();
|
||||
PrioritySortUtil::PriorityQueue<SortableRenderer> sortedRenderables(views);
|
||||
sortedRenderables.reserve(_renderablesToUpdate.size());
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
|
||||
std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr = _renderablesToUpdate.begin();
|
||||
|
@ -405,11 +406,14 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
|
||||
// process the sorted renderables
|
||||
size_t numSorted = sortedRenderables.size();
|
||||
while (!sortedRenderables.empty() && usecTimestampNow() < expiry) {
|
||||
const auto renderable = sortedRenderables.top().getRenderer();
|
||||
const auto& sortedRenderablesVector = sortedRenderables.getSortedVector();
|
||||
for (const auto& sortedRenderable : sortedRenderablesVector) {
|
||||
if (usecTimestampNow() > expiry) {
|
||||
break;
|
||||
}
|
||||
const auto& renderable = sortedRenderable.getRenderer();
|
||||
renderable->updateInScene(scene, transaction);
|
||||
_renderablesToUpdate.erase(renderable->getEntity()->getID());
|
||||
sortedRenderables.pop();
|
||||
}
|
||||
|
||||
// compute average per-renderable update cost
|
||||
|
|
|
@ -263,15 +263,19 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
|||
default:
|
||||
materials[matName] = currentMaterial;
|
||||
#ifdef WANT_DEBUG
|
||||
qCDebug(modelformat) << "OBJ Reader Last material illumination model:" << currentMaterial.illuminationModel <<
|
||||
" shininess:" << currentMaterial.shininess << " opacity:" << currentMaterial.opacity <<
|
||||
" diffuse color:" << currentMaterial.diffuseColor << " specular color:" <<
|
||||
currentMaterial.specularColor << " emissive color:" << currentMaterial.emissiveColor <<
|
||||
" diffuse texture:" << currentMaterial.diffuseTextureFilename << " specular texture:" <<
|
||||
currentMaterial.specularTextureFilename << " emissive texture:" <<
|
||||
currentMaterial.emissiveTextureFilename << " bump texture:" <<
|
||||
currentMaterial.bumpTextureFilename;
|
||||
#endif
|
||||
qCDebug(modelformat) <<
|
||||
"OBJ Reader Last material illumination model:" << currentMaterial.illuminationModel <<
|
||||
" shininess:" << currentMaterial.shininess <<
|
||||
" opacity:" << currentMaterial.opacity <<
|
||||
" diffuse color:" << currentMaterial.diffuseColor <<
|
||||
" specular color:" << currentMaterial.specularColor <<
|
||||
" emissive color:" << currentMaterial.emissiveColor <<
|
||||
" diffuse texture:" << currentMaterial.diffuseTextureFilename <<
|
||||
" specular texture:" << currentMaterial.specularTextureFilename <<
|
||||
" emissive texture:" << currentMaterial.emissiveTextureFilename <<
|
||||
" bump texture:" << currentMaterial.bumpTextureFilename <<
|
||||
" opacity texture:" << currentMaterial.opacityTextureFilename;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
QByteArray token = tokenizer.getDatum();
|
||||
|
@ -289,6 +293,8 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
|||
currentMaterial.emissiveTextureFilename = "";
|
||||
currentMaterial.specularTextureFilename = "";
|
||||
currentMaterial.bumpTextureFilename = "";
|
||||
currentMaterial.opacityTextureFilename = "";
|
||||
|
||||
} else if (token == "Ns") {
|
||||
currentMaterial.shininess = tokenizer.getFloat();
|
||||
} else if (token == "Ni") {
|
||||
|
@ -321,7 +327,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
|||
currentMaterial.emissiveColor = tokenizer.getVec3();
|
||||
} else if (token == "Ks") {
|
||||
currentMaterial.specularColor = tokenizer.getVec3();
|
||||
} else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump")) {
|
||||
} else if ((token == "map_Kd") || (token == "map_Ke") || (token == "map_Ks") || (token == "map_bump") || (token == "bump") || (token == "map_d")) {
|
||||
const QByteArray textureLine = tokenizer.getLineAsDatum();
|
||||
QByteArray filename;
|
||||
OBJMaterialTextureOptions textureOptions;
|
||||
|
@ -341,6 +347,8 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
|
|||
} else if ((token == "map_bump") || (token == "bump")) {
|
||||
currentMaterial.bumpTextureFilename = filename;
|
||||
currentMaterial.bumpTextureOptions = textureOptions;
|
||||
} else if (token == "map_d") {
|
||||
currentMaterial.opacityTextureFilename = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -900,6 +908,9 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m
|
|||
fbxMaterial.normalTexture.isBumpmap = true;
|
||||
fbxMaterial.bumpMultiplier = objMaterial.bumpTextureOptions.bumpMultiplier;
|
||||
}
|
||||
if (!objMaterial.opacityTextureFilename.isEmpty()) {
|
||||
fbxMaterial.opacityTexture.filename = objMaterial.opacityTextureFilename;
|
||||
}
|
||||
|
||||
modelMaterial->setEmissive(fbxMaterial.emissiveColor);
|
||||
modelMaterial->setAlbedo(fbxMaterial.diffuseColor);
|
||||
|
|
|
@ -66,6 +66,8 @@ public:
|
|||
QByteArray specularTextureFilename;
|
||||
QByteArray emissiveTextureFilename;
|
||||
QByteArray bumpTextureFilename;
|
||||
QByteArray opacityTextureFilename;
|
||||
|
||||
OBJMaterialTextureOptions bumpTextureOptions;
|
||||
int illuminationModel;
|
||||
bool used { false };
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <glm/detail/type_vec.hpp>
|
||||
|
||||
#include "GpuHelpers.h"
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
namespace graphics {
|
||||
class Mesh;
|
||||
|
@ -55,18 +56,16 @@ namespace buffer_helpers {
|
|||
tangent = glm::clamp(tangent, -1.0f, 1.0f);
|
||||
normal *= 511.0f;
|
||||
tangent *= 511.0f;
|
||||
normal = glm::round(normal);
|
||||
tangent = glm::round(tangent);
|
||||
|
||||
glm::detail::i10i10i10i2 normalStruct;
|
||||
glm::detail::i10i10i10i2 tangentStruct;
|
||||
normalStruct.data.x = int(normal.x);
|
||||
normalStruct.data.y = int(normal.y);
|
||||
normalStruct.data.z = int(normal.z);
|
||||
normalStruct.data.x = fastLrintf(normal.x);
|
||||
normalStruct.data.y = fastLrintf(normal.y);
|
||||
normalStruct.data.z = fastLrintf(normal.z);
|
||||
normalStruct.data.w = 0;
|
||||
tangentStruct.data.x = int(tangent.x);
|
||||
tangentStruct.data.y = int(tangent.y);
|
||||
tangentStruct.data.z = int(tangent.z);
|
||||
tangentStruct.data.x = fastLrintf(tangent.x);
|
||||
tangentStruct.data.y = fastLrintf(tangent.y);
|
||||
tangentStruct.data.z = fastLrintf(tangent.z);
|
||||
tangentStruct.data.w = 0;
|
||||
packedNormal = normalStruct.pack;
|
||||
packedTangent = tangentStruct.pack;
|
||||
|
|
|
@ -64,10 +64,14 @@ float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeSc
|
|||
return voxelSizeScale / powf(2.0f, renderLevel);
|
||||
}
|
||||
|
||||
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) {
|
||||
float getPerspectiveAccuracyAngleTan(float octreeSizeScale, int boundaryLevelAdjust) {
|
||||
const float maxScale = (float)TREE_SCALE;
|
||||
float visibleDistanceAtMaxScale = boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale) / OCTREE_TO_MESH_RATIO;
|
||||
return atan(maxScale / visibleDistanceAtMaxScale);
|
||||
return (maxScale / visibleDistanceAtMaxScale);
|
||||
}
|
||||
|
||||
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust) {
|
||||
return atan(getPerspectiveAccuracyAngleTan(octreeSizeScale, boundaryLevelAdjust));
|
||||
}
|
||||
|
||||
float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust) {
|
||||
|
@ -75,9 +79,3 @@ float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust
|
|||
const float smallestSize = 0.01f;
|
||||
return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale);
|
||||
}
|
||||
|
||||
bool isAngularSizeBigEnough(glm::vec3 position, const AACube& cube, float lodScaleFactor, float minDiameter) {
|
||||
float distance = glm::distance(cube.calcCenter(), position) + MIN_VISIBLE_DISTANCE;
|
||||
float angularDiameter = cube.getScale() / distance;
|
||||
return angularDiameter > minDiameter * lodScaleFactor;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ float calculateRenderAccuracy(const glm::vec3& position,
|
|||
|
||||
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);
|
||||
|
||||
float getPerspectiveAccuracyAngleTan(float octreeSizeScale, int boundaryLevelAdjust);
|
||||
float getPerspectiveAccuracyAngle(float octreeSizeScale, int boundaryLevelAdjust);
|
||||
float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust);
|
||||
|
||||
|
@ -38,6 +39,4 @@ const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians
|
|||
const float MIN_ENTITY_ANGULAR_DIAMETER = MIN_ELEMENT_ANGULAR_DIAMETER * SQRT_THREE;
|
||||
const float MIN_VISIBLE_DISTANCE = 0.0001f; // helps avoid divide-by-zero check
|
||||
|
||||
bool isAngularSizeBigEnough(glm::vec3 position, const AACube& cube, float lodScaleFactor, float minDiameter);
|
||||
|
||||
#endif // hifi_OctreeUtils_h
|
||||
|
|
|
@ -778,8 +778,10 @@ void EntityMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& ma
|
|||
|
||||
bool EntityMotionState::shouldSendBid() const {
|
||||
// NOTE: this method is only ever called when the entity's simulation is NOT locally owned
|
||||
return _body->isActive() && (_region == workload::Region::R1) &&
|
||||
glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) >= _entity->getSimulationPriority();
|
||||
return _body->isActive()
|
||||
&& (_region == workload::Region::R1)
|
||||
&& glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) >= _entity->getSimulationPriority()
|
||||
&& !_entity->getLocked();
|
||||
}
|
||||
|
||||
uint8_t EntityMotionState::computeFinalBidPriority() const {
|
||||
|
|
|
@ -898,11 +898,12 @@ void PhysicsEngine::setShowBulletConstraintLimits(bool value) {
|
|||
}
|
||||
|
||||
struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
|
||||
AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject) :
|
||||
AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject, float threshold) :
|
||||
btCollisionWorld::ContactResultCallback(),
|
||||
collisionObject(),
|
||||
contacts(),
|
||||
myAvatarCollisionObject(myAvatarCollisionObject) {
|
||||
myAvatarCollisionObject(myAvatarCollisionObject),
|
||||
threshold(threshold) {
|
||||
const btCollisionShape* collisionShape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);
|
||||
|
||||
collisionObject.setCollisionShape(const_cast<btCollisionShape*>(collisionShape));
|
||||
|
@ -924,8 +925,13 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback {
|
|||
btCollisionObject collisionObject;
|
||||
std::vector<ContactTestResult> contacts;
|
||||
btCollisionObject* myAvatarCollisionObject;
|
||||
btScalar threshold;
|
||||
|
||||
btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0, int partId0, int index0, const btCollisionObjectWrapper* colObj1, int partId1, int index1) override {
|
||||
if (cp.m_distance1 > -threshold) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const btCollisionObject* otherBody;
|
||||
btVector3 penetrationPoint;
|
||||
btVector3 otherPenetrationPoint;
|
||||
|
@ -968,14 +974,14 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
std::vector<ContactTestResult> PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group) const {
|
||||
std::vector<ContactTestResult> PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group, float threshold) const {
|
||||
// TODO: Give MyAvatar a motion state so we don't have to do this
|
||||
btCollisionObject* myAvatarCollisionObject = nullptr;
|
||||
if ((mask & USER_COLLISION_GROUP_MY_AVATAR) && _myAvatarController) {
|
||||
myAvatarCollisionObject = _myAvatarController->getCollisionObject();
|
||||
}
|
||||
|
||||
auto contactCallback = AllContactsCallback((int32_t)mask, (int32_t)group, regionShapeInfo, regionTransform, myAvatarCollisionObject);
|
||||
auto contactCallback = AllContactsCallback((int32_t)mask, (int32_t)group, regionShapeInfo, regionTransform, myAvatarCollisionObject, threshold);
|
||||
_dynamicsWorld->contactTest(&contactCallback.collisionObject, contactCallback);
|
||||
|
||||
return contactCallback.contacts;
|
||||
|
|
|
@ -142,7 +142,7 @@ public:
|
|||
|
||||
// Function for getting colliding objects in the world of specified type
|
||||
// See PhysicsCollisionGroups.h for mask flags.
|
||||
std::vector<ContactTestResult> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const;
|
||||
std::vector<ContactTestResult> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC, float threshold = 0.0f) const;
|
||||
|
||||
private:
|
||||
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QVariant>
|
||||
|
||||
#include <shared/ReadWriteLockable.h>
|
||||
#include <TransformNode.h>
|
||||
|
||||
enum IntersectionType {
|
||||
NONE = 0,
|
||||
|
@ -213,6 +214,10 @@ public:
|
|||
virtual bool isRightHand() const { return false; }
|
||||
virtual bool isMouse() const { return false; }
|
||||
|
||||
virtual Transform getResultTransform() const = 0;
|
||||
|
||||
std::shared_ptr<TransformNode> parentTransform;
|
||||
|
||||
private:
|
||||
PickFilter _filter;
|
||||
const float _maxDistance;
|
||||
|
|
|
@ -57,8 +57,8 @@ bool PickCacheOptimizer<T>::checkAndCompareCachedResults(T& pick, PickCache& cac
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void PickCacheOptimizer<T>::cacheResult(const bool intersects, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr<Pick<T>> pick) {
|
||||
if (intersects) {
|
||||
void PickCacheOptimizer<T>::cacheResult(const bool needToCompareResults, const PickResultPointer& resTemp, const PickCacheKey& key, PickResultPointer& res, T& mathPick, PickCache& cache, const std::shared_ptr<Pick<T>> pick) {
|
||||
if (needToCompareResults) {
|
||||
cache[mathPick][key] = resTemp;
|
||||
res = res->compareAndProcessNewResult(resTemp);
|
||||
} else {
|
||||
|
|
|
@ -90,6 +90,14 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& includ
|
|||
}
|
||||
}
|
||||
|
||||
Transform PickManager::getResultTransform(unsigned int uid) const {
|
||||
auto pick = findPick(uid);
|
||||
if (pick) {
|
||||
return pick->getResultTransform();
|
||||
}
|
||||
return Transform();
|
||||
}
|
||||
|
||||
void PickManager::update() {
|
||||
uint64_t expiry = usecTimestampNow() + _perFrameTimeBudget;
|
||||
std::unordered_map<PickQuery::PickType, std::unordered_map<unsigned int, std::shared_ptr<PickQuery>>> cachedPicks;
|
||||
|
|
|
@ -43,6 +43,8 @@ public:
|
|||
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const;
|
||||
void setIncludeItems(unsigned int uid, const QVector<QUuid>& include) const;
|
||||
|
||||
Transform getResultTransform(unsigned int uid) const;
|
||||
|
||||
bool isLeftHand(unsigned int uid);
|
||||
bool isRightHand(unsigned int uid);
|
||||
bool isMouse(unsigned int uid);
|
||||
|
|
26
libraries/pointers/src/PickTransformNode.cpp
Normal file
26
libraries/pointers/src/PickTransformNode.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// Created by Sabrina Shanman 8/22/2018
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "PickTransformNode.h"
|
||||
|
||||
#include "DependencyManager.h"
|
||||
#include "PickManager.h"
|
||||
|
||||
PickTransformNode::PickTransformNode(unsigned int uid) :
|
||||
_uid(uid)
|
||||
{
|
||||
}
|
||||
|
||||
Transform PickTransformNode::getTransform() {
|
||||
auto pickManager = DependencyManager::get<PickManager>();
|
||||
if (!pickManager) {
|
||||
return Transform();
|
||||
}
|
||||
|
||||
return pickManager->getResultTransform(_uid);
|
||||
}
|
23
libraries/pointers/src/PickTransformNode.h
Normal file
23
libraries/pointers/src/PickTransformNode.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// Created by Sabrina Shanman 8/22/2018
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#ifndef hifi_PickTransformNode_h
|
||||
#define hifi_PickTransformNode_h
|
||||
|
||||
#include "TransformNode.h"
|
||||
|
||||
// TODO: Remove this class when Picks are converted to SpatiallyNestables
|
||||
class PickTransformNode : public TransformNode {
|
||||
public:
|
||||
PickTransformNode(unsigned int uid);
|
||||
Transform getTransform() override;
|
||||
|
||||
protected:
|
||||
unsigned int _uid;
|
||||
};
|
||||
|
||||
#endif // hifi_PickTransformNode_h
|
|
@ -50,6 +50,8 @@ public:
|
|||
virtual void setRenderState(const std::string& state) = 0;
|
||||
virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) = 0;
|
||||
|
||||
virtual QVariantMap toVariantMap() const = 0;
|
||||
|
||||
virtual void setPrecisionPicking(bool precisionPicking);
|
||||
virtual void setIgnoreItems(const QVector<QUuid>& ignoreItems) const;
|
||||
virtual void setIncludeItems(const QVector<QUuid>& includeItems) const;
|
||||
|
|
|
@ -77,6 +77,15 @@ PickResultPointer PointerManager::getPrevPickResult(unsigned int uid) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
QVariantMap PointerManager::getPointerProperties(unsigned int uid) const {
|
||||
auto pointer = find(uid);
|
||||
if (pointer) {
|
||||
return pointer->toVariantMap();
|
||||
} else {
|
||||
return QVariantMap();
|
||||
}
|
||||
}
|
||||
|
||||
void PointerManager::update() {
|
||||
auto cachedPointers = resultWithReadLock<std::unordered_map<unsigned int, std::shared_ptr<Pointer>>>([&] {
|
||||
return _pointers;
|
||||
|
|
|
@ -30,6 +30,7 @@ public:
|
|||
void setRenderState(unsigned int uid, const std::string& renderState) const;
|
||||
void editRenderState(unsigned int uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const;
|
||||
PickResultPointer getPrevPickResult(unsigned int uid) const;
|
||||
QVariantMap getPointerProperties(unsigned int uid) const;
|
||||
|
||||
void setPrecisionPicking(unsigned int uid, bool precisionPicking) const;
|
||||
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignoreEntities) const;
|
||||
|
|
|
@ -102,7 +102,7 @@ void CauterizedModel::createRenderItemSet() {
|
|||
}
|
||||
}
|
||||
|
||||
void CauterizedModel::updateClusterMatrices() {
|
||||
void CauterizedModel::updateClusterMatrices(bool triggerBlendshapes) {
|
||||
PerformanceTimer perfTimer("CauterizedModel::updateClusterMatrices");
|
||||
|
||||
if (!_needsUpdateClusterMatrices || !isLoaded()) {
|
||||
|
@ -175,7 +175,7 @@ void CauterizedModel::updateClusterMatrices() {
|
|||
|
||||
// post the blender if we're not currently waiting for one to finish
|
||||
auto modelBlender = DependencyManager::get<ModelBlender>();
|
||||
if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||
if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||
modelBlender->noteRequiresBlend(getThisPointer());
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
|
||||
void createRenderItemSet() override;
|
||||
|
||||
virtual void updateClusterMatrices() override;
|
||||
virtual void updateClusterMatrices(bool triggerBlendshapes = true) override;
|
||||
void updateRenderItems() override;
|
||||
|
||||
const Model::MeshState& getCauterizeMeshState(int index) const;
|
||||
|
|
|
@ -38,33 +38,6 @@ void DebugDeferredBufferConfig::setMode(int newMode) {
|
|||
emit dirty();
|
||||
}
|
||||
|
||||
#if 0
|
||||
enum TextureSlot {
|
||||
Albedo = 0,
|
||||
Normal,
|
||||
Specular,
|
||||
Depth,
|
||||
Lighting,
|
||||
Shadow,
|
||||
LinearDepth,
|
||||
HalfLinearDepth,
|
||||
HalfNormal,
|
||||
Curvature,
|
||||
DiffusedCurvature,
|
||||
Scattering,
|
||||
AmbientOcclusion,
|
||||
AmbientOcclusionBlurred,
|
||||
Velocity,
|
||||
};
|
||||
|
||||
enum ParamSlot {
|
||||
CameraCorrection = 0,
|
||||
DeferredFrameTransform,
|
||||
ShadowTransform,
|
||||
DebugParametersBuffer
|
||||
};
|
||||
#endif
|
||||
|
||||
static const std::string DEFAULT_ALBEDO_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" DeferredFragment frag = unpackDeferredFragmentNoPosition(uv);"
|
||||
|
@ -178,19 +151,19 @@ static const std::string DEFAULT_SHADOW_CASCADE_SHADER{
|
|||
|
||||
static const std::string DEFAULT_LINEAR_DEPTH_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(vec3(1.0 - texture(linearDepthMap, uv).x * 0.01), 1.0);"
|
||||
" return vec4(vec3(1.0 - texture(debugTexture0, uv).x * 0.01), 1.0);"
|
||||
"}"
|
||||
};
|
||||
|
||||
static const std::string DEFAULT_HALF_LINEAR_DEPTH_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(vec3(1.0 - texture(halfLinearDepthMap, uv).x * 0.01), 1.0);"
|
||||
" return vec4(vec3(1.0 - texture(debugTexture0, uv).x * 0.01), 1.0);"
|
||||
" }"
|
||||
};
|
||||
|
||||
static const std::string DEFAULT_HALF_NORMAL_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(vec3(texture(halfNormalMap, uv).xyz), 1.0);"
|
||||
" return vec4(vec3(texture(debugTexture0, uv).xyz), 1.0);"
|
||||
" }"
|
||||
};
|
||||
|
||||
|
@ -240,27 +213,27 @@ static const std::string DEFAULT_CURVATURE_OCCLUSION_SHADER{
|
|||
|
||||
static const std::string DEFAULT_DEBUG_SCATTERING_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(pow(vec3(texture(scatteringMap, uv).xyz), vec3(1.0 / 2.2)), 1.0);"
|
||||
// " return vec4(vec3(texture(scatteringMap, uv).xyz), 1.0);"
|
||||
" return vec4(pow(vec3(texture(debugTexture0, uv).xyz), vec3(1.0 / 2.2)), 1.0);"
|
||||
// " return vec4(vec3(texture(debugTexture0, uv).xyz), 1.0);"
|
||||
" }"
|
||||
};
|
||||
|
||||
static const std::string DEFAULT_AMBIENT_OCCLUSION_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(vec3(texture(obscuranceMap, uv).x), 1.0);"
|
||||
// When drawing color " return vec4(vec3(texture(occlusionMap, uv).xyz), 1.0);"
|
||||
// when drawing normal" return vec4(normalize(texture(occlusionMap, uv).xyz * 2.0 - vec3(1.0)), 1.0);"
|
||||
// When drawing color " return vec4(vec3(texture(debugTexture0, uv).xyz), 1.0);"
|
||||
// when drawing normal" return vec4(normalize(texture(debugTexture0, uv).xyz * 2.0 - vec3(1.0)), 1.0);"
|
||||
" }"
|
||||
};
|
||||
static const std::string DEFAULT_AMBIENT_OCCLUSION_BLURRED_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(vec3(texture(occlusionBlurredMap, uv).xyz), 1.0);"
|
||||
" return vec4(vec3(texture(debugTexture0, uv).xyz), 1.0);"
|
||||
" }"
|
||||
};
|
||||
|
||||
static const std::string DEFAULT_VELOCITY_SHADER{
|
||||
"vec4 getFragmentColor() {"
|
||||
" return vec4(vec2(texture(velocityMap, uv).xy), 0.0, 1.0);"
|
||||
" return vec4(vec2(texture(debugTexture0, uv).xy), 0.0, 1.0);"
|
||||
" }"
|
||||
};
|
||||
|
||||
|
@ -463,13 +436,10 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
|
|||
batch.setResourceTexture(Textures::DeferredDepth, deferredFramebuffer->getPrimaryDepthTexture());
|
||||
batch.setResourceTexture(Textures::DeferredLighting, deferredFramebuffer->getLightingTexture());
|
||||
}
|
||||
if (velocityFramebuffer) {
|
||||
batch.setResourceTexture(Textures::DebugVelocity, velocityFramebuffer->getVelocityTexture());
|
||||
if (velocityFramebuffer && _mode == VelocityMode) {
|
||||
batch.setResourceTexture(Textures::DebugTexture0, velocityFramebuffer->getVelocityTexture());
|
||||
}
|
||||
|
||||
// FIXME can't find the corresponding buffer
|
||||
// batch.setUniformBuffer(UBOs:: DebugParametersBuffer, _parameters);
|
||||
|
||||
auto lightStage = renderContext->_scene->getStage<LightStage>();
|
||||
assert(lightStage);
|
||||
assert(lightStage->getNumLights() > 0);
|
||||
|
@ -479,12 +449,17 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
|
|||
batch.setResourceTexture(Textures::Shadow, globalShadow->map);
|
||||
batch.setUniformBuffer(UBOs::ShadowParams, globalShadow->getBuffer());
|
||||
batch.setUniformBuffer(UBOs::DeferredFrameTransform, frameTransform->getFrameTransformBuffer());
|
||||
batch.setUniformBuffer(UBOs::DebugDeferredParams, _parameters);
|
||||
}
|
||||
|
||||
if (linearDepthTarget) {
|
||||
batch.setResourceTexture(Textures::DebugDepth, linearDepthTarget->getLinearDepthTexture());
|
||||
batch.setResourceTexture(Textures::DebugHalfDepth, linearDepthTarget->getHalfLinearDepthTexture());
|
||||
batch.setResourceTexture(Textures::DebugHalfNormal, linearDepthTarget->getHalfNormalTexture());
|
||||
if (_mode == DepthMode) {
|
||||
batch.setResourceTexture(Textures::DebugTexture0, linearDepthTarget->getLinearDepthTexture());
|
||||
} else if (_mode == HalfLinearDepthMode) {
|
||||
batch.setResourceTexture(Textures::DebugTexture0, linearDepthTarget->getHalfLinearDepthTexture());
|
||||
} else if (_mode == HalfNormalMode) {
|
||||
batch.setResourceTexture(Textures::DebugTexture0, linearDepthTarget->getHalfNormalTexture());
|
||||
}
|
||||
}
|
||||
if (surfaceGeometryFramebuffer) {
|
||||
batch.setResourceTexture(Textures::DeferredCurvature, surfaceGeometryFramebuffer->getCurvatureTexture());
|
||||
|
@ -492,9 +467,11 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
|
|||
surfaceGeometryFramebuffer->getLowCurvatureTexture());
|
||||
}
|
||||
if (ambientOcclusionFramebuffer) {
|
||||
batch.setResourceTexture(Textures::DebugOcclusion, ambientOcclusionFramebuffer->getOcclusionTexture());
|
||||
batch.setResourceTexture(Textures::DebugOcclusionBlurred,
|
||||
ambientOcclusionFramebuffer->getOcclusionBlurredTexture());
|
||||
if (_mode == AmbientOcclusionMode) {
|
||||
batch.setResourceTexture(Textures::DebugTexture0, ambientOcclusionFramebuffer->getOcclusionTexture());
|
||||
} else if (_mode == AmbientOcclusionBlurredMode) {
|
||||
batch.setResourceTexture(Textures::DebugTexture0, ambientOcclusionFramebuffer->getOcclusionBlurredTexture());
|
||||
}
|
||||
}
|
||||
const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
const glm::vec2 bottomLeft(_size.x, _size.y);
|
||||
|
@ -505,19 +482,14 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I
|
|||
batch.setResourceTexture(Textures::DeferredNormal, nullptr);
|
||||
batch.setResourceTexture(Textures::DeferredSpecular, nullptr);
|
||||
batch.setResourceTexture(Textures::DeferredDepth, nullptr);
|
||||
batch.setResourceTexture(Textures::DeferredLighting, nullptr);
|
||||
batch.setResourceTexture(Textures::Shadow, nullptr);
|
||||
batch.setResourceTexture(Textures::DebugDepth, nullptr);
|
||||
batch.setResourceTexture(Textures::DebugHalfDepth, nullptr);
|
||||
batch.setResourceTexture(Textures::DebugHalfNormal, nullptr);
|
||||
|
||||
batch.setResourceTexture(Textures::DeferredCurvature, nullptr);
|
||||
batch.setResourceTexture(Textures::DeferredDiffusedCurvature, nullptr);
|
||||
batch.setResourceTexture(Textures::DeferredLighting, nullptr);
|
||||
|
||||
batch.setResourceTexture(Textures::DebugOcclusion, nullptr);
|
||||
batch.setResourceTexture(Textures::DebugOcclusionBlurred, nullptr);
|
||||
|
||||
batch.setResourceTexture(Textures::DebugVelocity, nullptr);
|
||||
for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {
|
||||
batch.setResourceTexture(Textures::Shadow + i, nullptr);
|
||||
}
|
||||
|
||||
batch.setResourceTexture(Textures::DebugTexture0, nullptr);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -976,7 +976,7 @@ bool Model::addToScene(const render::ScenePointer& scene,
|
|||
render::Transaction& transaction,
|
||||
render::Item::Status::Getters& statusGetters) {
|
||||
if (!_addedToScene && isLoaded()) {
|
||||
updateClusterMatrices();
|
||||
updateClusterMatrices(false);
|
||||
if (_modelMeshRenderItems.empty()) {
|
||||
createRenderItemSet();
|
||||
}
|
||||
|
@ -1486,7 +1486,7 @@ void Model::computeMeshPartLocalBounds() {
|
|||
}
|
||||
|
||||
// virtual
|
||||
void Model::updateClusterMatrices() {
|
||||
void Model::updateClusterMatrices(bool triggerBlendshapes) {
|
||||
DETAILED_PERFORMANCE_TIMER("Model::updateClusterMatrices");
|
||||
|
||||
if (!_needsUpdateClusterMatrices || !isLoaded()) {
|
||||
|
@ -1515,7 +1515,7 @@ void Model::updateClusterMatrices() {
|
|||
|
||||
// post the blender if we're not currently waiting for one to finish
|
||||
auto modelBlender = DependencyManager::get<ModelBlender>();
|
||||
if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||
if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||
modelBlender->noteRequiresBlend(getThisPointer());
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ public:
|
|||
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
|
||||
|
||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||
virtual void updateClusterMatrices();
|
||||
virtual void updateClusterMatrices(bool triggerBlendshapes = true);
|
||||
|
||||
/// Returns a reference to the shared geometry.
|
||||
const Geometry::Pointer& getGeometry() const { return _renderGeometry; }
|
||||
|
|
|
@ -62,8 +62,8 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
|
|||
|
||||
const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterReceiverFilter, queryResolution).asVarying();
|
||||
const auto shadowSelection = task.addJob<FetchSpatialTree>("FetchShadowTree", fetchInput);
|
||||
const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterReceiverFilter).asVarying();
|
||||
const auto shadowItems = task.addJob<FetchSpatialSelection>("FetchShadowSelection", selectionInputs);
|
||||
const auto selectionInputs = FilterSpatialSelection::Inputs(shadowSelection, shadowCasterReceiverFilter).asVarying();
|
||||
const auto shadowItems = task.addJob<FilterSpatialSelection>("FilterShadowSelection", selectionInputs);
|
||||
|
||||
// Cull objects that are not visible in camera view. Hopefully the cull functor only performs LOD culling, not
|
||||
// frustum culling or this will make shadow casters out of the camera frustum disappear.
|
||||
|
|
|
@ -31,7 +31,7 @@ int SoftAttachmentModel::getJointIndexOverride(int i) const {
|
|||
|
||||
// virtual
|
||||
// use the _rigOverride matrices instead of the Model::_rig
|
||||
void SoftAttachmentModel::updateClusterMatrices() {
|
||||
void SoftAttachmentModel::updateClusterMatrices(bool triggerBlendshapes) {
|
||||
if (!_needsUpdateClusterMatrices) {
|
||||
return;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ void SoftAttachmentModel::updateClusterMatrices() {
|
|||
|
||||
// post the blender if we're not currently waiting for one to finish
|
||||
auto modelBlender = DependencyManager::get<ModelBlender>();
|
||||
if (modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||
if (triggerBlendshapes && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
|
||||
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
|
||||
modelBlender->noteRequiresBlend(getThisPointer());
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
~SoftAttachmentModel();
|
||||
|
||||
void updateRig(float deltaTime, glm::mat4 parentTransform) override;
|
||||
void updateClusterMatrices() override;
|
||||
void updateClusterMatrices(bool triggerBlendshapes = true) override;
|
||||
|
||||
protected:
|
||||
int getJointIndexOverride(int i) const;
|
||||
|
|
|
@ -16,14 +16,8 @@
|
|||
<@include gpu/Color.slh@>
|
||||
<$declareColorWheel()$>
|
||||
|
||||
layout(binding=RENDER_UTILS_TEXTURE_DEBUG_DEPTH) uniform sampler2D linearDepthMap;
|
||||
layout(binding=RENDER_UTILS_TEXTURE_DEBUG_HALF_DEPTH) uniform sampler2D halfLinearDepthMap;
|
||||
layout(binding=RENDER_UTILS_TEXTURE_DEBUG_HALF_NORMAL) uniform sampler2D halfNormalMap;
|
||||
layout(binding=RENDER_UTILS_TEXTURE_DEBUG_OCCLUSION) uniform sampler2D occlusionMap;
|
||||
layout(binding=RENDER_UTILS_TEXTURE_DEBUG_OCCLUSION_BLURRED) uniform sampler2D occlusionBlurredMap;
|
||||
layout(binding=RENDER_UTILS_TEXTURE_DEBUG_SCATTERING) uniform sampler2D scatteringMap;
|
||||
layout(binding=RENDER_UTILS_TEXTURE_DEBUG_VELOCITY) uniform sampler2D velocityMap;
|
||||
layout(binding=RENDER_UTILS_TEXTURE_DEBUG_SHADOWS) uniform sampler2DArrayShadow shadowMaps;
|
||||
layout(binding=RENDER_UTILS_DEBUG_TEXTURE0) uniform sampler2D debugTexture0;
|
||||
layout(binding=RENDER_UTILS_TEXTURE_SHADOW) uniform sampler2DArrayShadow shadowMaps;
|
||||
|
||||
<@include ShadowCore.slh@>
|
||||
|
||||
|
@ -31,7 +25,7 @@ layout(binding=RENDER_UTILS_TEXTURE_DEBUG_SHADOWS) uniform sampler2DArrayShadow
|
|||
|
||||
<@include debug_deferred_buffer_shared.slh@>
|
||||
|
||||
layout(std140) uniform parametersBuffer {
|
||||
layout(std140, binding=RENDER_UTILS_BUFFER_DEBUG_DEFERRED_PARAMS) uniform parametersBuffer {
|
||||
DebugParameters parameters;
|
||||
};
|
||||
|
||||
|
|
|
@ -126,14 +126,8 @@
|
|||
|
||||
// Debugging
|
||||
#define RENDER_UTILS_BUFFER_DEBUG_SKYBOX 5
|
||||
#define RENDER_UTILS_TEXTURE_DEBUG_DEPTH 11
|
||||
#define RENDER_UTILS_TEXTURE_DEBUG_HALF_DEPTH 12
|
||||
#define RENDER_UTILS_TEXTURE_DEBUG_OCCLUSION 13
|
||||
#define RENDER_UTILS_TEXTURE_DEBUG_OCCLUSION_BLURRED 14
|
||||
#define RENDER_UTILS_TEXTURE_DEBUG_VELOCITY 15
|
||||
#define RENDER_UTILS_TEXTURE_DEBUG_SHADOWS 16
|
||||
#define RENDER_UTILS_TEXTURE_DEBUG_HALF_NORMAL 17
|
||||
#define RENDER_UTILS_TEXTURE_DEBUG_SCATTERING 18
|
||||
#define RENDER_UTILS_DEBUG_TEXTURE0 11
|
||||
#define RENDER_UTILS_BUFFER_DEBUG_DEFERRED_PARAMS 1
|
||||
|
||||
// <!
|
||||
#include <gpu/ShaderConstants.h>
|
||||
|
@ -174,6 +168,7 @@ enum Buffer {
|
|||
BloomParams = RENDER_UTILS_BUFFER_BLOOM_PARAMS,
|
||||
ToneMappingParams = RENDER_UTILS_BUFFER_TM_PARAMS,
|
||||
ShadowParams = RENDER_UTILS_BUFFER_SHADOW_PARAMS,
|
||||
DebugDeferredParams = RENDER_UTILS_BUFFER_DEBUG_DEFERRED_PARAMS,
|
||||
};
|
||||
} // namespace buffer
|
||||
|
||||
|
@ -212,14 +207,7 @@ enum Texture {
|
|||
BloomColor = RENDER_UTILS_TEXTURE_BLOOM_COLOR,
|
||||
ToneMappingColor = RENDER_UTILS_TEXTURE_TM_COLOR,
|
||||
TextFont = RENDER_UTILS_TEXTURE_TEXT_FONT,
|
||||
DebugDepth = RENDER_UTILS_TEXTURE_DEBUG_DEPTH,
|
||||
DebugHalfDepth = RENDER_UTILS_TEXTURE_DEBUG_HALF_DEPTH,
|
||||
DebugOcclusion = RENDER_UTILS_TEXTURE_DEBUG_OCCLUSION,
|
||||
DebugOcclusionBlurred = RENDER_UTILS_TEXTURE_DEBUG_OCCLUSION_BLURRED,
|
||||
DebugVelocity = RENDER_UTILS_TEXTURE_DEBUG_VELOCITY,
|
||||
DebugShadows = RENDER_UTILS_TEXTURE_DEBUG_SHADOWS,
|
||||
DebugHalfNormal = RENDER_UTILS_TEXTURE_DEBUG_HALF_NORMAL,
|
||||
DebugScattering = RENDER_UTILS_TEXTURE_DEBUG_SCATTERING,
|
||||
DebugTexture0 = RENDER_UTILS_DEBUG_TEXTURE0,
|
||||
};
|
||||
} // namespace texture
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ namespace render {
|
|||
Args(const gpu::ContextPointer& context,
|
||||
float sizeScale = 1.0f,
|
||||
int boundaryLevelAdjust = 0,
|
||||
float lodAngleHalfTan = 0.1f,
|
||||
RenderMode renderMode = DEFAULT_RENDER_MODE,
|
||||
DisplayMode displayMode = MONO,
|
||||
DebugFlags debugFlags = RENDER_DEBUG_NONE,
|
||||
|
@ -80,6 +81,8 @@ namespace render {
|
|||
_context(context),
|
||||
_sizeScale(sizeScale),
|
||||
_boundaryLevelAdjust(boundaryLevelAdjust),
|
||||
_lodAngleHalfTan(lodAngleHalfTan),
|
||||
_lodAngleHalfTanSq(lodAngleHalfTan * lodAngleHalfTan),
|
||||
_renderMode(renderMode),
|
||||
_displayMode(displayMode),
|
||||
_debugFlags(debugFlags),
|
||||
|
@ -105,8 +108,12 @@ namespace render {
|
|||
std::stack<ViewFrustum> _viewFrustums;
|
||||
glm::ivec4 _viewport { 0.0f, 0.0f, 1.0f, 1.0f };
|
||||
glm::vec3 _boomOffset { 0.0f, 0.0f, 1.0f };
|
||||
|
||||
float _sizeScale { 1.0f };
|
||||
int _boundaryLevelAdjust { 0 };
|
||||
float _lodAngleHalfTan{ 0.1f };
|
||||
float _lodAngleHalfTanSq{ _lodAngleHalfTan * _lodAngleHalfTan };
|
||||
|
||||
RenderMode _renderMode { DEFAULT_RENDER_MODE };
|
||||
DisplayMode _displayMode { MONO };
|
||||
DebugFlags _debugFlags { RENDER_DEBUG_NONE };
|
||||
|
|
|
@ -155,7 +155,7 @@ void FetchSpatialTree::run(const RenderContextPointer& renderContext, const Inpu
|
|||
// Octree selection!
|
||||
float threshold = 0.0f;
|
||||
if (queryFrustum.isPerspective()) {
|
||||
threshold = getPerspectiveAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust);
|
||||
threshold = args->_lodAngleHalfTan;
|
||||
if (frustumResolution.y > 0) {
|
||||
threshold = glm::max(queryFrustum.getFieldOfView() / frustumResolution.y, threshold);
|
||||
}
|
||||
|
@ -445,7 +445,7 @@ void ApplyCullFunctorOnItemBounds::run(const RenderContextPointer& renderContext
|
|||
}
|
||||
}
|
||||
|
||||
void FetchSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||
void FilterSpatialSelection::run(const RenderContextPointer& renderContext,
|
||||
const Inputs& inputs, ItemBounds& outItems) {
|
||||
assert(renderContext->args);
|
||||
auto& scene = renderContext->_scene;
|
||||
|
|
|
@ -147,12 +147,12 @@ namespace render {
|
|||
|
||||
};
|
||||
|
||||
class FetchSpatialSelection {
|
||||
class FilterSpatialSelection {
|
||||
public:
|
||||
using Inputs = render::VaryingSet2<ItemSpatialTree::ItemSelection, ItemFilter>;
|
||||
using JobModel = Job::ModelIO<FetchSpatialSelection, Inputs, ItemBounds>;
|
||||
using JobModel = Job::ModelIO<FilterSpatialSelection, Inputs, ItemBounds>;
|
||||
|
||||
FetchSpatialSelection() {}
|
||||
FilterSpatialSelection() {}
|
||||
void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems);
|
||||
};
|
||||
|
||||
|
|
|
@ -79,33 +79,43 @@ void AABox::setBox(const glm::vec3& corner, const glm::vec3& scale) {
|
|||
|
||||
glm::vec3 AABox::getFarthestVertex(const glm::vec3& normal) const {
|
||||
glm::vec3 result = _corner;
|
||||
if (normal.x > 0.0f) {
|
||||
result.x += _scale.x;
|
||||
}
|
||||
if (normal.y > 0.0f) {
|
||||
result.y += _scale.y;
|
||||
}
|
||||
if (normal.z > 0.0f) {
|
||||
result.z += _scale.z;
|
||||
}
|
||||
// This is a branchless version of:
|
||||
//if (normal.x > 0.0f) {
|
||||
// result.x += _scale.x;
|
||||
//}
|
||||
//if (normal.y > 0.0f) {
|
||||
// result.y += _scale.y;
|
||||
//}
|
||||
//if (normal.z > 0.0f) {
|
||||
// result.z += _scale.z;
|
||||
//}
|
||||
float blend = (float)(normal.x > 0.0f);
|
||||
result.x += blend * _scale.x + (1.0f - blend) * 0.0f;
|
||||
blend = (float)(normal.y > 0.0f);
|
||||
result.y += blend * _scale.y + (1.0f - blend) * 0.0f;
|
||||
blend = (float)(normal.z > 0.0f);
|
||||
result.z += blend * _scale.z + (1.0f - blend) * 0.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
glm::vec3 AABox::getNearestVertex(const glm::vec3& normal) const {
|
||||
glm::vec3 result = _corner;
|
||||
|
||||
if (normal.x < 0.0f) {
|
||||
result.x += _scale.x;
|
||||
}
|
||||
|
||||
if (normal.y < 0.0f) {
|
||||
result.y += _scale.y;
|
||||
}
|
||||
|
||||
if (normal.z < 0.0f) {
|
||||
result.z += _scale.z;
|
||||
}
|
||||
|
||||
// This is a branchless version of:
|
||||
//if (normal.x < 0.0f) {
|
||||
// result.x += _scale.x;
|
||||
//}
|
||||
//if (normal.y < 0.0f) {
|
||||
// result.y += _scale.y;
|
||||
//}
|
||||
//if (normal.z < 0.0f) {
|
||||
// result.z += _scale.z;
|
||||
//}
|
||||
float blend = (float)(normal.x < 0.0f);
|
||||
result.x += blend * _scale.x + (1.0f - blend) * 0.0f;
|
||||
blend = (float)(normal.y < 0.0f);
|
||||
result.y += blend * _scale.y + (1.0f - blend) * 0.0f;
|
||||
blend = (float)(normal.z < 0.0f);
|
||||
result.z += blend * _scale.z + (1.0f - blend) * 0.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -459,28 +469,6 @@ AABox AABox::clamp(float min, float max) const {
|
|||
return AABox(clampedCorner, clampedScale);
|
||||
}
|
||||
|
||||
AABox& AABox::operator += (const glm::vec3& point) {
|
||||
|
||||
if (isInvalid()) {
|
||||
_corner = glm::min(_corner, point);
|
||||
} else {
|
||||
glm::vec3 maximum(_corner + _scale);
|
||||
_corner = glm::min(_corner, point);
|
||||
maximum = glm::max(maximum, point);
|
||||
_scale = maximum - _corner;
|
||||
}
|
||||
|
||||
return (*this);
|
||||
}
|
||||
|
||||
AABox& AABox::operator += (const AABox& box) {
|
||||
if (!box.isInvalid()) {
|
||||
(*this) += box._corner;
|
||||
(*this) += box.calcTopFarLeft();
|
||||
}
|
||||
return (*this);
|
||||
}
|
||||
|
||||
void AABox::embiggen(float scale) {
|
||||
_corner += scale * (-0.5f * _scale);
|
||||
_scale *= scale;
|
||||
|
|
|
@ -85,8 +85,23 @@ public:
|
|||
AABox clamp(const glm::vec3& min, const glm::vec3& max) const;
|
||||
AABox clamp(float min, float max) const;
|
||||
|
||||
AABox& operator += (const glm::vec3& point);
|
||||
AABox& operator += (const AABox& box);
|
||||
inline AABox& operator+=(const glm::vec3& point) {
|
||||
bool valid = !isInvalid();
|
||||
glm::vec3 maximum = glm::max(_corner + _scale, point);
|
||||
_corner = glm::min(_corner, point);
|
||||
if (valid) {
|
||||
_scale = maximum - _corner;
|
||||
}
|
||||
return (*this);
|
||||
}
|
||||
|
||||
inline AABox& operator+=(const AABox& box) {
|
||||
if (!box.isInvalid()) {
|
||||
(*this) += box._corner;
|
||||
(*this) += box.calcTopFarLeft();
|
||||
}
|
||||
return (*this);
|
||||
}
|
||||
|
||||
// Translate the AABox just moving the corner
|
||||
void translate(const glm::vec3& translation) { _corner += translation; }
|
||||
|
@ -114,7 +129,7 @@ public:
|
|||
|
||||
static const glm::vec3 INFINITY_VECTOR;
|
||||
|
||||
bool isInvalid() const { return _corner == INFINITY_VECTOR; }
|
||||
bool isInvalid() const { return _corner.x == std::numeric_limits<float>::infinity(); }
|
||||
|
||||
void clear() { _corner = INFINITY_VECTOR; _scale = glm::vec3(0.0f); }
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ const float DEFAULT_AVATAR_RIGHTHAND_MASS = 2.0f;
|
|||
|
||||
// Used when avatar is missing joints... (avatar space)
|
||||
const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };
|
||||
const glm::vec3 DEFAULT_AVATAR_MIDDLE_EYE_POS { 0.0f, 0.6f, 0.0f };
|
||||
const glm::vec3 DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET = { 0.0f, 0.06f, -0.09f };
|
||||
const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.53f, 0.0f };
|
||||
const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 };
|
||||
const glm::vec3 DEFAULT_AVATAR_RIGHTARM_POS { -0.134824f, 0.396348f, -0.0515777f };
|
||||
|
|
|
@ -316,4 +316,17 @@ inline void glm_mat4u_mul(const glm::mat4& m1, const glm::mat4& m2, glm::mat4& r
|
|||
#endif
|
||||
}
|
||||
|
||||
// convert float to int, using round-to-nearest-even (undefined on overflow)
|
||||
inline int fastLrintf(float x) {
|
||||
#if GLM_ARCH & GLM_ARCH_SSE2_BIT
|
||||
return _mm_cvt_ss2si(_mm_set_ss(x));
|
||||
#else
|
||||
// return lrintf(x);
|
||||
static_assert(std::numeric_limits<double>::is_iec559, "Requires IEEE-754 double precision format");
|
||||
union { double d; int64_t i; } bits = { (double)x };
|
||||
bits.d += (3ULL << 51);
|
||||
return (int)bits.i;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // hifi_GLMHelpers_h
|
||||
|
|
31
libraries/shared/src/NestableTransformNode.cpp
Normal file
31
libraries/shared/src/NestableTransformNode.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Created by Sabrina Shanman 8/14/2018
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "NestableTransformNode.h"
|
||||
|
||||
NestableTransformNode::NestableTransformNode(SpatiallyNestableWeakPointer spatiallyNestable, int jointIndex) :
|
||||
_spatiallyNestable(spatiallyNestable),
|
||||
_jointIndex(jointIndex)
|
||||
{
|
||||
}
|
||||
|
||||
Transform NestableTransformNode::getTransform() {
|
||||
auto nestable = _spatiallyNestable.lock();
|
||||
if (!nestable) {
|
||||
return Transform();
|
||||
}
|
||||
|
||||
bool success;
|
||||
Transform jointWorldTransform = nestable->getTransform(_jointIndex, success);
|
||||
|
||||
if (success) {
|
||||
return jointWorldTransform;
|
||||
} else {
|
||||
return Transform();
|
||||
}
|
||||
}
|
25
libraries/shared/src/NestableTransformNode.h
Normal file
25
libraries/shared/src/NestableTransformNode.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Created by Sabrina Shanman 8/14/2018
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#ifndef hifi_NestableTransformNode_h
|
||||
#define hifi_NestableTransformNode_h
|
||||
|
||||
#include "TransformNode.h"
|
||||
|
||||
#include "SpatiallyNestable.h"
|
||||
|
||||
class NestableTransformNode : public TransformNode {
|
||||
public:
|
||||
NestableTransformNode(SpatiallyNestableWeakPointer spatiallyNestable, int jointIndex);
|
||||
Transform getTransform() override;
|
||||
|
||||
protected:
|
||||
SpatiallyNestableWeakPointer _spatiallyNestable;
|
||||
int _jointIndex;
|
||||
};
|
||||
|
||||
#endif // hifi_NestableTransformNode_h
|
|
@ -12,7 +12,6 @@
|
|||
#define hifi_PrioritySortUtil_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <queue>
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
#include "shared/ConicalViewFrustum.h"
|
||||
|
@ -75,7 +74,6 @@ namespace PrioritySortUtil {
|
|||
|
||||
void setPriority(float priority) { _priority = priority; }
|
||||
float getPriority() const { return _priority; }
|
||||
bool operator<(const Sortable& other) const { return _priority < other._priority; }
|
||||
private:
|
||||
float _priority { 0.0f };
|
||||
};
|
||||
|
@ -97,14 +95,18 @@ namespace PrioritySortUtil {
|
|||
_ageWeight = ageWeight;
|
||||
}
|
||||
|
||||
size_t size() const { return _queue.size(); }
|
||||
size_t size() const { return _vector.size(); }
|
||||
void push(T thing) {
|
||||
thing.setPriority(computePriority(thing));
|
||||
_queue.push(thing);
|
||||
_vector.push_back(thing);
|
||||
}
|
||||
void reserve(size_t num) {
|
||||
_vector.reserve(num);
|
||||
}
|
||||
const std::vector<T>& getSortedVector() {
|
||||
std::sort(_vector.begin(), _vector.end(), [](const T& left, const T& right) { return left.getPriority() > right.getPriority(); });
|
||||
return _vector;
|
||||
}
|
||||
const T& top() const { return _queue.top(); }
|
||||
void pop() { return _queue.pop(); }
|
||||
bool empty() const { return _queue.empty(); }
|
||||
|
||||
private:
|
||||
|
||||
|
@ -153,7 +155,7 @@ namespace PrioritySortUtil {
|
|||
}
|
||||
|
||||
ConicalViewFrustums _views;
|
||||
std::priority_queue<T> _queue;
|
||||
std::vector<T> _vector;
|
||||
float _angularWeight { DEFAULT_ANGULAR_COEF };
|
||||
float _centerWeight { DEFAULT_CENTER_COEF };
|
||||
float _ageWeight { DEFAULT_AGE_COEF };
|
||||
|
|
|
@ -253,18 +253,36 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/**jsdoc
|
||||
* A CollisionPick defines a volume for checking collisions in the physics simulation.
|
||||
// TODO: Add "loaded" to CollisionRegion jsdoc once model collision picks are supported.
|
||||
|
||||
* @typedef {object} CollisionPick
|
||||
/**jsdoc
|
||||
* A CollisionRegion defines a volume for checking collisions in the physics simulation.
|
||||
|
||||
* @typedef {object} CollisionRegion
|
||||
* @property {Shape} shape - The information about the collision region's size and shape.
|
||||
* @property {Vec3} position - The position of the collision region.
|
||||
* @property {Quat} orientation - The orientation of the collision region.
|
||||
* @property {Vec3} position - The position of the collision region, relative to a parent if defined.
|
||||
* @property {Quat} orientation - The orientation of the collision region, relative to a parent if defined.
|
||||
* @property {float} threshold - The approximate minimum penetration depth for a test object to be considered in contact with the collision region.
|
||||
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay.
|
||||
* @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
|
||||
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.
|
||||
*/
|
||||
class CollisionRegion : public MathPick {
|
||||
public:
|
||||
CollisionRegion() { }
|
||||
|
||||
CollisionRegion(const CollisionRegion& collisionRegion) :
|
||||
loaded(collisionRegion.loaded),
|
||||
modelURL(collisionRegion.modelURL),
|
||||
shapeInfo(std::make_shared<ShapeInfo>()),
|
||||
transform(collisionRegion.transform),
|
||||
threshold(collisionRegion.threshold)
|
||||
{
|
||||
shapeInfo->setParams(collisionRegion.shapeInfo->getType(), collisionRegion.shapeInfo->getHalfExtents(), collisionRegion.modelURL.toString());
|
||||
}
|
||||
|
||||
CollisionRegion(const QVariantMap& pickVariant) {
|
||||
// "loaded" is not deserialized here because there is no way to know if the shape is actually loaded
|
||||
if (pickVariant["shape"].isValid()) {
|
||||
auto shape = pickVariant["shape"].toMap();
|
||||
if (!shape.empty()) {
|
||||
|
@ -287,6 +305,10 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if (pickVariant["threshold"].isValid()) {
|
||||
threshold = glm::max(0.0f, pickVariant["threshold"].toFloat());
|
||||
}
|
||||
|
||||
if (pickVariant["position"].isValid()) {
|
||||
transform.setTranslation(vec3FromVariant(pickVariant["position"]));
|
||||
}
|
||||
|
@ -304,6 +326,9 @@ public:
|
|||
shape["dimensions"] = vec3toVariant(transform.getScale());
|
||||
|
||||
collisionRegion["shape"] = shape;
|
||||
collisionRegion["loaded"] = loaded;
|
||||
|
||||
collisionRegion["threshold"] = threshold;
|
||||
|
||||
collisionRegion["position"] = vec3toVariant(transform.getTranslation());
|
||||
collisionRegion["orientation"] = quatToVariant(transform.getRotation());
|
||||
|
@ -312,13 +337,16 @@ public:
|
|||
}
|
||||
|
||||
operator bool() const override {
|
||||
return !(glm::any(glm::isnan(transform.getTranslation())) ||
|
||||
return !std::isnan(threshold) &&
|
||||
!(glm::any(glm::isnan(transform.getTranslation())) ||
|
||||
glm::any(glm::isnan(transform.getRotation())) ||
|
||||
shapeInfo->getType() == SHAPE_TYPE_NONE);
|
||||
}
|
||||
|
||||
bool operator==(const CollisionRegion& other) const {
|
||||
return glm::all(glm::equal(transform.getTranslation(), other.transform.getTranslation())) &&
|
||||
return loaded == other.loaded &&
|
||||
threshold == other.threshold &&
|
||||
glm::all(glm::equal(transform.getTranslation(), other.transform.getTranslation())) &&
|
||||
glm::all(glm::equal(transform.getRotation(), other.transform.getRotation())) &&
|
||||
glm::all(glm::equal(transform.getScale(), other.transform.getScale())) &&
|
||||
shapeInfo->getType() == other.shapeInfo->getType() &&
|
||||
|
@ -337,11 +365,13 @@ public:
|
|||
}
|
||||
|
||||
// We can't load the model here because it would create a circular dependency, so we delegate that responsibility to the owning CollisionPick
|
||||
bool loaded { false };
|
||||
QUrl modelURL;
|
||||
|
||||
// We can't compute the shapeInfo here without loading the model first, so we delegate that responsibility to the owning CollisionPick
|
||||
std::shared_ptr<ShapeInfo> shapeInfo = std::make_shared<ShapeInfo>();
|
||||
Transform transform;
|
||||
float threshold;
|
||||
};
|
||||
|
||||
namespace std {
|
||||
|
|
18
libraries/shared/src/TransformNode.h
Normal file
18
libraries/shared/src/TransformNode.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// Created by Sabrina Shanman 8/14/2018
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#ifndef hifi_TransformNode_h
|
||||
#define hifi_TransformNode_h
|
||||
|
||||
#include "Transform.h"
|
||||
|
||||
class TransformNode {
|
||||
public:
|
||||
virtual Transform getTransform() = 0;
|
||||
};
|
||||
|
||||
#endif // hifi_TransformNode_h
|
|
@ -156,10 +156,12 @@ bool FileUtils::canCreateFile(const QString& fullPath) {
|
|||
qDebug(shared) << "unable to overwrite file '" << fullPath << "'";
|
||||
return false;
|
||||
}
|
||||
QDir dir(fileInfo.absolutePath());
|
||||
|
||||
QString absolutePath = fileInfo.absolutePath();
|
||||
QDir dir(absolutePath);
|
||||
if (!dir.exists()) {
|
||||
if (!dir.mkpath(fullPath)) {
|
||||
qDebug(shared) << "unable to create dir '" << dir.absolutePath() << "'";
|
||||
if (!dir.mkpath(absolutePath)) {
|
||||
qDebug(shared) << "unable to create dir '" << absolutePath << "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/emote.js"
|
||||
];
|
||||
var DEFAULT_SCRIPTS_SEPARATE = [
|
||||
"system/controllers/controllerScripts.js"
|
||||
"system/controllers/controllerScripts.js",
|
||||
"system/interstitialPage.js"
|
||||
//"system/chat.js"
|
||||
];
|
||||
|
||||
|
|
|
@ -48,6 +48,11 @@ Item {
|
|||
|
||||
|
||||
property var valueMax : 1
|
||||
property var valueMin : 0
|
||||
|
||||
property var displayMinAt0 : true
|
||||
property var _displayMaxValue : 1
|
||||
property var _displayMinValue : 0
|
||||
|
||||
property var _values
|
||||
property var tick : 0
|
||||
|
@ -71,7 +76,9 @@ Item {
|
|||
value: value,
|
||||
fromBinding: isBinding,
|
||||
valueMax: 1,
|
||||
valueMin: 0,
|
||||
numSamplesConstantMax: 0,
|
||||
numSamplesConstantMin: 0,
|
||||
valueHistory: new Array(),
|
||||
label: (plot["label"] !== undefined ? plot["label"] : ""),
|
||||
color: (plot["color"] !== undefined ? plot["color"] : "white"),
|
||||
|
@ -90,6 +97,11 @@ Item {
|
|||
_values[i].valueMax *= 0.25 // Fast reduce the max value as we click
|
||||
}
|
||||
}
|
||||
function resetMin() {
|
||||
for (var i = 0; i < _values.length; i++) {
|
||||
_values[i].valueMin *= 0.25 // Fast reduce the min value as we click
|
||||
}
|
||||
}
|
||||
|
||||
function pullFreshValues() {
|
||||
// Wait until values are created to begin pulling
|
||||
|
@ -99,6 +111,7 @@ Item {
|
|||
tick++;
|
||||
|
||||
var currentValueMax = 0
|
||||
var currentValueMin = 0
|
||||
for (var i = 0; i < _values.length; i++) {
|
||||
|
||||
var currentVal = (+_values[i].object[_values[i].value]) * _values[i].scale;
|
||||
|
@ -112,26 +125,47 @@ Item {
|
|||
_values[i].valueMax *= 0.99
|
||||
_values[i].numSamplesConstantMax = 0
|
||||
}
|
||||
if (lostValue <= _values[i].valueMin) {
|
||||
_values[i].valueMin *= 0.99
|
||||
_values[i].numSamplesConstantMin = 0
|
||||
}
|
||||
}
|
||||
|
||||
if (_values[i].valueMax < currentVal) {
|
||||
_values[i].valueMax = currentVal;
|
||||
_values[i].numSamplesConstantMax = 0
|
||||
}
|
||||
if (_values[i].valueMin > currentVal) {
|
||||
_values[i].valueMin = currentVal;
|
||||
_values[i].numSamplesConstantMin = 0
|
||||
}
|
||||
|
||||
if (_values[i].numSamplesConstantMax > VALUE_HISTORY_SIZE) {
|
||||
_values[i].numSamplesConstantMax = 0
|
||||
_values[i].valueMax *= 0.95 // lower slowly the current max if no new above max since a while
|
||||
}
|
||||
|
||||
if (_values[i].numSamplesConstantMin > VALUE_HISTORY_SIZE) {
|
||||
_values[i].numSamplesConstantMin = 0
|
||||
_values[i].valueMin *= 0.95 // lower slowly the current min if no new above min since a while
|
||||
}
|
||||
|
||||
if (currentValueMax < _values[i].valueMax) {
|
||||
currentValueMax = _values[i].valueMax
|
||||
}
|
||||
if (currentValueMin > _values[i].valueMin) {
|
||||
currentValueMin = _values[i].valueMin
|
||||
}
|
||||
}
|
||||
|
||||
if ((valueMax < currentValueMax) || (tick % VALUE_HISTORY_SIZE == 0)) {
|
||||
valueMax = currentValueMax;
|
||||
}
|
||||
if ((valueMin > currentValueMin) || (tick % VALUE_HISTORY_SIZE == 0)) {
|
||||
valueMin = currentValueMin;
|
||||
}
|
||||
_displayMaxValue = valueMax;
|
||||
_displayMinValue = ( displayMinAt0 ? 0 : valueMin )
|
||||
|
||||
mycanvas.requestPaint()
|
||||
}
|
||||
|
||||
|
@ -152,10 +186,10 @@ Item {
|
|||
}
|
||||
|
||||
function pixelFromVal(val, valScale) {
|
||||
return lineHeight + (height - lineHeight) * (1 - (0.9) * val / valueMax);
|
||||
return lineHeight + (height - lineHeight) * (1 - (0.99) * (val - _displayMinValue) / (_displayMaxValue - _displayMinValue));
|
||||
}
|
||||
function valueFromPixel(pixY) {
|
||||
return ((pixY - lineHeight) / (height - lineHeight) - 1) * valueMax / (-0.9);
|
||||
return _displayMinValue + (((pixY - lineHeight) / (height - lineHeight) - 1) * (_displayMaxValue - _displayMinValue) / (-0.99));
|
||||
}
|
||||
function plotValueHistory(ctx, valHistory, color) {
|
||||
var widthStep= width / (valHistory.length - 1);
|
||||
|
@ -183,8 +217,10 @@ Item {
|
|||
function displayTitle(ctx, text, maxVal) {
|
||||
ctx.fillStyle = "grey";
|
||||
ctx.textAlign = "right";
|
||||
ctx.fillText(displayValue(valueFromPixel(lineHeight), root.valueUnit), width, lineHeight);
|
||||
ctx.fillText("max " + displayValue(_displayMaxValue, root.valueUnit), width, pixelFromVal(_displayMaxValue));
|
||||
|
||||
ctx.fillText("min " + displayValue(_displayMinValue, root.valueUnit), width, pixelFromVal(_displayMinValue));
|
||||
|
||||
ctx.fillStyle = "white";
|
||||
ctx.textAlign = "left";
|
||||
ctx.fillText(text, 0, lineHeight);
|
||||
|
@ -193,15 +229,37 @@ Item {
|
|||
ctx.fillStyle = Qt.rgba(0, 0, 0, root.backgroundOpacity);
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
ctx.strokeStyle= "grey";
|
||||
/* ctx.strokeStyle= "grey";
|
||||
ctx.lineWidth="2";
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, lineHeight + 1);
|
||||
ctx.lineTo(width, lineHeight + 1);
|
||||
ctx.lineTo(width, lineHeight + 1);
|
||||
ctx.moveTo(0, height);
|
||||
ctx.lineTo(width, height);
|
||||
ctx.stroke();*/
|
||||
}
|
||||
|
||||
function displayMaxZeroMin(ctx) {
|
||||
var maxY = pixelFromVal(_displayMaxValue);
|
||||
|
||||
ctx.strokeStyle= "LightSlateGray";
|
||||
ctx.lineWidth="1";
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, maxY);
|
||||
ctx.lineTo(width, maxY);
|
||||
ctx.stroke();
|
||||
|
||||
if (_displayMinValue != 0) {
|
||||
var zeroY = pixelFromVal(0);
|
||||
var minY = pixelFromVal(_displayMinValue);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, zeroY);
|
||||
ctx.lineTo(width, zeroY);
|
||||
ctx.moveTo(0, minY);
|
||||
ctx.lineTo(width, minY);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
var ctx = getContext("2d");
|
||||
|
@ -215,7 +273,9 @@ Item {
|
|||
displayValueLegend(ctx, _values[i], i)
|
||||
}
|
||||
|
||||
displayTitle(ctx, title, valueMax)
|
||||
displayMaxZeroMin(ctx);
|
||||
|
||||
displayTitle(ctx, title, _displayMaxValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,6 +285,7 @@ Item {
|
|||
|
||||
onClicked: {
|
||||
resetMax();
|
||||
resetMin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// RichSlider.qml
|
||||
//
|
||||
// Created by Zach Pomerantz on 2/8/2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4 as Original
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import "qrc:///qml/styles-uit"
|
||||
import "qrc:///qml/controls-uit" as HifiControls
|
||||
|
||||
|
||||
Item {
|
||||
HifiConstants { id: hifi }
|
||||
id: root
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 24
|
||||
|
||||
function defaultGet() { return 0 }
|
||||
function defaultSet(value) { }
|
||||
|
||||
property var labelAreaWidthScale: 0.5
|
||||
property bool integral: false
|
||||
property var numDigits: 2
|
||||
|
||||
property var valueVarSetter: defaultSet
|
||||
property alias valueVar : sliderControl.value
|
||||
|
||||
property alias min: sliderControl.minimumValue
|
||||
property alias max: sliderControl.maximumValue
|
||||
|
||||
property alias label: labelControl.text
|
||||
property bool showLabel: true
|
||||
|
||||
property bool showValue: true
|
||||
|
||||
|
||||
|
||||
signal valueChanged(real value)
|
||||
|
||||
Component.onCompleted: {
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: labelControl
|
||||
text: root.label
|
||||
enabled: root.showLabel
|
||||
anchors.left: root.left
|
||||
width: root.width * root.labelAreaWidthScale
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
}
|
||||
|
||||
HifiControls.Slider {
|
||||
id: sliderControl
|
||||
stepSize: root.integral ? 1.0 : 0.0
|
||||
anchors.left: labelControl.right
|
||||
anchors.right: root.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.top: root.top
|
||||
anchors.topMargin: 0
|
||||
onValueChanged: { root.valueVarSetter(value) }
|
||||
}
|
||||
|
||||
HifiControls.Label {
|
||||
id: labelValue
|
||||
enabled: root.showValue
|
||||
text: sliderControl.value.toFixed(root.integral ? 0 : root.numDigits)
|
||||
anchors.right: labelControl.right
|
||||
anchors.rightMargin: 5
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
}
|
||||
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
ConfigSlider 1.0 ConfigSlider.qml
|
||||
ConfigSlider 1.0 ConfigSlider.qml
|
||||
RichSlider 1.0 RichSlider.qml
|
|
@ -16,15 +16,9 @@
|
|||
var ICON_URL = Script.resolvePath("../../../system/assets/images/lod-i.svg");
|
||||
var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/lod-a.svg");
|
||||
|
||||
var onScreen = false;
|
||||
var onTablet = false; // set this to true to use the tablet, false use a floating window
|
||||
|
||||
function onClicked() {
|
||||
if (onScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
} else {
|
||||
tablet.loadQMLSource(QMLAPP_URL);
|
||||
}
|
||||
}
|
||||
var onAppScreen = false;
|
||||
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
var button = tablet.addButton({
|
||||
|
@ -35,6 +29,50 @@
|
|||
|
||||
var hasEventBridge = false;
|
||||
|
||||
var onScreen = false;
|
||||
var window;
|
||||
|
||||
function onClicked() {
|
||||
if (onTablet) {
|
||||
if (onAppScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
} else {
|
||||
tablet.loadQMLSource(QMLAPP_URL);
|
||||
}
|
||||
} else {
|
||||
if (onScreen) {
|
||||
killWindow()
|
||||
} else {
|
||||
createWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
var qml = Script.resolvePath(QMLAPP_URL);
|
||||
window = Desktop.createWindow(Script.resolvePath(QMLAPP_URL), {
|
||||
title: TABLET_BUTTON_NAME,
|
||||
flags: Desktop.ALWAYS_ON_TOP,
|
||||
presentationMode: Desktop.PresentationMode.NATIVE,
|
||||
size: {x: 400, y: 600}
|
||||
});
|
||||
window.closed.connect(killWindow);
|
||||
window.fromQml.connect(fromQml);
|
||||
onScreen = true
|
||||
button.editProperties({isActive: true});
|
||||
}
|
||||
|
||||
function killWindow() {
|
||||
if (window !== undefined) {
|
||||
window.closed.disconnect(killWindow);
|
||||
window.fromQml.disconnect(fromQml);
|
||||
window.close()
|
||||
window = undefined
|
||||
}
|
||||
onScreen = false
|
||||
button.editProperties({isActive: false})
|
||||
}
|
||||
|
||||
function wireEventBridge(on) {
|
||||
if (!tablet) {
|
||||
print("Warning in wireEventBridge(): 'tablet' undefined!");
|
||||
|
@ -54,23 +92,38 @@
|
|||
}
|
||||
|
||||
function onScreenChanged(type, url) {
|
||||
onScreen = (url === QMLAPP_URL);
|
||||
button.editProperties({isActive: onScreen});
|
||||
wireEventBridge(onScreen);
|
||||
}
|
||||
|
||||
function fromQml(message) {
|
||||
if (onTablet) {
|
||||
onAppScreen = (url === QMLAPP_URL);
|
||||
|
||||
button.editProperties({isActive: onAppScreen});
|
||||
wireEventBridge(onAppScreen);
|
||||
}
|
||||
}
|
||||
|
||||
button.clicked.connect(onClicked);
|
||||
tablet.screenChanged.connect(onScreenChanged);
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
if (onScreen) {
|
||||
killWindow()
|
||||
if (onAppScreen) {
|
||||
tablet.gotoHomeScreen();
|
||||
}
|
||||
button.clicked.disconnect(onClicked);
|
||||
tablet.screenChanged.disconnect(onScreenChanged);
|
||||
tablet.removeButton(button);
|
||||
});
|
||||
|
||||
function fromQml(message) {
|
||||
}
|
||||
|
||||
function sendToQml(message) {
|
||||
if (onTablet) {
|
||||
tablet.sendToQml(message);
|
||||
} else {
|
||||
if (window) {
|
||||
window.sendToQml(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}());
|
||||
|
|
|
@ -10,20 +10,174 @@
|
|||
//
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import "qrc:///qml/styles-uit"
|
||||
import "qrc:///qml/controls-uit" as HifiControls
|
||||
|
||||
import "../lib/plotperf"
|
||||
import "configSlider"
|
||||
|
||||
Item {
|
||||
id: lodIU
|
||||
anchors.fill:parent
|
||||
|
||||
Component.onCompleted: {
|
||||
Render.getConfig("RenderMainView.DrawSceneOctree").showVisibleCells = false
|
||||
Render.getConfig("RenderMainView.DrawSceneOctree").showEmptyCells = false
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
Render.getConfig("RenderMainView.DrawSceneOctree").enabled = false
|
||||
}
|
||||
|
||||
Column {
|
||||
id: topHeader
|
||||
spacing: 8
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
HifiControls.CheckBox {
|
||||
boxSize: 20
|
||||
text: "Show LOD Reticule"
|
||||
checked: Render.getConfig("RenderMainView.DrawSceneOctree").enabled
|
||||
onCheckedChanged: { Render.getConfig("RenderMainView.DrawSceneOctree").enabled = checked }
|
||||
}
|
||||
|
||||
RichSlider {
|
||||
showLabel: true
|
||||
showValue: false
|
||||
label: "World Quality"
|
||||
valueVar: LODManager["worldDetailQuality"]
|
||||
valueVarSetter: (function (v) { LODManager["worldDetailQuality"] = v })
|
||||
max: 0.75
|
||||
min: 0.25
|
||||
integral: false
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
Row {
|
||||
HifiControls.CheckBox {
|
||||
id: autoLOD
|
||||
boxSize: 20
|
||||
text: "Auto LOD"
|
||||
checked: LODManager.automaticLODAdjust
|
||||
onCheckedChanged: { LODManager.automaticLODAdjust = (checked) }
|
||||
}
|
||||
HifiControls.CheckBox {
|
||||
id: showLODRegulatorDetails
|
||||
visible: LODManager.automaticLODAdjust
|
||||
boxSize: 20
|
||||
text: "Show LOD Details"
|
||||
}
|
||||
}
|
||||
|
||||
RichSlider {
|
||||
visible: !LODManager.automaticLODAdjust
|
||||
showLabel: true
|
||||
label: "LOD Angle [deg]"
|
||||
valueVar: LODManager["lodAngleDeg"]
|
||||
valueVarSetter: (function (v) { LODManager["lodAngleDeg"] = v })
|
||||
max: 90.0
|
||||
min: 0.5
|
||||
integral: false
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
Column {
|
||||
id: lodRegulatorDetails
|
||||
visible: LODManager.automaticLODAdjust && showLODRegulatorDetails.checked
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
RichSlider {
|
||||
visible: lodRegulatorDetails.visible
|
||||
showLabel: true
|
||||
label: "LOD Kp"
|
||||
valueVar: LODManager["pidKp"]
|
||||
valueVarSetter: (function (v) { LODManager["pidKp"] = v })
|
||||
max: 2.0
|
||||
min: 0.0
|
||||
integral: false
|
||||
numDigits: 3
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
RichSlider {
|
||||
visible: false && lodRegulatorDetails.visible
|
||||
showLabel: true
|
||||
label: "LOD Ki"
|
||||
valueVar: LODManager["pidKi"]
|
||||
valueVarSetter: (function (v) { LODManager["pidKi"] = v })
|
||||
max: 0.1
|
||||
min: 0.0
|
||||
integral: false
|
||||
numDigits: 8
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
RichSlider {
|
||||
visible: false && lodRegulatorDetails.visible
|
||||
showLabel: true
|
||||
label: "LOD Kd"
|
||||
valueVar: LODManager["pidKd"]
|
||||
valueVarSetter: (function (v) { LODManager["pidKd"] = v })
|
||||
max: 10.0
|
||||
min: 0.0
|
||||
integral: false
|
||||
numDigits: 3
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
RichSlider {
|
||||
visible: lodRegulatorDetails.visible
|
||||
showLabel: true
|
||||
label: "LOD Kv"
|
||||
valueVar: LODManager["pidKv"]
|
||||
valueVarSetter: (function (v) { LODManager["pidKv"] = v })
|
||||
max: 2.0
|
||||
min: 0.0
|
||||
integral: false
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
RichSlider {
|
||||
visible: lodRegulatorDetails.visible
|
||||
showLabel: true
|
||||
label: "LOD Smooth Scale"
|
||||
valueVar: LODManager["smoothScale"]
|
||||
valueVarSetter: (function (v) { LODManager["smoothScale"] = v })
|
||||
max: 20.0
|
||||
min: 1.0
|
||||
integral: true
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: stats
|
||||
spacing: 8
|
||||
anchors.fill:parent
|
||||
spacing: 4
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.top: topHeader.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
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
|
||||
var numPlots = (children.length + (lodRegulatorDetails.visible ? 1 : 0) - 2)
|
||||
return (height - topLine.height - bottomLine.height - spacing * (numPlots - 1)) / (numPlots)
|
||||
}
|
||||
|
||||
Separator {
|
||||
id: topLine
|
||||
}
|
||||
|
||||
PlotPerf {
|
||||
|
@ -38,6 +192,11 @@ Item {
|
|||
label: "present",
|
||||
color: "#FFFF00"
|
||||
},
|
||||
{
|
||||
prop: "batchTime",
|
||||
label: "batch",
|
||||
color: "#00FF00"
|
||||
},
|
||||
{
|
||||
prop: "engineRunTime",
|
||||
label: "engineRun",
|
||||
|
@ -58,35 +217,70 @@ Item {
|
|||
valueUnit: "Hz"
|
||||
plots: [
|
||||
{
|
||||
prop: "lodIncreaseFPS",
|
||||
label: "LOD++",
|
||||
prop: "lodTargetFPS",
|
||||
label: "target",
|
||||
color: "#66FF66"
|
||||
},
|
||||
{
|
||||
prop: "fps",
|
||||
prop: "nowRenderFPS",
|
||||
label: "FPS",
|
||||
color: "#FFFFFF"
|
||||
color: "#FFFF55"
|
||||
},
|
||||
{
|
||||
prop: "lodDecreaseFPS",
|
||||
label: "LOD--",
|
||||
color: "#FF6666"
|
||||
}
|
||||
]
|
||||
}
|
||||
PlotPerf {
|
||||
title: "LOD"
|
||||
height: parent.evalEvenHeight()
|
||||
object: LODManager
|
||||
valueScale: 0.1
|
||||
valueUnit: ""
|
||||
plots: [
|
||||
{
|
||||
prop: "lodLevel",
|
||||
label: "LOD",
|
||||
prop: "smoothRenderFPS",
|
||||
label: "Smooth FPS",
|
||||
color: "#9999FF"
|
||||
}
|
||||
]
|
||||
}
|
||||
PlotPerf {
|
||||
title: "LOD Angle"
|
||||
height: parent.evalEvenHeight()
|
||||
object: LODManager
|
||||
valueScale: 1.0
|
||||
valueUnit: "deg"
|
||||
plots: [
|
||||
{
|
||||
prop: "lodAngleDeg",
|
||||
label: "LOD Angle",
|
||||
color: "#9999FF"
|
||||
}
|
||||
]
|
||||
}
|
||||
PlotPerf {
|
||||
// visible: lodRegulatorDetails.visible
|
||||
title: "PID Output"
|
||||
height: parent.evalEvenHeight()
|
||||
object: LODManager
|
||||
valueScale: 1.0
|
||||
valueUnit: "deg"
|
||||
valueNumDigits: 1
|
||||
displayMinAt0: false
|
||||
plots: [
|
||||
{
|
||||
prop: "pidOp",
|
||||
label: "Op",
|
||||
color: "#9999FF"
|
||||
},
|
||||
{
|
||||
prop: "pidOi",
|
||||
label: "Oi",
|
||||
color: "#FFFFFF"
|
||||
},
|
||||
{
|
||||
prop: "pidOd",
|
||||
label: "Od",
|
||||
color: "#FF6666"
|
||||
},
|
||||
{
|
||||
prop: "pidO",
|
||||
label: "Output",
|
||||
color: "#66FF66"
|
||||
}
|
||||
]
|
||||
}
|
||||
Separator {
|
||||
id: bottomLine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue