mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 20:23:04 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into one
This commit is contained in:
commit
cc582cf2ce
118 changed files with 1750 additions and 562 deletions
|
@ -180,6 +180,7 @@ add_subdirectory(tools)
|
||||||
|
|
||||||
if (BUILD_TESTS)
|
if (BUILD_TESTS)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
add_subdirectory(tests-manual)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_INSTALLER)
|
if (BUILD_INSTALLER)
|
||||||
|
|
|
@ -124,15 +124,14 @@ macro(SETUP_HIFI_TESTCASE)
|
||||||
# This target will also build + run the other test targets using ctest when built.
|
# This target will also build + run the other test targets using ctest when built.
|
||||||
|
|
||||||
add_custom_target(${TEST_TARGET}
|
add_custom_target(${TEST_TARGET}
|
||||||
COMMAND ctest .
|
|
||||||
SOURCES ${TEST_PROJ_SRC_FILES} # display source files under the testcase target
|
SOURCES ${TEST_PROJ_SRC_FILES} # display source files under the testcase target
|
||||||
DEPENDS ${${TEST_PROJ_NAME}_TARGETS})
|
DEPENDS ${${TEST_PROJ_NAME}_TARGETS})
|
||||||
|
|
||||||
set_target_properties(${TEST_TARGET} PROPERTIES
|
set_target_properties(${TEST_TARGET} PROPERTIES
|
||||||
|
FOLDER "Tests"
|
||||||
EXCLUDE_FROM_DEFAULT_BUILD TRUE
|
EXCLUDE_FROM_DEFAULT_BUILD TRUE
|
||||||
EXCLUDE_FROM_ALL TRUE)
|
EXCLUDE_FROM_ALL TRUE)
|
||||||
|
|
||||||
set_target_properties(${TEST_TARGET} PROPERTIES FOLDER "Tests")
|
|
||||||
|
|
||||||
list (APPEND ALL_TEST_TARGETS ${TEST_TARGET})
|
list (APPEND ALL_TEST_TARGETS ${TEST_TARGET})
|
||||||
set(ALL_TEST_TARGETS "${ALL_TEST_TARGETS}" PARENT_SCOPE)
|
set(ALL_TEST_TARGETS "${ALL_TEST_TARGETS}" PARENT_SCOPE)
|
||||||
else ()
|
else ()
|
||||||
|
|
|
@ -20,15 +20,10 @@ import "../fileDialog"
|
||||||
Item {
|
Item {
|
||||||
// Set from OffscreenUi::assetDialog()
|
// Set from OffscreenUi::assetDialog()
|
||||||
property alias dir: assetTableModel.folder
|
property alias dir: assetTableModel.folder
|
||||||
property alias filter: selectionType.filtersString // FIXME: Currently only supports simple filters, "*.xxx".
|
property alias filter: selectionType.filtersString
|
||||||
property int options // Not used.
|
property int options
|
||||||
|
|
||||||
property bool selectDirectory: false
|
property bool selectDirectory: false
|
||||||
|
|
||||||
// Not implemented.
|
|
||||||
//property bool saveDialog: false;
|
|
||||||
//property bool multiSelect: false;
|
|
||||||
|
|
||||||
property bool singleClickNavigate: false
|
property bool singleClickNavigate: false
|
||||||
|
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
|
@ -85,7 +80,6 @@ Item {
|
||||||
size: 28
|
size: 28
|
||||||
width: height
|
width: height
|
||||||
enabled: destination !== ""
|
enabled: destination !== ""
|
||||||
//onClicked: d.navigateHome();
|
|
||||||
onClicked: assetTableModel.folder = destination;
|
onClicked: assetTableModel.folder = destination;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,7 +222,9 @@ Item {
|
||||||
|
|
||||||
function onGetAllMappings(error, map) {
|
function onGetAllMappings(error, map) {
|
||||||
var mappings,
|
var mappings,
|
||||||
fileTypeFilter,
|
fileTypeFilters = [],
|
||||||
|
filterListStart,
|
||||||
|
filterListEnd,
|
||||||
index,
|
index,
|
||||||
path,
|
path,
|
||||||
fileName,
|
fileName,
|
||||||
|
@ -249,7 +245,16 @@ Item {
|
||||||
|
|
||||||
if (error === "") {
|
if (error === "") {
|
||||||
mappings = Object.keys(map);
|
mappings = Object.keys(map);
|
||||||
fileTypeFilter = filter.replace("*", "").toLowerCase();
|
filter = filter.replace(/\s/g, '');
|
||||||
|
filterListStart = filter.indexOf("(");
|
||||||
|
filterListEnd = filter.indexOf(")");
|
||||||
|
if (filterListStart !== -1 && filterListEnd !== -1) {
|
||||||
|
var FIRST_EXTENSION_OFFSET = 2;
|
||||||
|
fileTypeFilters = filter.substring(filterListStart + FIRST_EXTENSION_OFFSET
|
||||||
|
, filterListEnd).toLowerCase().split("*");
|
||||||
|
} else if (filter !== "") {
|
||||||
|
fileTypeFilters[0] = filter.replace("*", "").toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0, length = mappings.length; i < length; i++) {
|
for (i = 0, length = mappings.length; i < length; i++) {
|
||||||
index = mappings[i].lastIndexOf("/");
|
index = mappings[i].lastIndexOf("/");
|
||||||
|
@ -260,7 +265,24 @@ Item {
|
||||||
fileIsDir = false;
|
fileIsDir = false;
|
||||||
isValid = false;
|
isValid = false;
|
||||||
|
|
||||||
if (fileType.toLowerCase() === fileTypeFilter) {
|
if (fileTypeFilters.length > 1) {
|
||||||
|
if (fileTypeFilters.indexOf(fileType.toLowerCase()) !== -1) {
|
||||||
|
if (path === folder) {
|
||||||
|
isValid = !selectDirectory;
|
||||||
|
} else if (path.length > folder.length) {
|
||||||
|
subDirectory = path.slice(folder.length);
|
||||||
|
index = subDirectory.indexOf("/");
|
||||||
|
if (index === subDirectory.lastIndexOf("/")) {
|
||||||
|
fileName = subDirectory.slice(0, index);
|
||||||
|
if (subDirectories.indexOf(fileName) === -1) {
|
||||||
|
fileIsDir = true;
|
||||||
|
isValid = true;
|
||||||
|
subDirectories.push(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (fileType.toLowerCase() === fileTypeFilters[0] || fileTypeFilters.length === 0) {
|
||||||
if (path === folder) {
|
if (path === folder) {
|
||||||
isValid = !selectDirectory;
|
isValid = !selectDirectory;
|
||||||
} else if (path.length > folder.length) {
|
} else if (path.length > folder.length) {
|
||||||
|
|
47
interface/resources/shaders/splashSkybox.frag
Normal file
47
interface/resources/shaders/splashSkybox.frag
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
const vec3 COLOR = vec3(0x00, 0xD8, 0x02) / vec3(0xFF);
|
||||||
|
const float CUTOFF = 0.65;
|
||||||
|
const float NOISE_MULT = 8.0;
|
||||||
|
const float NOISE_POWER = 1.0;
|
||||||
|
|
||||||
|
float noise4D(vec4 p) {
|
||||||
|
return fract(sin(dot(p ,vec4(12.9898,78.233,126.7235, 593.2241))) * 43758.5453);
|
||||||
|
}
|
||||||
|
|
||||||
|
float worley4D(vec4 p) {
|
||||||
|
float r = 3.0;
|
||||||
|
vec4 f = floor(p);
|
||||||
|
vec4 x = fract(p);
|
||||||
|
for(int i = -1; i<=1; i++)
|
||||||
|
{
|
||||||
|
for(int j = -1; j<=1; j++)
|
||||||
|
{
|
||||||
|
for(int k = -1; k<=1; k++)
|
||||||
|
{
|
||||||
|
for (int l = -1; l <= 1; l++) {
|
||||||
|
vec4 q = vec4(float(i),float(j),float(k), float(l));
|
||||||
|
vec4 v = q + vec4(noise4D((q+f)*1.11), noise4D((q+f)*1.14), noise4D((q+f)*1.17), noise4D((q+f)*1.20)) - x;
|
||||||
|
float d = dot(v, v);
|
||||||
|
r = min(r, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sqrt(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec3 mainColor(vec3 direction) {
|
||||||
|
float n = worley4D(vec4(direction * NOISE_MULT, iGlobalTime / 3.0));
|
||||||
|
n = 1.0 - n;
|
||||||
|
n = pow(n, NOISE_POWER);
|
||||||
|
if (n < CUTOFF) {
|
||||||
|
return vec3(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = (n - CUTOFF) / (1.0 - CUTOFF);
|
||||||
|
return COLOR * (1.0 - n);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 getSkyboxColor() {
|
||||||
|
return mainColor(normalize(_normal));
|
||||||
|
}
|
|
@ -144,6 +144,7 @@
|
||||||
#include <trackers/EyeTracker.h>
|
#include <trackers/EyeTracker.h>
|
||||||
#include <avatars-renderer/ScriptAvatar.h>
|
#include <avatars-renderer/ScriptAvatar.h>
|
||||||
#include <RenderableEntityItem.h>
|
#include <RenderableEntityItem.h>
|
||||||
|
#include <procedural/ProceduralSkybox.h>
|
||||||
|
|
||||||
#include "AudioClient.h"
|
#include "AudioClient.h"
|
||||||
#include "audio/AudioScope.h"
|
#include "audio/AudioScope.h"
|
||||||
|
@ -375,7 +376,7 @@ Setting::Handle<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTRE
|
||||||
static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com";
|
static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com";
|
||||||
static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds
|
static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds
|
||||||
static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
|
static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
|
||||||
|
static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin";
|
||||||
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
|
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
|
||||||
|
|
||||||
const std::vector<std::pair<QString, Application::AcceptURLMethod>> Application::_acceptedExtensions {
|
const std::vector<std::pair<QString, Application::AcceptURLMethod>> Application::_acceptedExtensions {
|
||||||
|
@ -1349,10 +1350,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
_glWidget->createContext();
|
_glWidget->createContext();
|
||||||
|
|
||||||
// Create the main thread context, the GPU backend, and the display plugins
|
// Create the main thread context, the GPU backend
|
||||||
initializeGL();
|
initializeGL();
|
||||||
DependencyManager::get<TextureCache>()->setGPUContext(_gpuContext);
|
qCDebug(interfaceapp, "Initialized GL");
|
||||||
qCDebug(interfaceapp, "Initialized Display.");
|
|
||||||
|
// Initialize the display plugin architecture
|
||||||
|
initializeDisplayPlugins();
|
||||||
|
qCDebug(interfaceapp, "Initialized Display");
|
||||||
|
|
||||||
// Create the rendering engine. This can be slow on some machines due to lots of
|
// Create the rendering engine. This can be slow on some machines due to lots of
|
||||||
// GPU pipeline creation.
|
// GPU pipeline creation.
|
||||||
initializeRenderEngine();
|
initializeRenderEngine();
|
||||||
|
@ -2253,8 +2258,11 @@ void Application::updateVerboseLogging() {
|
||||||
}
|
}
|
||||||
bool enable = menu->isOptionChecked(MenuOption::VerboseLogging);
|
bool enable = menu->isOptionChecked(MenuOption::VerboseLogging);
|
||||||
|
|
||||||
QString rules = "*.debug=%1\n"
|
QString rules =
|
||||||
"*.info=%1";
|
"hifi.*.debug=%1\n"
|
||||||
|
"hifi.*.info=%1\n"
|
||||||
|
"hifi.audio-stream.debug=false\n"
|
||||||
|
"hifi.audio-stream.info=false";
|
||||||
rules = rules.arg(enable ? "true" : "false");
|
rules = rules.arg(enable ? "true" : "false");
|
||||||
QLoggingCategory::setFilterRules(rules);
|
QLoggingCategory::setFilterRules(rules);
|
||||||
}
|
}
|
||||||
|
@ -2369,6 +2377,10 @@ void Application::onAboutToQuit() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The active display plugin needs to be loaded before the menu system is active,
|
||||||
|
// so its persisted explicitly here
|
||||||
|
Setting::Handle<QString>{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME }.set(getActiveDisplayPlugin()->getName());
|
||||||
|
|
||||||
getActiveDisplayPlugin()->deactivate();
|
getActiveDisplayPlugin()->deactivate();
|
||||||
if (_autoSwitchDisplayModeSupportedHMDPlugin
|
if (_autoSwitchDisplayModeSupportedHMDPlugin
|
||||||
&& _autoSwitchDisplayModeSupportedHMDPlugin->isSessionActive()) {
|
&& _autoSwitchDisplayModeSupportedHMDPlugin->isSessionActive()) {
|
||||||
|
@ -2608,10 +2620,84 @@ void Application::initializeGL() {
|
||||||
_glWidget->makeCurrent();
|
_glWidget->makeCurrent();
|
||||||
_gpuContext = std::make_shared<gpu::Context>();
|
_gpuContext = std::make_shared<gpu::Context>();
|
||||||
|
|
||||||
|
DependencyManager::get<TextureCache>()->setGPUContext(_gpuContext);
|
||||||
|
|
||||||
// Restore the default main thread context
|
// Restore the default main thread context
|
||||||
_offscreenContext->makeCurrent();
|
_offscreenContext->makeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
updateDisplayMode();
|
static const QString SPLASH_SKYBOX{ "{\"ProceduralEntity\":{ \"version\":2, \"shaderUrl\":\"qrc:///shaders/splashSkybox.frag\" } }" };
|
||||||
|
|
||||||
|
void Application::initializeDisplayPlugins() {
|
||||||
|
auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
|
||||||
|
Setting::Handle<QString> activeDisplayPluginSetting{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME, displayPlugins.at(0)->getName() };
|
||||||
|
auto lastActiveDisplayPluginName = activeDisplayPluginSetting.get();
|
||||||
|
|
||||||
|
auto defaultDisplayPlugin = displayPlugins.at(0);
|
||||||
|
// Once time initialization code
|
||||||
|
DisplayPluginPointer targetDisplayPlugin;
|
||||||
|
foreach(auto displayPlugin, displayPlugins) {
|
||||||
|
displayPlugin->setContext(_gpuContext);
|
||||||
|
if (displayPlugin->getName() == lastActiveDisplayPluginName) {
|
||||||
|
targetDisplayPlugin = displayPlugin;
|
||||||
|
}
|
||||||
|
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged,
|
||||||
|
[this](const QSize& size) { resizeGL(); });
|
||||||
|
QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default display plugin needs to be activated first, otherwise the display plugin thread
|
||||||
|
// may be launched by an external plugin, which is bad
|
||||||
|
setDisplayPlugin(defaultDisplayPlugin);
|
||||||
|
|
||||||
|
// Now set the desired plugin if it's not the same as the default plugin
|
||||||
|
if (targetDisplayPlugin != defaultDisplayPlugin) {
|
||||||
|
setDisplayPlugin(targetDisplayPlugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit a default frame to render until the engine starts up
|
||||||
|
updateRenderArgs(0.0f);
|
||||||
|
|
||||||
|
_offscreenContext->makeCurrent();
|
||||||
|
|
||||||
|
#define ENABLE_SPLASH_FRAME 0
|
||||||
|
#if ENABLE_SPLASH_FRAME
|
||||||
|
{
|
||||||
|
QMutexLocker viewLocker(&_renderArgsMutex);
|
||||||
|
|
||||||
|
if (_appRenderArgs._isStereo) {
|
||||||
|
_gpuContext->enableStereo(true);
|
||||||
|
_gpuContext->setStereoProjections(_appRenderArgs._eyeProjections);
|
||||||
|
_gpuContext->setStereoViews(_appRenderArgs._eyeOffsets);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frame resources
|
||||||
|
auto framebufferCache = DependencyManager::get<FramebufferCache>();
|
||||||
|
gpu::FramebufferPointer finalFramebuffer = framebufferCache->getFramebuffer();
|
||||||
|
std::shared_ptr<ProceduralSkybox> procedural = std::make_shared<ProceduralSkybox>();
|
||||||
|
procedural->parse(SPLASH_SKYBOX);
|
||||||
|
|
||||||
|
_gpuContext->beginFrame(_appRenderArgs._view, _appRenderArgs._headPose);
|
||||||
|
gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) {
|
||||||
|
batch.resetStages();
|
||||||
|
batch.enableStereo(false);
|
||||||
|
batch.setFramebuffer(finalFramebuffer);
|
||||||
|
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, { 0, 0, 0, 1 });
|
||||||
|
batch.enableSkybox(true);
|
||||||
|
batch.enableStereo(_appRenderArgs._isStereo);
|
||||||
|
batch.setViewportTransform({ 0, 0, finalFramebuffer->getSize() });
|
||||||
|
procedural->render(batch, _appRenderArgs._renderArgs.getViewFrustum());
|
||||||
|
});
|
||||||
|
auto frame = _gpuContext->endFrame();
|
||||||
|
frame->frameIndex = 0;
|
||||||
|
frame->framebuffer = finalFramebuffer;
|
||||||
|
frame->pose = _appRenderArgs._headPose;
|
||||||
|
frame->framebufferRecycler = [framebufferCache, procedural](const gpu::FramebufferPointer& framebuffer) {
|
||||||
|
framebufferCache->releaseFramebuffer(framebuffer);
|
||||||
|
};
|
||||||
|
_displayPlugin->submitFrame(frame);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::initializeRenderEngine() {
|
void Application::initializeRenderEngine() {
|
||||||
|
@ -2635,6 +2721,7 @@ void Application::initializeRenderEngine() {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void setupPreferences();
|
extern void setupPreferences();
|
||||||
|
static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, bool active);
|
||||||
|
|
||||||
void Application::initializeUi() {
|
void Application::initializeUi() {
|
||||||
// Build a shared canvas / context for the Chromium processes
|
// Build a shared canvas / context for the Chromium processes
|
||||||
|
@ -2776,10 +2863,25 @@ void Application::initializeUi() {
|
||||||
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
|
offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1);
|
||||||
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
|
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
|
||||||
|
|
||||||
// Now that the menu is instantiated, ensure the display plugin menu is properly updated
|
|
||||||
updateDisplayMode();
|
|
||||||
flushMenuUpdates();
|
flushMenuUpdates();
|
||||||
|
|
||||||
|
// Now that the menu is instantiated, ensure the display plugin menu is properly updated
|
||||||
|
{
|
||||||
|
auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
|
||||||
|
// first sort the plugins into groupings: standard, advanced, developer
|
||||||
|
std::stable_sort(displayPlugins.begin(), displayPlugins.end(),
|
||||||
|
[](const DisplayPluginPointer& a, const DisplayPluginPointer& b)->bool { return a->getGrouping() < b->getGrouping(); });
|
||||||
|
|
||||||
|
// concatenate the groupings into a single list in the order: standard, advanced, developer
|
||||||
|
for(const auto& displayPlugin : displayPlugins) {
|
||||||
|
addDisplayPluginToMenu(displayPlugin, _displayPlugin == displayPlugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// after all plugins have been added to the menu, add a separator to the menu
|
||||||
|
auto parent = getPrimaryMenu()->getMenu(MenuOption::OutputMenu);
|
||||||
|
parent->addSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
// The display plugins are created before the menu now, so we need to do this here to hide the menu bar
|
// The display plugins are created before the menu now, so we need to do this here to hide the menu bar
|
||||||
// now that it exists
|
// now that it exists
|
||||||
if (_window && _window->isFullScreen()) {
|
if (_window && _window->isFullScreen()) {
|
||||||
|
@ -2930,7 +3032,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
||||||
_thirdPersonHMDCameraBoomValid = false;
|
_thirdPersonHMDCameraBoomValid = false;
|
||||||
|
|
||||||
_myCamera.setOrientation(myAvatar->getHead()->getOrientation());
|
_myCamera.setOrientation(myAvatar->getHead()->getOrientation());
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
|
if (isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||||
+ _myCamera.getOrientation() * boomOffset);
|
+ _myCamera.getOrientation() * boomOffset);
|
||||||
}
|
}
|
||||||
|
@ -5732,6 +5834,32 @@ void Application::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updateRenderArgs(deltaTime);
|
||||||
|
|
||||||
|
// HACK
|
||||||
|
// load the view frustum
|
||||||
|
// FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering.
|
||||||
|
// Then we can move this logic into the Avatar::simulate call.
|
||||||
|
myAvatar->preDisplaySide(&_appRenderArgs._renderArgs);
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
PerformanceTimer perfTimer("limitless");
|
||||||
|
AnimDebugDraw::getInstance().update();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PerformanceTimer perfTimer("limitless");
|
||||||
|
DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over
|
||||||
|
PerformanceTimer perfTimer("enqueueFrame");
|
||||||
|
getMain3DScene()->enqueueFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::updateRenderArgs(float deltaTime) {
|
||||||
editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) {
|
editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) {
|
||||||
PerformanceTimer perfTimer("editRenderArgs");
|
PerformanceTimer perfTimer("editRenderArgs");
|
||||||
appRenderArgs._headPose = getHMDSensorPose();
|
appRenderArgs._headPose = getHMDSensorPose();
|
||||||
|
@ -5755,9 +5883,9 @@ void Application::update(float deltaTime) {
|
||||||
QMutexLocker viewLocker(&_viewMutex);
|
QMutexLocker viewLocker(&_viewMutex);
|
||||||
// adjust near clip plane to account for sensor scaling.
|
// adjust near clip plane to account for sensor scaling.
|
||||||
auto adjustedProjection = glm::perspective(glm::radians(_fieldOfView.get()),
|
auto adjustedProjection = glm::perspective(glm::radians(_fieldOfView.get()),
|
||||||
getActiveDisplayPlugin()->getRecommendedAspectRatio(),
|
getActiveDisplayPlugin()->getRecommendedAspectRatio(),
|
||||||
DEFAULT_NEAR_CLIP * sensorToWorldScale,
|
DEFAULT_NEAR_CLIP * sensorToWorldScale,
|
||||||
DEFAULT_FAR_CLIP);
|
DEFAULT_FAR_CLIP);
|
||||||
_viewFrustum.setProjection(adjustedProjection);
|
_viewFrustum.setProjection(adjustedProjection);
|
||||||
_viewFrustum.calculate();
|
_viewFrustum.calculate();
|
||||||
}
|
}
|
||||||
|
@ -5773,8 +5901,14 @@ void Application::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(render, "/resizeGL");
|
PROFILE_RANGE(render, "/resizeGL");
|
||||||
PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings));
|
bool showWarnings = false;
|
||||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
bool suppressShortTimings = false;
|
||||||
|
auto menu = Menu::getInstance();
|
||||||
|
if (menu) {
|
||||||
|
suppressShortTimings = menu->isOptionChecked(MenuOption::SuppressShortTimings);
|
||||||
|
showWarnings = menu->isOptionChecked(MenuOption::PipelineWarnings);
|
||||||
|
}
|
||||||
|
PerformanceWarning::setSuppressShortTimings(suppressShortTimings);
|
||||||
PerformanceWarning warn(showWarnings, "Application::paintGL()");
|
PerformanceWarning warn(showWarnings, "Application::paintGL()");
|
||||||
resizeGL();
|
resizeGL();
|
||||||
}
|
}
|
||||||
|
@ -5830,12 +5964,6 @@ void Application::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK
|
|
||||||
// load the view frustum
|
|
||||||
// FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering.
|
|
||||||
// Then we can move this logic into the Avatar::simulate call.
|
|
||||||
myAvatar->preDisplaySide(&appRenderArgs._renderArgs);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
QMutexLocker viewLocker(&_viewMutex);
|
QMutexLocker viewLocker(&_viewMutex);
|
||||||
_myCamera.loadViewFrustum(_displayViewFrustum);
|
_myCamera.loadViewFrustum(_displayViewFrustum);
|
||||||
|
@ -5847,21 +5975,6 @@ void Application::update(float deltaTime) {
|
||||||
appRenderArgs._renderArgs.setViewFrustum(_displayViewFrustum);
|
appRenderArgs._renderArgs.setViewFrustum(_displayViewFrustum);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
|
||||||
PerformanceTimer perfTimer("limitless");
|
|
||||||
AnimDebugDraw::getInstance().update();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
PerformanceTimer perfTimer("limitless");
|
|
||||||
DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
{ // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over
|
|
||||||
PerformanceTimer perfTimer("enqueueFrame");
|
|
||||||
getMain3DScene()->enqueueFrame();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::queryAvatars() {
|
void Application::queryAvatars() {
|
||||||
|
@ -7502,15 +7615,19 @@ void Application::shareSnapshot(const QString& path, const QUrl& href) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float Application::getRenderResolutionScale() const {
|
float Application::getRenderResolutionScale() const {
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) {
|
auto menu = Menu::getInstance();
|
||||||
|
if (!menu) {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionTwoThird)) {
|
}
|
||||||
|
if (menu->isOptionChecked(MenuOption::RenderResolutionOne)) {
|
||||||
|
return 1.0f;
|
||||||
|
} else if (menu->isOptionChecked(MenuOption::RenderResolutionTwoThird)) {
|
||||||
return 0.666f;
|
return 0.666f;
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionHalf)) {
|
} else if (menu->isOptionChecked(MenuOption::RenderResolutionHalf)) {
|
||||||
return 0.5f;
|
return 0.5f;
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionThird)) {
|
} else if (menu->isOptionChecked(MenuOption::RenderResolutionThird)) {
|
||||||
return 0.333f;
|
return 0.333f;
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionQuarter)) {
|
} else if (menu->isOptionChecked(MenuOption::RenderResolutionQuarter)) {
|
||||||
return 0.25f;
|
return 0.25f;
|
||||||
} else {
|
} else {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
@ -7734,7 +7851,7 @@ DisplayPluginPointer Application::getActiveDisplayPlugin() const {
|
||||||
|
|
||||||
static const char* EXCLUSION_GROUP_KEY = "exclusionGroup";
|
static const char* EXCLUSION_GROUP_KEY = "exclusionGroup";
|
||||||
|
|
||||||
static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
|
static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, bool active) {
|
||||||
auto menu = Menu::getInstance();
|
auto menu = Menu::getInstance();
|
||||||
QString name = displayPlugin->getName();
|
QString name = displayPlugin->getName();
|
||||||
auto grouping = displayPlugin->getGrouping();
|
auto grouping = displayPlugin->getGrouping();
|
||||||
|
@ -7779,65 +7896,12 @@ void Application::updateDisplayMode() {
|
||||||
qFatal("Attempted to switch display plugins from a non-main thread");
|
qFatal("Attempted to switch display plugins from a non-main thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins();
|
|
||||||
|
|
||||||
// Once time initialization code
|
|
||||||
static std::once_flag once;
|
|
||||||
std::call_once(once, [&] {
|
|
||||||
foreach(auto displayPlugin, displayPlugins) {
|
|
||||||
displayPlugin->setContext(_gpuContext);
|
|
||||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged,
|
|
||||||
[this](const QSize& size) { resizeGL(); });
|
|
||||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Once time initialization code that depends on the UI being available
|
// Once time initialization code that depends on the UI being available
|
||||||
auto menu = Menu::getInstance();
|
auto displayPlugins = getDisplayPlugins();
|
||||||
if (menu) {
|
|
||||||
static std::once_flag onceUi;
|
|
||||||
std::call_once(onceUi, [&] {
|
|
||||||
bool first = true;
|
|
||||||
|
|
||||||
// first sort the plugins into groupings: standard, advanced, developer
|
|
||||||
DisplayPluginList standard;
|
|
||||||
DisplayPluginList advanced;
|
|
||||||
DisplayPluginList developer;
|
|
||||||
foreach(auto displayPlugin, displayPlugins) {
|
|
||||||
displayPlugin->setContext(_gpuContext);
|
|
||||||
auto grouping = displayPlugin->getGrouping();
|
|
||||||
switch (grouping) {
|
|
||||||
case Plugin::ADVANCED:
|
|
||||||
advanced.push_back(displayPlugin);
|
|
||||||
break;
|
|
||||||
case Plugin::DEVELOPER:
|
|
||||||
developer.push_back(displayPlugin);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
standard.push_back(displayPlugin);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// concatenate the groupings into a single list in the order: standard, advanced, developer
|
|
||||||
standard.insert(std::end(standard), std::begin(advanced), std::end(advanced));
|
|
||||||
standard.insert(std::end(standard), std::begin(developer), std::end(developer));
|
|
||||||
|
|
||||||
foreach(auto displayPlugin, standard) {
|
|
||||||
addDisplayPluginToMenu(displayPlugin, first);
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// after all plugins have been added to the menu, add a separator to the menu
|
|
||||||
auto parent = menu->getMenu(MenuOption::OutputMenu);
|
|
||||||
parent->addSeparator();
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Default to the first item on the list, in case none of the menu items match
|
// Default to the first item on the list, in case none of the menu items match
|
||||||
DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0);
|
DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0);
|
||||||
|
auto menu = getPrimaryMenu();
|
||||||
if (menu) {
|
if (menu) {
|
||||||
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
|
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
|
||||||
QString name = displayPlugin->getName();
|
QString name = displayPlugin->getName();
|
||||||
|
@ -7861,6 +7925,14 @@ void Application::updateDisplayMode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
|
void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
|
||||||
|
if (newDisplayPlugin == _displayPlugin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME don't have the application directly set the state of the UI,
|
||||||
|
// instead emit a signal that the display plugin is changing and let
|
||||||
|
// the desktop lock itself. Reduces coupling between the UI and display
|
||||||
|
// plugins
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
auto desktop = offscreenUi->getDesktop();
|
auto desktop = offscreenUi->getDesktop();
|
||||||
auto menu = Menu::getInstance();
|
auto menu = Menu::getInstance();
|
||||||
|
@ -7871,8 +7943,8 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
|
||||||
bool wasRepositionLocked = false;
|
bool wasRepositionLocked = false;
|
||||||
if (desktop) {
|
if (desktop) {
|
||||||
// Tell the desktop to no reposition (which requires plugin info), until we have set the new plugin, below.
|
// Tell the desktop to no reposition (which requires plugin info), until we have set the new plugin, below.
|
||||||
wasRepositionLocked = offscreenUi->getDesktop()->property("repositionLocked").toBool();
|
wasRepositionLocked = desktop->property("repositionLocked").toBool();
|
||||||
offscreenUi->getDesktop()->setProperty("repositionLocked", true);
|
desktop->setProperty("repositionLocked", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_displayPlugin) {
|
if (_displayPlugin) {
|
||||||
|
|
|
@ -148,6 +148,7 @@ public:
|
||||||
Q_INVOKABLE QString getUserAgent();
|
Q_INVOKABLE QString getUserAgent();
|
||||||
|
|
||||||
void initializeGL();
|
void initializeGL();
|
||||||
|
void initializeDisplayPlugins();
|
||||||
void initializeRenderEngine();
|
void initializeRenderEngine();
|
||||||
void initializeUi();
|
void initializeUi();
|
||||||
|
|
||||||
|
@ -671,6 +672,7 @@ private:
|
||||||
|
|
||||||
using RenderArgsEditor = std::function <void (AppRenderArgs&)>;
|
using RenderArgsEditor = std::function <void (AppRenderArgs&)>;
|
||||||
void editRenderArgs(RenderArgsEditor editor);
|
void editRenderArgs(RenderArgsEditor editor);
|
||||||
|
void updateRenderArgs(float deltaTime);
|
||||||
|
|
||||||
|
|
||||||
Overlays _overlays;
|
Overlays _overlays;
|
||||||
|
|
|
@ -13,8 +13,9 @@
|
||||||
|
|
||||||
#include <display-plugins/CompositorHelper.h>
|
#include <display-plugins/CompositorHelper.h>
|
||||||
#include <FramebufferCache.h>
|
#include <FramebufferCache.h>
|
||||||
#include "ui/Stats.h"
|
#include <plugins/PluginManager.h>
|
||||||
#include <SceneScriptingInterface.h>
|
#include <SceneScriptingInterface.h>
|
||||||
|
#include "ui/Stats.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -233,3 +234,4 @@ void Application::runRenderFrame(RenderArgs* renderArgs) {
|
||||||
_renderEngine->run();
|
_renderEngine->run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2039,7 +2039,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
|
void MyAvatar::preDisplaySide(const RenderArgs* renderArgs) {
|
||||||
|
|
||||||
// toggle using the cauterizedBones depending on where the camera is and the rendering pass type.
|
// toggle using the cauterizedBones depending on where the camera is and the rendering pass type.
|
||||||
const bool shouldDrawHead = shouldRenderHead(renderArgs);
|
const bool shouldDrawHead = shouldRenderHead(renderArgs);
|
||||||
|
|
|
@ -272,7 +272,7 @@ public:
|
||||||
|
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
virtual void postUpdate(float deltaTime, const render::ScenePointer& scene) override;
|
virtual void postUpdate(float deltaTime, const render::ScenePointer& scene) override;
|
||||||
void preDisplaySide(RenderArgs* renderArgs);
|
void preDisplaySide(const RenderArgs* renderArgs);
|
||||||
|
|
||||||
const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; }
|
const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; }
|
||||||
const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; }
|
const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; }
|
||||||
|
|
|
@ -12,5 +12,4 @@
|
||||||
#include "AudioLogging.h"
|
#include "AudioLogging.h"
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(audio, "hifi.audio")
|
Q_LOGGING_CATEGORY(audio, "hifi.audio")
|
||||||
Q_LOGGING_CATEGORY(audiostream, "hifi.audio-stream", QtWarningMsg)
|
Q_LOGGING_CATEGORY(audiostream, "hifi.audio-stream")
|
||||||
|
|
||||||
|
|
|
@ -336,9 +336,8 @@ void OpenGLDisplayPlugin::deactivate() {
|
||||||
|
|
||||||
_container->showDisplayPluginsTools(false);
|
_container->showDisplayPluginsTools(false);
|
||||||
if (!_container->currentDisplayActions().isEmpty()) {
|
if (!_container->currentDisplayActions().isEmpty()) {
|
||||||
auto menu = _container->getPrimaryMenu();
|
|
||||||
foreach(auto itemInfo, _container->currentDisplayActions()) {
|
foreach(auto itemInfo, _container->currentDisplayActions()) {
|
||||||
menu->removeMenuItem(itemInfo.first, itemInfo.second);
|
_container->removeMenuItem(itemInfo.first, itemInfo.second);
|
||||||
}
|
}
|
||||||
_container->currentDisplayActions().clear();
|
_container->currentDisplayActions().clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,6 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type,
|
||||||
_myAvatar->updateAvatarEntity(entityItemID, binaryProperties);
|
_myAvatar->updateAvatarEntity(entityItemID, binaryProperties);
|
||||||
|
|
||||||
entity->setLastBroadcast(usecTimestampNow());
|
entity->setLastBroadcast(usecTimestampNow());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,10 +84,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
|
||||||
EntityTreePointer entityTree,
|
EntityTreePointer entityTree,
|
||||||
EntityItemID entityItemID,
|
EntityItemID entityItemID,
|
||||||
const EntityItemProperties& properties) {
|
const EntityItemProperties& properties) {
|
||||||
if (!_shouldSend) {
|
|
||||||
return; // bail early
|
|
||||||
}
|
|
||||||
|
|
||||||
if (properties.getClientOnly() && properties.getOwningAvatarID() == _myAvatar->getID()) {
|
if (properties.getClientOnly() && properties.getOwningAvatarID() == _myAvatar->getID()) {
|
||||||
// this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server
|
// this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server
|
||||||
queueEditAvatarEntityMessage(type, entityTree, entityItemID, properties);
|
queueEditAvatarEntityMessage(type, entityTree, entityItemID, properties);
|
||||||
|
@ -147,9 +142,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityItemID) {
|
void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityItemID) {
|
||||||
if (!_shouldSend) {
|
|
||||||
return; // bail early
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case this was a clientOnly entity:
|
// in case this was a clientOnly entity:
|
||||||
if(_myAvatar) {
|
if(_myAvatar) {
|
||||||
|
|
|
@ -103,12 +103,15 @@ void OffscreenGLCanvas::onMessageLogged(const QOpenGLDebugMessage& debugMessage)
|
||||||
|
|
||||||
bool OffscreenGLCanvas::makeCurrent() {
|
bool OffscreenGLCanvas::makeCurrent() {
|
||||||
bool result = _context->makeCurrent(_offscreenSurface);
|
bool result = _context->makeCurrent(_offscreenSurface);
|
||||||
std::call_once(_reportOnce, []{
|
if (glGetString) {
|
||||||
qCDebug(glLogging) << "GL Version: " << QString((const char*) glGetString(GL_VERSION));
|
std::call_once(_reportOnce, [] {
|
||||||
qCDebug(glLogging) << "GL Shader Language Version: " << QString((const char*) glGetString(GL_SHADING_LANGUAGE_VERSION));
|
qCDebug(glLogging) << "GL Version: " << QString((const char*)glGetString(GL_VERSION));
|
||||||
qCDebug(glLogging) << "GL Vendor: " << QString((const char*) glGetString(GL_VENDOR));
|
qCDebug(glLogging) << "GL Shader Language Version: "
|
||||||
qCDebug(glLogging) << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
|
<< QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||||
});
|
qCDebug(glLogging) << "GL Vendor: " << QString((const char*)glGetString(GL_VENDOR));
|
||||||
|
qCDebug(glLogging) << "GL Renderer: " << QString((const char*)glGetString(GL_RENDERER));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
117
libraries/networking/src/HMACAuth.cpp
Normal file
117
libraries/networking/src/HMACAuth.cpp
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
//
|
||||||
|
// HMACAuth.cpp
|
||||||
|
// libraries/networking/src
|
||||||
|
//
|
||||||
|
// Created by Simon Walton on 3/19/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 "HMACAuth.h"
|
||||||
|
|
||||||
|
#include <openssl/opensslv.h>
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
|
||||||
|
#include <QUuid>
|
||||||
|
#include "NetworkLogging.h"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000
|
||||||
|
HMACAuth::HMACAuth(AuthMethod authMethod)
|
||||||
|
: _hmacContext(HMAC_CTX_new())
|
||||||
|
, _authMethod(authMethod) { }
|
||||||
|
|
||||||
|
HMACAuth::~HMACAuth()
|
||||||
|
{
|
||||||
|
HMAC_CTX_free(_hmacContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
HMACAuth::HMACAuth(AuthMethod authMethod)
|
||||||
|
: _hmacContext(new HMAC_CTX())
|
||||||
|
, _authMethod(authMethod) {
|
||||||
|
HMAC_CTX_init(_hmacContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
HMACAuth::~HMACAuth() {
|
||||||
|
HMAC_CTX_cleanup(_hmacContext);
|
||||||
|
delete _hmacContext;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool HMACAuth::setKey(const char* keyValue, int keyLen) {
|
||||||
|
const EVP_MD* sslStruct = nullptr;
|
||||||
|
|
||||||
|
switch (_authMethod) {
|
||||||
|
case MD5:
|
||||||
|
sslStruct = EVP_md5();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHA1:
|
||||||
|
sslStruct = EVP_sha1();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHA224:
|
||||||
|
sslStruct = EVP_sha224();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SHA256:
|
||||||
|
sslStruct = EVP_sha256();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RIPEMD160:
|
||||||
|
sslStruct = EVP_ripemd160();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker lock(&_lock);
|
||||||
|
return (bool) HMAC_Init_ex(_hmacContext, keyValue, keyLen, sslStruct, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HMACAuth::setKey(const QUuid& uidKey) {
|
||||||
|
const QByteArray rfcBytes(uidKey.toRfc4122());
|
||||||
|
return setKey(rfcBytes.constData(), rfcBytes.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HMACAuth::addData(const char* data, int dataLen) {
|
||||||
|
QMutexLocker lock(&_lock);
|
||||||
|
return (bool) HMAC_Update(_hmacContext, reinterpret_cast<const unsigned char*>(data), dataLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
HMACAuth::HMACHash HMACAuth::result() {
|
||||||
|
HMACHash hashValue(EVP_MAX_MD_SIZE);
|
||||||
|
unsigned int hashLen;
|
||||||
|
QMutexLocker lock(&_lock);
|
||||||
|
|
||||||
|
auto hmacResult = HMAC_Final(_hmacContext, &hashValue[0], &hashLen);
|
||||||
|
|
||||||
|
if (hmacResult) {
|
||||||
|
hashValue.resize((size_t)hashLen);
|
||||||
|
} else {
|
||||||
|
// the HMAC_FINAL call failed - should not be possible to get into this state
|
||||||
|
qCWarning(networking) << "Error occured calling HMAC_Final";
|
||||||
|
assert(hmacResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear state for possible reuse.
|
||||||
|
HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
|
||||||
|
return hashValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HMACAuth::calculateHash(HMACHash& hashResult, const char* data, int dataLen) {
|
||||||
|
QMutexLocker lock(&_lock);
|
||||||
|
if (!addData(data, dataLen)) {
|
||||||
|
qCWarning(networking) << "Error occured calling HMACAuth::addData()";
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hashResult = result();
|
||||||
|
return true;
|
||||||
|
}
|
47
libraries/networking/src/HMACAuth.h
Normal file
47
libraries/networking/src/HMACAuth.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// HMACAuth.h
|
||||||
|
// libraries/networking/src
|
||||||
|
//
|
||||||
|
// Created by Simon Walton on 3/19/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_HMACAuth_h
|
||||||
|
#define hifi_HMACAuth_h
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <QtCore/QMutex>
|
||||||
|
|
||||||
|
class QUuid;
|
||||||
|
|
||||||
|
class HMACAuth {
|
||||||
|
public:
|
||||||
|
enum AuthMethod { MD5, SHA1, SHA224, SHA256, RIPEMD160 };
|
||||||
|
using HMACHash = std::vector<unsigned char>;
|
||||||
|
|
||||||
|
explicit HMACAuth(AuthMethod authMethod = MD5);
|
||||||
|
~HMACAuth();
|
||||||
|
|
||||||
|
bool setKey(const char* keyValue, int keyLen);
|
||||||
|
bool setKey(const QUuid& uidKey);
|
||||||
|
// Calculate complete hash in one.
|
||||||
|
bool calculateHash(HMACHash& hashResult, const char* data, int dataLen);
|
||||||
|
|
||||||
|
// Append to data to be hashed.
|
||||||
|
bool addData(const char* data, int dataLen);
|
||||||
|
// Get the resulting hash from calls to addData().
|
||||||
|
// Note that only one hash may be calculated at a time for each
|
||||||
|
// HMACAuth instance if this interface is used.
|
||||||
|
HMACHash result();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMutex _lock { QMutex::Recursive };
|
||||||
|
struct hmac_ctx_st* _hmacContext;
|
||||||
|
AuthMethod _authMethod;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_HMACAuth_h
|
|
@ -36,6 +36,7 @@
|
||||||
#include "HifiSockAddr.h"
|
#include "HifiSockAddr.h"
|
||||||
#include "NetworkLogging.h"
|
#include "NetworkLogging.h"
|
||||||
#include "udt/Packet.h"
|
#include "udt/Packet.h"
|
||||||
|
#include "HMACAuth.h"
|
||||||
|
|
||||||
static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
|
static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
|
||||||
|
|
||||||
|
@ -332,15 +333,20 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
||||||
if (verifiedPacket && !ignoreVerification) {
|
if (verifiedPacket && !ignoreVerification) {
|
||||||
|
|
||||||
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
|
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
|
||||||
QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
|
QByteArray expectedHash;
|
||||||
|
auto sourceNodeHMACAuth = sourceNode->getAuthenticateHash();
|
||||||
|
if (sourceNode->getAuthenticateHash()) {
|
||||||
|
expectedHash = NLPacket::hashForPacketAndHMAC(packet, *sourceNodeHMACAuth);
|
||||||
|
}
|
||||||
|
|
||||||
// check if the md5 hash in the header matches the hash we would expect
|
// check if the HMAC-md5 hash in the header matches the hash we would expect
|
||||||
if (packetHeaderHash != expectedHash) {
|
if (!sourceNodeHMACAuth || packetHeaderHash != expectedHash) {
|
||||||
static QMultiMap<QUuid, PacketType> hashDebugSuppressMap;
|
static QMultiMap<QUuid, PacketType> hashDebugSuppressMap;
|
||||||
|
|
||||||
if (!hashDebugSuppressMap.contains(sourceID, headerType)) {
|
if (!hashDebugSuppressMap.contains(sourceID, headerType)) {
|
||||||
qCDebug(networking) << packetHeaderHash << expectedHash;
|
|
||||||
qCDebug(networking) << "Packet hash mismatch on" << headerType << "- Sender" << sourceID;
|
qCDebug(networking) << "Packet hash mismatch on" << headerType << "- Sender" << sourceID;
|
||||||
|
qCDebug(networking) << "Packet len:" << packet.getDataSize() << "Expected hash:" <<
|
||||||
|
expectedHash.toHex() << "Actual:" << packetHeaderHash.toHex();
|
||||||
|
|
||||||
hashDebugSuppressMap.insert(sourceID, headerType);
|
hashDebugSuppressMap.insert(sourceID, headerType);
|
||||||
}
|
}
|
||||||
|
@ -372,15 +378,15 @@ void LimitedNodeList::collectPacketStats(const NLPacket& packet) {
|
||||||
_numCollectedBytes += packet.getDataSize();
|
_numCollectedBytes += packet.getDataSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret) {
|
void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth) {
|
||||||
if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) {
|
if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) {
|
||||||
packet.writeSourceID(getSessionLocalID());
|
packet.writeSourceID(getSessionLocalID());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!connectionSecret.isNull()
|
if (hmacAuth
|
||||||
&& !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
|
&& !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
|
||||||
&& !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
|
&& !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
|
||||||
packet.writeVerificationHashGivenSecret(connectionSecret);
|
packet.writeVerificationHash(*hmacAuth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,17 +402,17 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node&
|
||||||
emit dataSent(destinationNode.getType(), packet.getDataSize());
|
emit dataSent(destinationNode.getType(), packet.getDataSize());
|
||||||
destinationNode.recordBytesSent(packet.getDataSize());
|
destinationNode.recordBytesSent(packet.getDataSize());
|
||||||
|
|
||||||
return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret());
|
return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getAuthenticateHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
|
qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
|
||||||
const QUuid& connectionSecret) {
|
HMACAuth* hmacAuth) {
|
||||||
Q_ASSERT(!packet.isPartOfMessage());
|
Q_ASSERT(!packet.isPartOfMessage());
|
||||||
Q_ASSERT_X(!packet.isReliable(), "LimitedNodeList::sendUnreliablePacket",
|
Q_ASSERT_X(!packet.isReliable(), "LimitedNodeList::sendUnreliablePacket",
|
||||||
"Trying to send a reliable packet unreliably.");
|
"Trying to send a reliable packet unreliably.");
|
||||||
|
|
||||||
collectPacketStats(packet);
|
collectPacketStats(packet);
|
||||||
fillPacketHeader(packet, connectionSecret);
|
fillPacketHeader(packet, hmacAuth);
|
||||||
|
|
||||||
return _nodeSocket.writePacket(packet, sockAddr);
|
return _nodeSocket.writePacket(packet, sockAddr);
|
||||||
}
|
}
|
||||||
|
@ -419,7 +425,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
|
||||||
emit dataSent(destinationNode.getType(), packet->getDataSize());
|
emit dataSent(destinationNode.getType(), packet->getDataSize());
|
||||||
destinationNode.recordBytesSent(packet->getDataSize());
|
destinationNode.recordBytesSent(packet->getDataSize());
|
||||||
|
|
||||||
return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret());
|
return sendPacket(std::move(packet), *activeSocket, destinationNode.getAuthenticateHash());
|
||||||
} else {
|
} else {
|
||||||
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
|
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
|
||||||
return ERROR_SENDING_PACKET_BYTES;
|
return ERROR_SENDING_PACKET_BYTES;
|
||||||
|
@ -427,18 +433,18 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
|
qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
|
||||||
const QUuid& connectionSecret) {
|
HMACAuth* hmacAuth) {
|
||||||
Q_ASSERT(!packet->isPartOfMessage());
|
Q_ASSERT(!packet->isPartOfMessage());
|
||||||
if (packet->isReliable()) {
|
if (packet->isReliable()) {
|
||||||
collectPacketStats(*packet);
|
collectPacketStats(*packet);
|
||||||
fillPacketHeader(*packet, connectionSecret);
|
fillPacketHeader(*packet, hmacAuth);
|
||||||
|
|
||||||
auto size = packet->getDataSize();
|
auto size = packet->getDataSize();
|
||||||
_nodeSocket.writePacket(std::move(packet), sockAddr);
|
_nodeSocket.writePacket(std::move(packet), sockAddr);
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
} else {
|
} else {
|
||||||
return sendUnreliablePacket(*packet, sockAddr, connectionSecret);
|
return sendUnreliablePacket(*packet, sockAddr, hmacAuth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,13 +453,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
|
||||||
|
|
||||||
if (activeSocket) {
|
if (activeSocket) {
|
||||||
qint64 bytesSent = 0;
|
qint64 bytesSent = 0;
|
||||||
auto connectionSecret = destinationNode.getConnectionSecret();
|
auto connectionHash = destinationNode.getAuthenticateHash();
|
||||||
|
|
||||||
// close the last packet in the list
|
// close the last packet in the list
|
||||||
packetList.closeCurrentPacket();
|
packetList.closeCurrentPacket();
|
||||||
|
|
||||||
while (!packetList._packets.empty()) {
|
while (!packetList._packets.empty()) {
|
||||||
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), *activeSocket, connectionSecret);
|
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), *activeSocket,
|
||||||
|
connectionHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit dataSent(destinationNode.getType(), bytesSent);
|
emit dataSent(destinationNode.getType(), bytesSent);
|
||||||
|
@ -466,14 +473,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
|
qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
|
||||||
const QUuid& connectionSecret) {
|
HMACAuth* hmacAuth) {
|
||||||
qint64 bytesSent = 0;
|
qint64 bytesSent = 0;
|
||||||
|
|
||||||
// close the last packet in the list
|
// close the last packet in the list
|
||||||
packetList.closeCurrentPacket();
|
packetList.closeCurrentPacket();
|
||||||
|
|
||||||
while (!packetList._packets.empty()) {
|
while (!packetList._packets.empty()) {
|
||||||
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), sockAddr, connectionSecret);
|
bytesSent += sendPacket(packetList.takeFront<NLPacket>(), sockAddr, hmacAuth);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytesSent;
|
return bytesSent;
|
||||||
|
@ -501,7 +508,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr<NLPacketList> packetList,
|
||||||
for (std::unique_ptr<udt::Packet>& packet : packetList->_packets) {
|
for (std::unique_ptr<udt::Packet>& packet : packetList->_packets) {
|
||||||
NLPacket* nlPacket = static_cast<NLPacket*>(packet.get());
|
NLPacket* nlPacket = static_cast<NLPacket*>(packet.get());
|
||||||
collectPacketStats(*nlPacket);
|
collectPacketStats(*nlPacket);
|
||||||
fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret());
|
fillPacketHeader(*nlPacket, destinationNode.getAuthenticateHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
|
return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
|
||||||
|
@ -524,7 +531,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr<NLPacket> packet, const Node&
|
||||||
auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket()
|
auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket()
|
||||||
: overridenSockAddr;
|
: overridenSockAddr;
|
||||||
|
|
||||||
return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret());
|
return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getAuthenticateHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||||
|
|
|
@ -138,19 +138,17 @@ public:
|
||||||
// use sendUnreliablePacket to send an unreliable packet (that you do not need to move)
|
// use sendUnreliablePacket to send an unreliable packet (that you do not need to move)
|
||||||
// either to a node (via its active socket) or to a manual sockaddr
|
// either to a node (via its active socket) or to a manual sockaddr
|
||||||
qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
|
qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
|
||||||
qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
|
qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
|
||||||
const QUuid& connectionSecret = QUuid());
|
|
||||||
|
|
||||||
// use sendPacket to send a moved unreliable or reliable NL packet to a node's active socket or manual sockaddr
|
// use sendPacket to send a moved unreliable or reliable NL packet to a node's active socket or manual sockaddr
|
||||||
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode);
|
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const Node& destinationNode);
|
||||||
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr,
|
qint64 sendPacket(std::unique_ptr<NLPacket> packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
|
||||||
const QUuid& connectionSecret = QUuid());
|
|
||||||
|
|
||||||
// use sendUnreliableUnorderedPacketList to unreliably send separate packets from the packet list
|
// use sendUnreliableUnorderedPacketList to unreliably send separate packets from the packet list
|
||||||
// either to a node's active socket or to a manual sockaddr
|
// either to a node's active socket or to a manual sockaddr
|
||||||
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const Node& destinationNode);
|
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const Node& destinationNode);
|
||||||
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
|
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
|
||||||
const QUuid& connectionSecret = QUuid());
|
HMACAuth* hmacAuth = nullptr);
|
||||||
|
|
||||||
// use sendPacketList to send reliable packet lists (ordered or unordered) to a node's active socket
|
// use sendPacketList to send reliable packet lists (ordered or unordered) to a node's active socket
|
||||||
// or to a manual sock addr
|
// or to a manual sock addr
|
||||||
|
@ -372,7 +370,7 @@ protected:
|
||||||
qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr,
|
qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr,
|
||||||
const QUuid& connectionSecret = QUuid());
|
const QUuid& connectionSecret = QUuid());
|
||||||
void collectPacketStats(const NLPacket& packet);
|
void collectPacketStats(const NLPacket& packet);
|
||||||
void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret = QUuid());
|
void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr);
|
||||||
|
|
||||||
void setLocalSocket(const HifiSockAddr& sockAddr);
|
void setLocalSocket(const HifiSockAddr& sockAddr);
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include "NLPacket.h"
|
#include "NLPacket.h"
|
||||||
|
|
||||||
|
#include "HMACAuth.h"
|
||||||
|
|
||||||
int NLPacket::localHeaderSize(PacketType type) {
|
int NLPacket::localHeaderSize(PacketType type) {
|
||||||
bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type);
|
bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type);
|
||||||
bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type);
|
bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type);
|
||||||
|
@ -150,18 +152,16 @@ QByteArray NLPacket::verificationHashInHeader(const udt::Packet& packet) {
|
||||||
return QByteArray(packet.getData() + offset, NUM_BYTES_MD5_HASH);
|
return QByteArray(packet.getData() + offset, NUM_BYTES_MD5_HASH);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray NLPacket::hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret) {
|
QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash) {
|
||||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
|
||||||
|
|
||||||
int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
|
int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
|
||||||
+ NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
|
+ NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
|
||||||
|
|
||||||
// add the packet payload and the connection UUID
|
// add the packet payload and the connection UUID
|
||||||
hash.addData(packet.getData() + offset, packet.getDataSize() - offset);
|
HMACAuth::HMACHash hashResult;
|
||||||
hash.addData(connectionSecret.toRfc4122());
|
if (!hash.calculateHash(hashResult, packet.getData() + offset, packet.getDataSize() - offset)) {
|
||||||
|
return QByteArray();
|
||||||
// return the hash
|
}
|
||||||
return hash.result();
|
return QByteArray((const char*) hashResult.data(), (int) hashResult.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NLPacket::writeTypeAndVersion() {
|
void NLPacket::writeTypeAndVersion() {
|
||||||
|
@ -214,14 +214,14 @@ void NLPacket::writeSourceID(LocalID sourceID) const {
|
||||||
_sourceID = sourceID;
|
_sourceID = sourceID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NLPacket::writeVerificationHashGivenSecret(const QUuid& connectionSecret) const {
|
void NLPacket::writeVerificationHash(HMACAuth& hmacAuth) const {
|
||||||
Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) &&
|
Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) &&
|
||||||
!PacketTypeEnum::getNonVerifiedPackets().contains(_type));
|
!PacketTypeEnum::getNonVerifiedPackets().contains(_type));
|
||||||
|
|
||||||
auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
|
auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
|
||||||
+ NUM_BYTES_LOCALID;
|
+ NUM_BYTES_LOCALID;
|
||||||
|
|
||||||
QByteArray verificationHash = hashForPacketAndSecret(*this, connectionSecret);
|
QByteArray verificationHash = hashForPacketAndHMAC(*this, hmacAuth);
|
||||||
|
|
||||||
memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size());
|
memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size());
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include "udt/Packet.h"
|
#include "udt/Packet.h"
|
||||||
|
|
||||||
|
class HMACAuth;
|
||||||
|
|
||||||
class NLPacket : public udt::Packet {
|
class NLPacket : public udt::Packet {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -69,7 +71,7 @@ public:
|
||||||
|
|
||||||
static LocalID sourceIDInHeader(const udt::Packet& packet);
|
static LocalID sourceIDInHeader(const udt::Packet& packet);
|
||||||
static QByteArray verificationHashInHeader(const udt::Packet& packet);
|
static QByteArray verificationHashInHeader(const udt::Packet& packet);
|
||||||
static QByteArray hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret);
|
static QByteArray hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash);
|
||||||
|
|
||||||
PacketType getType() const { return _type; }
|
PacketType getType() const { return _type; }
|
||||||
void setType(PacketType type);
|
void setType(PacketType type);
|
||||||
|
@ -80,7 +82,7 @@ public:
|
||||||
LocalID getSourceID() const { return _sourceID; }
|
LocalID getSourceID() const { return _sourceID; }
|
||||||
|
|
||||||
void writeSourceID(LocalID sourceID) const;
|
void writeSourceID(LocalID sourceID) const;
|
||||||
void writeVerificationHashGivenSecret(const QUuid& connectionSecret) const;
|
void writeVerificationHash(HMACAuth& hmacAuth) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ NodeType_t NodeType::fromString(QString type) {
|
||||||
|
|
||||||
|
|
||||||
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
|
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
|
||||||
const HifiSockAddr& localSocket, QObject* parent) :
|
const HifiSockAddr& localSocket, QObject* parent) :
|
||||||
NetworkPeer(uuid, publicSocket, localSocket, parent),
|
NetworkPeer(uuid, publicSocket, localSocket, parent),
|
||||||
_type(type),
|
_type(type),
|
||||||
_pingMs(-1), // "Uninitialized"
|
_pingMs(-1), // "Uninitialized"
|
||||||
|
@ -108,6 +108,7 @@ void Node::setType(char type) {
|
||||||
_symmetricSocket.setObjectName(typeString);
|
_symmetricSocket.setObjectName(typeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Node::updateClockSkewUsec(qint64 clockSkewSample) {
|
void Node::updateClockSkewUsec(qint64 clockSkewSample) {
|
||||||
_clockSkewMovingPercentile.updatePercentile(clockSkewSample);
|
_clockSkewMovingPercentile.updatePercentile(clockSkewSample);
|
||||||
_clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
|
_clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
|
||||||
|
@ -194,3 +195,16 @@ QDebug operator<<(QDebug debug, const Node& node) {
|
||||||
debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket();
|
debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket();
|
||||||
return debug.nospace();
|
return debug.nospace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Node::setConnectionSecret(const QUuid& connectionSecret) {
|
||||||
|
if (_connectionSecret == connectionSecret) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_authenticateHash) {
|
||||||
|
_authenticateHash.reset(new HMACAuth());
|
||||||
|
}
|
||||||
|
|
||||||
|
_connectionSecret = connectionSecret;
|
||||||
|
_authenticateHash->setKey(_connectionSecret);
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "SimpleMovingAverage.h"
|
#include "SimpleMovingAverage.h"
|
||||||
#include "MovingPercentile.h"
|
#include "MovingPercentile.h"
|
||||||
#include "NodePermissions.h"
|
#include "NodePermissions.h"
|
||||||
|
#include "HMACAuth.h"
|
||||||
|
|
||||||
class Node : public NetworkPeer {
|
class Node : public NetworkPeer {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -55,7 +56,8 @@ public:
|
||||||
void setIsUpstream(bool isUpstream) { _isUpstream = isUpstream; }
|
void setIsUpstream(bool isUpstream) { _isUpstream = isUpstream; }
|
||||||
|
|
||||||
const QUuid& getConnectionSecret() const { return _connectionSecret; }
|
const QUuid& getConnectionSecret() const { return _connectionSecret; }
|
||||||
void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
|
void setConnectionSecret(const QUuid& connectionSecret);
|
||||||
|
HMACAuth* getAuthenticateHash() const { return _authenticateHash.get(); }
|
||||||
|
|
||||||
NodeData* getLinkedData() const { return _linkedData.get(); }
|
NodeData* getLinkedData() const { return _linkedData.get(); }
|
||||||
void setLinkedData(std::unique_ptr<NodeData> linkedData) { _linkedData = std::move(linkedData); }
|
void setLinkedData(std::unique_ptr<NodeData> linkedData) { _linkedData = std::move(linkedData); }
|
||||||
|
@ -97,6 +99,7 @@ private:
|
||||||
NodeType_t _type;
|
NodeType_t _type;
|
||||||
|
|
||||||
QUuid _connectionSecret;
|
QUuid _connectionSecret;
|
||||||
|
std::unique_ptr<HMACAuth> _authenticateHash { nullptr };
|
||||||
std::unique_ptr<NodeData> _linkedData;
|
std::unique_ptr<NodeData> _linkedData;
|
||||||
bool _isReplicated { false };
|
bool _isReplicated { false };
|
||||||
int _pingMs;
|
int _pingMs;
|
||||||
|
|
|
@ -92,7 +92,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::AvatarQuery:
|
case PacketType::AvatarQuery:
|
||||||
return static_cast<PacketVersion>(AvatarQueryVersion::ConicalFrustums);
|
return static_cast<PacketVersion>(AvatarQueryVersion::ConicalFrustums);
|
||||||
default:
|
default:
|
||||||
return 20;
|
return 21;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ const int OctreeEditPacketSender::DEFAULT_MAX_PENDING_MESSAGES = PacketSender::D
|
||||||
|
|
||||||
|
|
||||||
OctreeEditPacketSender::OctreeEditPacketSender() :
|
OctreeEditPacketSender::OctreeEditPacketSender() :
|
||||||
_shouldSend(true),
|
|
||||||
_maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES),
|
_maxPendingMessages(DEFAULT_MAX_PENDING_MESSAGES),
|
||||||
_releaseQueuedMessagesPending(false)
|
_releaseQueuedMessagesPending(false)
|
||||||
{
|
{
|
||||||
|
@ -146,10 +145,6 @@ void OctreeEditPacketSender::queuePendingPacketToNodes(std::unique_ptr<NLPacket>
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr<NLPacket> packet) {
|
void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr<NLPacket> packet) {
|
||||||
if (!_shouldSend) {
|
|
||||||
return; // bail early
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(serversExist()); // we must have servers to be here!!
|
assert(serversExist()); // we must have servers to be here!!
|
||||||
|
|
||||||
auto node = DependencyManager::get<NodeList>()->soloNodeOfType(getMyNodeType());
|
auto node = DependencyManager::get<NodeList>()->soloNodeOfType(getMyNodeType());
|
||||||
|
@ -162,10 +157,6 @@ void OctreeEditPacketSender::queuePacketToNodes(std::unique_ptr<NLPacket> packet
|
||||||
// NOTE: editMessage - is JUST the octcode/color and does not contain the packet header
|
// NOTE: editMessage - is JUST the octcode/color and does not contain the packet header
|
||||||
void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, QByteArray& editMessage) {
|
void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, QByteArray& editMessage) {
|
||||||
|
|
||||||
if (!_shouldSend) {
|
|
||||||
return; // bail early
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't have servers, then we will simply queue up all of these packets and wait till we have
|
// If we don't have servers, then we will simply queue up all of these packets and wait till we have
|
||||||
// servers for processing
|
// servers for processing
|
||||||
if (!serversExist()) {
|
if (!serversExist()) {
|
||||||
|
|
|
@ -41,12 +41,10 @@ public:
|
||||||
|
|
||||||
/// are we in sending mode. If we're not in sending mode then all packets and messages will be ignored and
|
/// are we in sending mode. If we're not in sending mode then all packets and messages will be ignored and
|
||||||
/// not queued and not sent
|
/// not queued and not sent
|
||||||
bool getShouldSend() const { return _shouldSend; }
|
|
||||||
|
|
||||||
/// set sending mode. By default we are set to shouldSend=TRUE and packets will be sent. If shouldSend=FALSE, then we'll
|
/// set sending mode. By default we are set to shouldSend=TRUE and packets will be sent. If shouldSend=FALSE, then we'll
|
||||||
/// switch to not sending mode, and all packets and messages will be ignored, not queued, and not sent. This might be used
|
/// switch to not sending mode, and all packets and messages will be ignored, not queued, and not sent. This might be used
|
||||||
/// in an application like interface when all octree features are disabled.
|
/// in an application like interface when all octree features are disabled.
|
||||||
void setShouldSend(bool shouldSend) { _shouldSend = shouldSend; }
|
|
||||||
|
|
||||||
/// if you're running in non-threaded mode, you must call this method regularly
|
/// if you're running in non-threaded mode, you must call this method regularly
|
||||||
virtual bool process() override;
|
virtual bool process() override;
|
||||||
|
@ -76,7 +74,6 @@ public slots:
|
||||||
protected:
|
protected:
|
||||||
using EditMessagePair = std::pair<PacketType, QByteArray>;
|
using EditMessagePair = std::pair<PacketType, QByteArray>;
|
||||||
|
|
||||||
bool _shouldSend;
|
|
||||||
void queuePacketToNode(const QUuid& nodeID, std::unique_ptr<NLPacket> packet);
|
void queuePacketToNode(const QUuid& nodeID, std::unique_ptr<NLPacket> packet);
|
||||||
void queuePacketListToNode(const QUuid& nodeUUID, std::unique_ptr<NLPacketList> packetList);
|
void queuePacketListToNode(const QUuid& nodeUUID, std::unique_ptr<NLPacketList> packetList);
|
||||||
|
|
||||||
|
|
|
@ -349,7 +349,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
|
|
||||||
if (_numInactiveUpdates > 0) {
|
if (_numInactiveUpdates > 0) {
|
||||||
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
|
const uint8_t MAX_NUM_INACTIVE_UPDATES = 20;
|
||||||
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES) {
|
if (_numInactiveUpdates > MAX_NUM_INACTIVE_UPDATES || isServerlessMode()) {
|
||||||
// clear local ownership (stop sending updates) and let the server clear itself
|
// clear local ownership (stop sending updates) and let the server clear itself
|
||||||
_entity->clearSimulationOwnership();
|
_entity->clearSimulationOwnership();
|
||||||
return false;
|
return false;
|
||||||
|
@ -833,3 +833,9 @@ void EntityMotionState::clearObjectVelocities() const {
|
||||||
}
|
}
|
||||||
_entity->setAcceleration(glm::vec3(0.0f));
|
_entity->setAcceleration(glm::vec3(0.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntityMotionState::isServerlessMode() {
|
||||||
|
EntityTreeElementPointer element = _entity->getElement();
|
||||||
|
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
||||||
|
return tree ? tree->isServerlessMode() : false;
|
||||||
|
}
|
||||||
|
|
|
@ -156,6 +156,8 @@ protected:
|
||||||
uint8_t _numInactiveUpdates { 1 };
|
uint8_t _numInactiveUpdates { 1 };
|
||||||
uint8_t _bidPriority { 0 };
|
uint8_t _bidPriority { 0 };
|
||||||
bool _serverVariablesSet { false };
|
bool _serverVariablesSet { false };
|
||||||
|
|
||||||
|
bool isServerlessMode();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityMotionState_h
|
#endif // hifi_EntityMotionState_h
|
||||||
|
|
|
@ -328,10 +328,18 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalEntitySimulation::addOwnershipBid(EntityMotionState* motionState) {
|
void PhysicalEntitySimulation::addOwnershipBid(EntityMotionState* motionState) {
|
||||||
motionState->initForBid();
|
if (getEntityTree()->isServerlessMode()) {
|
||||||
motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
|
EntityItemPointer entity = motionState->getEntity();
|
||||||
_bids.push_back(motionState);
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
_nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry());
|
auto sessionID = nodeList->getSessionUUID();
|
||||||
|
entity->setSimulationOwner(SimulationOwner(sessionID, SCRIPT_GRAB_SIMULATION_PRIORITY));
|
||||||
|
_owned.push_back(motionState);
|
||||||
|
} else {
|
||||||
|
motionState->initForBid();
|
||||||
|
motionState->sendBid(_entityPacketSender, _physicsEngine->getNumSubsteps());
|
||||||
|
_bids.push_back(motionState);
|
||||||
|
_nextBidExpiry = glm::min(_nextBidExpiry, motionState->getNextBidExpiry());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalEntitySimulation::addOwnership(EntityMotionState* motionState) {
|
void PhysicalEntitySimulation::addOwnership(EntityMotionState* motionState) {
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
|
#include <NetworkingConstants.h>
|
||||||
#include "ProceduralCommon_frag.h"
|
#include "ProceduralCommon_frag.h"
|
||||||
|
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
@ -178,6 +178,8 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_shaderPath = shaderUrl.toLocalFile();
|
_shaderPath = shaderUrl.toLocalFile();
|
||||||
|
} else if (shaderUrl.scheme() == URL_SCHEME_QRC) {
|
||||||
|
_shaderPath = ":" + shaderUrl.path();
|
||||||
} else {
|
} else {
|
||||||
_networkShader = ShaderCache::instance().getShader(shaderUrl);
|
_networkShader = ShaderCache::instance().getShader(shaderUrl);
|
||||||
}
|
}
|
||||||
|
|
3
libraries/test-utils/CMakeLists.txt
Normal file
3
libraries/test-utils/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
set(TARGET_NAME test-utils)
|
||||||
|
setup_hifi_library(Network Gui)
|
||||||
|
|
21
libraries/test-utils/src/test-utils/FileDownloader.cpp
Normal file
21
libraries/test-utils/src/test-utils/FileDownloader.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include "FileDownloader.h"
|
||||||
|
|
||||||
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtNetwork/QNetworkReply>
|
||||||
|
|
||||||
|
FileDownloader::FileDownloader(QUrl url, const Handler& handler, QObject* parent) : QObject(parent), _handler(handler) {
|
||||||
|
connect(&_accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*)));
|
||||||
|
_accessManager.get(QNetworkRequest(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDownloader::waitForDownload() {
|
||||||
|
while (!_complete) {
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDownloader::fileDownloaded(QNetworkReply* pReply) {
|
||||||
|
_handler(pReply->readAll());
|
||||||
|
pReply->deleteLater();
|
||||||
|
_complete = true;
|
||||||
|
}
|
23
libraries/test-utils/src/test-utils/FileDownloader.h
Normal file
23
libraries/test-utils/src/test-utils/FileDownloader.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtNetwork/QNetworkAccessManager>
|
||||||
|
|
||||||
|
class FileDownloader : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Handler = std::function<void(const QByteArray& data)>;
|
||||||
|
|
||||||
|
FileDownloader(QUrl url, const Handler& handler, QObject* parent = 0);
|
||||||
|
|
||||||
|
void waitForDownload();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void fileDownloaded(QNetworkReply* pReply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager _accessManager;
|
||||||
|
Handler _handler;
|
||||||
|
bool _complete { false };
|
||||||
|
};
|
|
@ -54,6 +54,12 @@ public:
|
||||||
void setFullscreen(const QScreen* targetScreen, bool hideMenu = false);
|
void setFullscreen(const QScreen* targetScreen, bool hideMenu = false);
|
||||||
void unsetFullscreen(const QScreen* avoidScreen = nullptr);
|
void unsetFullscreen(const QScreen* avoidScreen = nullptr);
|
||||||
|
|
||||||
|
// FIXME remove access tot he menu from the plugin container
|
||||||
|
// Instead let display plugins expose a structure about the kinds
|
||||||
|
// of actions and menu items they want to have appear when they are
|
||||||
|
// active and allow the application to act on that when the display
|
||||||
|
// plugin becomes active (or when the UI is initialized, and a
|
||||||
|
// display plugin is already active)
|
||||||
virtual ui::Menu* getPrimaryMenu() = 0;
|
virtual ui::Menu* getPrimaryMenu() = 0;
|
||||||
virtual void showDisplayPluginsTools(bool show = true) = 0;
|
virtual void showDisplayPluginsTools(bool show = true) = 0;
|
||||||
virtual void requestReset() = 0;
|
virtual void requestReset() = 0;
|
||||||
|
|
|
@ -166,12 +166,11 @@ void OculusBaseDisplayPlugin::deactivateSession() {
|
||||||
//_session = nullptr;
|
//_session = nullptr;
|
||||||
}
|
}
|
||||||
void OculusBaseDisplayPlugin::updatePresentPose() {
|
void OculusBaseDisplayPlugin::updatePresentPose() {
|
||||||
//mat4 sensorResetMat;
|
_currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
|
||||||
//_currentPresentFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
|
_currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, 0);
|
||||||
//_currentPresentFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, _currentFrame->frameIndex);
|
auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse);
|
||||||
//auto trackingState = ovr_GetTrackingState(_session, _currentRenderFrameInfo.predictedDisplayTime, ovrFalse);
|
_currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose);
|
||||||
//_currentPresentFrameInfo.presentPose = toGlm(trackingState.HeadPose.ThePose);
|
_currentPresentFrameInfo.renderPose = _currentPresentFrameInfo.presentPose;
|
||||||
_currentPresentFrameInfo.presentPose = _currentPresentFrameInfo.renderPose;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OculusBaseDisplayPlugin::~OculusBaseDisplayPlugin() {
|
OculusBaseDisplayPlugin::~OculusBaseDisplayPlugin() {
|
||||||
|
|
|
@ -21,7 +21,8 @@ function printd(str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var radar = false;
|
var radar = false;
|
||||||
var radarHeight = 10; // camera position meters above the avatar
|
var RADAR_HEIGHT_INIT_DELTA = 10;
|
||||||
|
var radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA; // camera position (absolute y)
|
||||||
var tablet;
|
var tablet;
|
||||||
|
|
||||||
var RADAR_CAMERA_OFFSET = -1; // 1 meter below the avatar
|
var RADAR_CAMERA_OFFSET = -1; // 1 meter below the avatar
|
||||||
|
@ -45,12 +46,12 @@ var uniqueColor;
|
||||||
|
|
||||||
function moveTo(position) {
|
function moveTo(position) {
|
||||||
if (radar) {
|
if (radar) {
|
||||||
MyAvatar.position = position;
|
MyAvatar.goToLocation(position, false);
|
||||||
Camera.position = Vec3.sum(MyAvatar.position, {
|
Camera.position = {
|
||||||
x : 0,
|
x : position.x,
|
||||||
y : radarHeight,
|
y : radarHeight,
|
||||||
z : 0
|
z : position.z
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,46 +90,6 @@ function keyPressEvent(event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function actionOnObjectFromEvent(event) {
|
|
||||||
var rayIntersection = findRayIntersection(Camera.computePickRay(event.x,
|
|
||||||
event.y));
|
|
||||||
if (rayIntersection && rayIntersection.intersects
|
|
||||||
&& rayIntersection.overlayID) {
|
|
||||||
printd("found overlayID touched " + rayIntersection.overlayID);
|
|
||||||
if (entitiesByOverlayID[rayIntersection.overlayID]) {
|
|
||||||
var entity = Entities.getEntityProperties(
|
|
||||||
entitiesByOverlayID[rayIntersection.overlayID],
|
|
||||||
[ "sourceUrl" ]);
|
|
||||||
App.openUrl(entity.sourceUrl);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rayIntersection && rayIntersection.intersects
|
|
||||||
&& rayIntersection.entityID && rayIntersection.properties) {
|
|
||||||
printd("found " + rayIntersection.entityID + " of type "
|
|
||||||
+ rayIntersection.properties.type);
|
|
||||||
if (rayIntersection.properties.type == "Web") {
|
|
||||||
printd("found web element to "
|
|
||||||
+ rayIntersection.properties.sourceUrl);
|
|
||||||
App.openUrl(rayIntersection.properties.sourceUrl);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mousePress(event) {
|
|
||||||
mousePressOrTouchEnd(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mousePressOrTouchEnd(event) {
|
|
||||||
if (radar) {
|
|
||||||
if (actionOnObjectFromEvent(event)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleRadarMode() {
|
function toggleRadarMode() {
|
||||||
if (radar) {
|
if (radar) {
|
||||||
endRadar();
|
endRadar();
|
||||||
|
@ -229,9 +190,6 @@ function touchEnd(event) {
|
||||||
if (analyzeDoubleTap(event))
|
if (analyzeDoubleTap(event))
|
||||||
return; // double tap detected, finish
|
return; // double tap detected, finish
|
||||||
|
|
||||||
if (radar) {
|
|
||||||
mousePressOrTouchEnd(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -386,12 +344,13 @@ function pinchUpdate(event) {
|
||||||
radarHeight -= pinchIncrement;
|
radarHeight -= pinchIncrement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var deltaHeight = avatarY + radarHeight - Camera.position.y;
|
|
||||||
Camera.position = Vec3.sum(Camera.position, {
|
Camera.position = {
|
||||||
x : 0,
|
x : Camera.position.x,
|
||||||
y : deltaHeight,
|
y : radarHeight,
|
||||||
z : 0
|
z : Camera.position.z
|
||||||
});
|
};
|
||||||
|
|
||||||
if (!draggingCamera) {
|
if (!draggingCamera) {
|
||||||
startedDraggingCamera = true;
|
startedDraggingCamera = true;
|
||||||
draggingCamera = true;
|
draggingCamera = true;
|
||||||
|
@ -401,7 +360,8 @@ function pinchUpdate(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isInsideSquare(coords0, coords1, halfside) {
|
function isInsideSquare(coords0, coords1, halfside) {
|
||||||
return Math.abs(coords0.x - coords1.x) <= halfside
|
return coords0 != undefined && coords1 != undefined &&
|
||||||
|
Math.abs(coords0.x - coords1.x) <= halfside
|
||||||
&& Math.abs(coords0.y - coords1.y) <= halfside;
|
&& Math.abs(coords0.y - coords1.y) <= halfside;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,7 +372,7 @@ function dragScrollUpdate(event) {
|
||||||
// drag management
|
// drag management
|
||||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
var dragAt = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction,
|
var dragAt = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction,
|
||||||
radarHeight));
|
radarHeight - MyAvatar.position.y));
|
||||||
|
|
||||||
if (lastDragAt === undefined || lastDragAt === null) {
|
if (lastDragAt === undefined || lastDragAt === null) {
|
||||||
lastDragAt = dragAt;
|
lastDragAt = dragAt;
|
||||||
|
@ -654,6 +614,7 @@ function Teleporter() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Camera.position = Vec3.sum(Camera.position, {
|
Camera.position = Vec3.sum(Camera.position, {
|
||||||
x : xDelta,
|
x : xDelta,
|
||||||
y : 0,
|
y : 0,
|
||||||
|
@ -722,7 +683,7 @@ function Teleporter() {
|
||||||
return {
|
return {
|
||||||
dragTeleportBegin : function(event) {
|
dragTeleportBegin : function(event) {
|
||||||
printd("[newTeleport] TELEPORT began");
|
printd("[newTeleport] TELEPORT began");
|
||||||
var overlayDimensions = entityIconModelDimensions();
|
var overlayDimensions = teleportIconModelDimensions(MyAvatar.position.y);
|
||||||
// var destination = computeDestination(event, MyAvatar.position,
|
// var destination = computeDestination(event, MyAvatar.position,
|
||||||
// Camera.position, radarHeight);
|
// Camera.position, radarHeight);
|
||||||
// Dimension teleport and cancel overlays (not show them yet)
|
// Dimension teleport and cancel overlays (not show them yet)
|
||||||
|
@ -843,7 +804,7 @@ var avatarIconDimensionsVal = {
|
||||||
};
|
};
|
||||||
function avatarIconPlaneDimensions() {
|
function avatarIconPlaneDimensions() {
|
||||||
// given the current height, give a size
|
// given the current height, give a size
|
||||||
var xy = -0.003531 * radarHeight + 0.1;
|
var xy = -0.003531 * (radarHeight - MyAvatar.position.y) + 0.1;
|
||||||
avatarIconDimensionsVal.x = Math.abs(xy);
|
avatarIconDimensionsVal.x = Math.abs(xy);
|
||||||
avatarIconDimensionsVal.y = Math.abs(xy);
|
avatarIconDimensionsVal.y = Math.abs(xy);
|
||||||
// reuse object
|
// reuse object
|
||||||
|
@ -1121,172 +1082,20 @@ function renderAllOthersAvatarIcons() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function entityAdded(entityID) {
|
|
||||||
printd("Entity added " + entityID);
|
|
||||||
var props = Entities.getEntityProperties(entityID, [ "type" ]);
|
|
||||||
printd("Entity added " + entityID + " PROPS " + JSON.stringify(props));
|
|
||||||
if (props && props.type == "Web") {
|
|
||||||
printd("Entity Web added " + entityID);
|
|
||||||
saveEntityData(entityID, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function entityRemoved(entityID) {
|
|
||||||
printd("Entity removed " + entityID);
|
|
||||||
var props = Entities.getEntityProperties(entityID, [ "type" ]);
|
|
||||||
if (props && props.type == "Web") {
|
|
||||||
print("Entity Web removed " + entityID);
|
|
||||||
removeEntityData(entityID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* Entities (to remark) cache structure for showing entities markers
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
var entitiesData = {}; // by entityID
|
|
||||||
var entitiesByOverlayID = {}; // by overlayID
|
|
||||||
var entitiesIcons = []; // a parallel list of icons (overlays) to easily run
|
|
||||||
// through
|
|
||||||
|
|
||||||
var ICON_ENTITY_WEB_MODEL_URL = Script.resolvePath("../assets/images/web.svg");
|
|
||||||
var ICON_ENTITY_IMG_MODEL_URL = Script
|
|
||||||
.resolvePath("../assets/models/teleport-cancel.fbx"); // FIXME - use
|
|
||||||
// correct
|
|
||||||
// model&texture
|
|
||||||
var ICON_ENTITY_DEFAULT_DIMENSIONS = {
|
var ICON_ENTITY_DEFAULT_DIMENSIONS = {
|
||||||
x : 0.10,
|
x : 0.10,
|
||||||
y : 0.00001,
|
y : 0.00001,
|
||||||
z : 0.10
|
z : 0.10
|
||||||
};
|
};
|
||||||
|
|
||||||
var entityIconModelDimensionsVal = {
|
|
||||||
x : 0,
|
function teleportIconModelDimensions(y) {
|
||||||
y : 0.00001,
|
var teleportModelDimensions = ICON_ENTITY_DEFAULT_DIMENSIONS;
|
||||||
z : 0
|
var xz = -0.002831 * (radarHeight - y) + 0.1;
|
||||||
};
|
teleportModelDimensions.x = xz;
|
||||||
function entityIconModelDimensions() {
|
teleportModelDimensions.z = xz;
|
||||||
// given the current height, give a size
|
|
||||||
var xz = -0.002831 * radarHeight + 0.1;
|
|
||||||
entityIconModelDimensionsVal.x = xz;
|
|
||||||
entityIconModelDimensionsVal.z = xz;
|
|
||||||
// reuse object
|
// reuse object
|
||||||
return entityIconModelDimensionsVal;
|
return teleportModelDimensions;
|
||||||
}
|
|
||||||
/*
|
|
||||||
* entityIconPlaneDimensions: similar to entityIconModelDimensions but using xy
|
|
||||||
* plane
|
|
||||||
*/
|
|
||||||
function entityIconPlaneDimensions() {
|
|
||||||
var dim = entityIconModelDimensions();
|
|
||||||
var z = dim.z;
|
|
||||||
dim.z = dim.y;
|
|
||||||
dim.y = z;
|
|
||||||
return dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
function currentOverlayForEntity(QUuid) {
|
|
||||||
if (entitiesData[QUuid] != undefined) {
|
|
||||||
return entitiesData[QUuid].icon;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveEntityData(QUuid, planar) {
|
|
||||||
if (QUuid == null)
|
|
||||||
return;
|
|
||||||
var entity = Entities.getEntityProperties(QUuid, [ "position" ]);
|
|
||||||
printd("entity added save entity " + QUuid);
|
|
||||||
if (entitiesData[QUuid] != undefined) {
|
|
||||||
entitiesData[QUuid].position = entity.position;
|
|
||||||
} else {
|
|
||||||
var entityIcon = Overlays.addOverlay("image3d", {
|
|
||||||
subImage : {
|
|
||||||
x : 0,
|
|
||||||
y : 0,
|
|
||||||
width : 150,
|
|
||||||
height : 150
|
|
||||||
},
|
|
||||||
url : ICON_ENTITY_WEB_MODEL_URL,
|
|
||||||
dimensions : ICON_ENTITY_DEFAULT_DIMENSIONS,
|
|
||||||
visible : false,
|
|
||||||
ignoreRayIntersection : false,
|
|
||||||
orientation : Quat.fromPitchYawRollDegrees(-90, 0, 0)
|
|
||||||
});
|
|
||||||
|
|
||||||
entitiesIcons.push(entityIcon);
|
|
||||||
entitiesData[QUuid] = {
|
|
||||||
position : entity.position,
|
|
||||||
icon : entityIcon
|
|
||||||
};
|
|
||||||
entitiesByOverlayID[entityIcon] = QUuid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeEntityData(QUuid) {
|
|
||||||
if (QUuid == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var itsOverlay = currentOverlayForEntity(QUuid);
|
|
||||||
if (itsOverlay != null) {
|
|
||||||
Overlays.deleteOverlay(itsOverlay);
|
|
||||||
delete entitiesByOverlayID[itsOverlay];
|
|
||||||
}
|
|
||||||
var idx = entitiesIcons.indexOf(itsOverlay);
|
|
||||||
entitiesIcons.splice(idx, 1);
|
|
||||||
|
|
||||||
delete entitiesData[QUuid];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* Entities to remark Icon/Markers rendering
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
function hideAllEntitiesIcons() {
|
|
||||||
var len = entitiesIcons.length;
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
Overlays.editOverlay(entitiesIcons[i], {
|
|
||||||
visible : false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderAllEntitiesIcons() {
|
|
||||||
var entityPos;
|
|
||||||
var entityProps;
|
|
||||||
var iconDimensions = entityIconModelDimensions();
|
|
||||||
var planeDimensions = entityIconPlaneDimensions(); // plane overlays uses
|
|
||||||
// xy instead of xz
|
|
||||||
for ( var QUuid in entitiesData) {
|
|
||||||
if (entitiesData.hasOwnProperty(QUuid)) {
|
|
||||||
entityProps = Entities.getEntityProperties(QUuid, [ "position",
|
|
||||||
"visible" ]);
|
|
||||||
if (entityProps != null) {
|
|
||||||
entityPos = entityProps.position;
|
|
||||||
if (entitiesData[QUuid].icon != undefined && entityPos) {
|
|
||||||
var iconPos = findLineToHeightIntersectionCoords(
|
|
||||||
entityPos.x,
|
|
||||||
entityPos.y
|
|
||||||
+ RADAR_ICONS_APPARENT_DISTANCE_TO_AVATAR_BASE,
|
|
||||||
entityPos.z, Camera.position.x, Camera.position.y,
|
|
||||||
Camera.position.z, Camera.position.y
|
|
||||||
- RADAR_CAMERA_DISTANCE_TO_ICONS);
|
|
||||||
if (!iconPos) {
|
|
||||||
printd("entity icon pos bad for " + QUuid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var dimensions = entitiesData[QUuid].planar ? planeDimensions
|
|
||||||
: iconDimensions;
|
|
||||||
Overlays.editOverlay(entitiesData[QUuid].icon, {
|
|
||||||
visible : entityProps.visible,
|
|
||||||
dimensions : dimensions,
|
|
||||||
position : iconPos
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
@ -1298,11 +1107,8 @@ function startRadar() {
|
||||||
saveAllOthersAvatarsData();
|
saveAllOthersAvatarsData();
|
||||||
Camera.mode = "independent";
|
Camera.mode = "independent";
|
||||||
|
|
||||||
Camera.position = Vec3.sum(MyAvatar.position, {
|
initCameraOverMyAvatar();
|
||||||
x : 0,
|
|
||||||
y : radarHeight,
|
|
||||||
z : 0
|
|
||||||
});
|
|
||||||
Camera.orientation = Quat.fromPitchYawRollDegrees(-90, 0, 0);
|
Camera.orientation = Quat.fromPitchYawRollDegrees(-90, 0, 0);
|
||||||
radar = true;
|
radar = true;
|
||||||
|
|
||||||
|
@ -1319,7 +1125,6 @@ function endRadar() {
|
||||||
Controller.setVPadEnabled(true);
|
Controller.setVPadEnabled(true);
|
||||||
|
|
||||||
disconnectRadarModeEvents();
|
disconnectRadarModeEvents();
|
||||||
hideAllEntitiesIcons();
|
|
||||||
hideAllAvatarIcons();
|
hideAllAvatarIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1353,12 +1158,10 @@ function updateRadar() {
|
||||||
// Update avatar icons
|
// Update avatar icons
|
||||||
if (startedDraggingCamera) {
|
if (startedDraggingCamera) {
|
||||||
hideAllAvatarIcons();
|
hideAllAvatarIcons();
|
||||||
hideAllEntitiesIcons();
|
|
||||||
startedDraggingCamera = false;
|
startedDraggingCamera = false;
|
||||||
} else if (!draggingCamera) {
|
} else if (!draggingCamera) {
|
||||||
renderMyAvatarIcon();
|
renderMyAvatarIcon();
|
||||||
renderAllOthersAvatarIcons();
|
renderAllOthersAvatarIcons();
|
||||||
renderAllEntitiesIcons();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1366,48 +1169,41 @@ function valueIfDefined(value) {
|
||||||
return value !== undefined ? value : "";
|
return value !== undefined ? value : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function entitiesAnalysis() {
|
|
||||||
var ids = Entities.findEntitiesInFrustum(Camera.frustum);
|
|
||||||
var entities = [];
|
|
||||||
for (var i = 0; i < ids.length; i++) {
|
|
||||||
var id = ids[i];
|
|
||||||
var properties = Entities.getEntityProperties(id);
|
|
||||||
entities.push({
|
|
||||||
id : id,
|
|
||||||
name : properties.name,
|
|
||||||
type : properties.type,
|
|
||||||
url : properties.type == "Model" ? properties.modelURL : "",
|
|
||||||
sourceUrl : properties.sourceUrl,
|
|
||||||
locked : properties.locked,
|
|
||||||
visible : properties.visible,
|
|
||||||
drawCalls : valueIfDefined(properties.renderInfo.drawCalls),
|
|
||||||
hasScript : properties.script !== ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function connectRadarModeEvents() {
|
function connectRadarModeEvents() {
|
||||||
Script.update.connect(updateRadar); // 60Hz loop
|
Script.update.connect(updateRadar); // 60Hz loop
|
||||||
Controller.keyPressEvent.connect(keyPressEvent);
|
Controller.keyPressEvent.connect(keyPressEvent);
|
||||||
Controller.mousePressEvent.connect(mousePress); // single click/touch
|
|
||||||
Controller.touchUpdateEvent.connect(touchUpdate);
|
Controller.touchUpdateEvent.connect(touchUpdate);
|
||||||
|
Window.domainChanged.connect(domainChanged);
|
||||||
MyAvatar.positionGoneTo.connect(positionGoneTo);
|
MyAvatar.positionGoneTo.connect(positionGoneTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
function positionGoneTo() {
|
function initCameraOverMyAvatar() {
|
||||||
Camera.position = Vec3.sum(MyAvatar.position, {
|
radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA;
|
||||||
x : 0,
|
Camera.position = {
|
||||||
|
x : MyAvatar.position.x,
|
||||||
y : radarHeight,
|
y : radarHeight,
|
||||||
z : 0
|
z : MyAvatar.position.z
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function domainChanged() {
|
||||||
|
initCameraOverMyAvatar();
|
||||||
|
}
|
||||||
|
|
||||||
|
function positionGoneTo() {
|
||||||
|
Camera.position = {
|
||||||
|
x : MyAvatar.position.x,
|
||||||
|
y : radarHeight,
|
||||||
|
z : MyAvatar.position.z
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function disconnectRadarModeEvents() {
|
function disconnectRadarModeEvents() {
|
||||||
Script.update.disconnect(updateRadar);
|
Script.update.disconnect(updateRadar);
|
||||||
Controller.keyPressEvent.disconnect(keyPressEvent);
|
Controller.keyPressEvent.disconnect(keyPressEvent);
|
||||||
Controller.mousePressEvent.disconnect(mousePress);
|
|
||||||
Controller.touchUpdateEvent.disconnect(touchUpdate);
|
Controller.touchUpdateEvent.disconnect(touchUpdate);
|
||||||
MyAvatar.positionGoneTo.disconnect(positionGoneTo);
|
MyAvatar.positionGoneTo.disconnect(positionGoneTo);
|
||||||
|
Window.domainChanged.disconnect(domainChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
@ -1418,7 +1214,4 @@ function init() {
|
||||||
|
|
||||||
AvatarList.avatarAddedEvent.connect(avatarAdded);
|
AvatarList.avatarAddedEvent.connect(avatarAdded);
|
||||||
AvatarList.avatarRemovedEvent.connect(avatarRemoved);
|
AvatarList.avatarRemovedEvent.connect(avatarRemoved);
|
||||||
|
|
||||||
Entities.addingEntity.connect(entityAdded);
|
|
||||||
Entities.deletingEntity.connect(entityRemoved);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
var headset; // The preferred headset. Default to the first one found in the following list.
|
var headset; // The preferred headset. Default to the first one found in the following list.
|
||||||
var displayMenuName = "Display";
|
var displayMenuName = "Display";
|
||||||
var desktopMenuItemName = "Desktop";
|
var desktopMenuItemName = "Desktop";
|
||||||
['OpenVR (Vive)', 'Oculus Rift'].forEach(function (name) {
|
['HTC Vive', 'Oculus Rift', 'WindowMS'].forEach(function (name) {
|
||||||
if (!headset && Menu.menuItemExists(displayMenuName, name)) {
|
if (!headset && Menu.menuItemExists(displayMenuName, name)) {
|
||||||
headset = name;
|
headset = name;
|
||||||
}
|
}
|
||||||
|
|
8
tests-manual/CMakeLists.txt
Normal file
8
tests-manual/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# add the manual test directories
|
||||||
|
file(GLOB TEST_SUBDIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*")
|
||||||
|
foreach(DIR ${TEST_SUBDIRS})
|
||||||
|
if((IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}") AND (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}/CMakeLists.txt"))
|
||||||
|
set(TEST_PROJ_NAME ${DIR})
|
||||||
|
add_subdirectory(${DIR})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
16
tests-manual/gpu-textures/CMakeLists.txt
Normal file
16
tests-manual/gpu-textures/CMakeLists.txt
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
set(TARGET_NAME gpu-textures-tests)
|
||||||
|
AUTOSCRIBE_SHADER_LIB(gpu graphics render-utils)
|
||||||
|
# This is not a testcase -- just set it up as a regular hifi project
|
||||||
|
setup_hifi_project(Quick Gui Script)
|
||||||
|
setup_memory_debugger()
|
||||||
|
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||||
|
link_hifi_libraries(
|
||||||
|
shared task networking gl
|
||||||
|
ktx gpu octree
|
||||||
|
${PLATFORM_GL_BACKEND}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(EXTRA_DEPLOY_OPTIONS "--qmldir \"${PROJECT_SOURCE_DIR}/qml\"")
|
||||||
|
package_libraries_for_deployment()
|
||||||
|
|
||||||
|
target_nsight()
|
52
tests-manual/gpu-textures/qml/textureStats.qml
Normal file
52
tests-manual/gpu-textures/qml/textureStats.qml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 2.3
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 400
|
||||||
|
height: 600
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 10
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: 10
|
||||||
|
|
||||||
|
Text { text: qsTr("Total") }
|
||||||
|
Text { text: Stats.total + " MB" }
|
||||||
|
Text { text: qsTr("Allocated") }
|
||||||
|
Text { text: Stats.allocated }
|
||||||
|
Text { text: qsTr("Populated") }
|
||||||
|
Text { text: Stats.populated }
|
||||||
|
Text { text: qsTr("Pending") }
|
||||||
|
Text { text: Stats.pending }
|
||||||
|
Text { text: qsTr("Current Index") }
|
||||||
|
Text { text: Stats.index }
|
||||||
|
Text { text: qsTr("Current Source") }
|
||||||
|
Text { text: Stats.source }
|
||||||
|
Text { text: qsTr("Current Rez") }
|
||||||
|
Text { text: Stats.rez.width + " x " + Stats.rez.height }
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row1
|
||||||
|
spacing: 10
|
||||||
|
anchors.bottom: row2.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: 10
|
||||||
|
Button { text: "1024"; onClicked: Stats.maxTextureMemory(1024); }
|
||||||
|
Button { text: "256"; onClicked: Stats.maxTextureMemory(256); }
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: row2
|
||||||
|
spacing: 10
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: 10
|
||||||
|
Button { text: "Change Textures"; onClicked: Stats.changeTextures(); }
|
||||||
|
Button { text: "Next"; onClicked: Stats.nextTexture(); }
|
||||||
|
Button { text: "Previous"; onClicked: Stats.prevTexture(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
26
tests-manual/gpu-textures/src/TestHelpers.cpp
Normal file
26
tests-manual/gpu-textures/src/TestHelpers.cpp
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 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 "TestHelpers.h"
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
|
|
||||||
|
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings) {
|
||||||
|
auto vs = gpu::Shader::createVertex(vertexShaderSrc);
|
||||||
|
auto fs = gpu::Shader::createPixel(fragmentShaderSrc);
|
||||||
|
auto shader = gpu::Shader::createProgram(vs, fs);
|
||||||
|
if (!gpu::Shader::makeProgram(*shader, bindings)) {
|
||||||
|
printf("Could not compile shader\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString projectRootDir() {
|
||||||
|
static QString projectRootPath = QFileInfo(QFileInfo(__FILE__).absolutePath() + "/..").absoluteFilePath();
|
||||||
|
return projectRootPath;
|
||||||
|
}
|
40
tests-manual/gpu-textures/src/TestHelpers.h
Normal file
40
tests-manual/gpu-textures/src/TestHelpers.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
#include <Transform.h>
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
|
#include <gpu/Resource.h>
|
||||||
|
#include <gpu/Forward.h>
|
||||||
|
#include <gpu/Shader.h>
|
||||||
|
#include <gpu/Stream.h>
|
||||||
|
|
||||||
|
struct RenderArgs {
|
||||||
|
gpu::ContextPointer _context;
|
||||||
|
ivec4 _viewport;
|
||||||
|
gpu::Batch* _batch;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GpuTestBase : public QObject {
|
||||||
|
public:
|
||||||
|
virtual ~GpuTestBase() {}
|
||||||
|
virtual bool isReady() const { return true; }
|
||||||
|
virtual size_t getTestCount() const { return 1; }
|
||||||
|
virtual void renderTest(size_t test, const RenderArgs& args) = 0;
|
||||||
|
virtual QObject * statsObject() { return nullptr; }
|
||||||
|
virtual QUrl statUrl() { return QUrl(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t toCompactColor(const glm::vec4& color);
|
||||||
|
gpu::ShaderPointer makeShader(const std::string & vertexShaderSrc, const std::string & fragmentShaderSrc, const gpu::Shader::BindingSet & bindings);
|
||||||
|
QString projectRootDir();
|
166
tests-manual/gpu-textures/src/TestTextures.cpp
Normal file
166
tests-manual/gpu-textures/src/TestTextures.cpp
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 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 "TestTextures.h"
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
#include <QtQuick/QQuickView>
|
||||||
|
#include <QtQml/QQmlContext>
|
||||||
|
#include <gpu/Batch.h>
|
||||||
|
#include <gpu/Context.h>
|
||||||
|
|
||||||
|
#include "TestHelpers.h"
|
||||||
|
|
||||||
|
std::string vertexShaderSource = R"SHADER(
|
||||||
|
#line 14
|
||||||
|
layout(location = 0) out vec2 outTexCoord0;
|
||||||
|
|
||||||
|
const vec4 VERTICES[] = vec4[](
|
||||||
|
vec4(-1.0, -1.0, 0.0, 1.0),
|
||||||
|
vec4( 1.0, -1.0, 0.0, 1.0),
|
||||||
|
vec4(-1.0, 1.0, 0.0, 1.0),
|
||||||
|
vec4( 1.0, 1.0, 0.0, 1.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
outTexCoord0 = VERTICES[gl_VertexID].xy;
|
||||||
|
outTexCoord0 += 1.0;
|
||||||
|
outTexCoord0 /= 2.0;
|
||||||
|
gl_Position = VERTICES[gl_VertexID];
|
||||||
|
}
|
||||||
|
)SHADER";
|
||||||
|
|
||||||
|
std::string fragmentShaderSource = R"SHADER(
|
||||||
|
#line 28
|
||||||
|
|
||||||
|
uniform sampler2D tex;
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 inTexCoord0;
|
||||||
|
layout(location = 0) out vec4 outFragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
outFragColor = texture(tex, inTexCoord0);
|
||||||
|
outFragColor.a = 1.0;
|
||||||
|
//outFragColor.rb = inTexCoord0;
|
||||||
|
}
|
||||||
|
|
||||||
|
)SHADER";
|
||||||
|
|
||||||
|
#define STAT_UPDATE(name, src) \
|
||||||
|
{ \
|
||||||
|
auto val = src; \
|
||||||
|
if (_##name != val) { \
|
||||||
|
_##name = val; \
|
||||||
|
emit name##Changed(); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TextureTestStats::update(int curIndex, const gpu::TexturePointer& texture) {
|
||||||
|
STAT_UPDATE(total, (int)BYTES_TO_MB(gpu::Context::getTextureGPUMemSize()));
|
||||||
|
STAT_UPDATE(allocated, (int)gpu::Context::getTextureResourceGPUMemSize());
|
||||||
|
STAT_UPDATE(pending, (int)gpu::Context::getTexturePendingGPUTransferMemSize());
|
||||||
|
STAT_UPDATE(populated, (int)gpu::Context::getTextureResourcePopulatedGPUMemSize());
|
||||||
|
STAT_UPDATE(source, texture->source().c_str());
|
||||||
|
STAT_UPDATE(index, curIndex);
|
||||||
|
auto dims = texture->getDimensions();
|
||||||
|
STAT_UPDATE(rez, QSize(dims.x, dims.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
TexturesTest::TexturesTest() {
|
||||||
|
connect(&stats, &TextureTestStats::changeTextures, this, &TexturesTest::onChangeTextures);
|
||||||
|
connect(&stats, &TextureTestStats::nextTexture, this, &TexturesTest::onNextTexture);
|
||||||
|
connect(&stats, &TextureTestStats::prevTexture, this, &TexturesTest::onPrevTexture);
|
||||||
|
connect(&stats, &TextureTestStats::maxTextureMemory, this, &TexturesTest::onMaxTextureMemory);
|
||||||
|
{
|
||||||
|
auto VS = gpu::Shader::createVertex(vertexShaderSource);
|
||||||
|
auto PS = gpu::Shader::createPixel(fragmentShaderSource);
|
||||||
|
auto program = gpu::Shader::createProgram(VS, PS);
|
||||||
|
gpu::Shader::BindingSet slotBindings;
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
// If the pipeline did not exist, make it
|
||||||
|
auto state = std::make_shared<gpu::State>();
|
||||||
|
state->setCullMode(gpu::State::CULL_NONE);
|
||||||
|
state->setDepthTest({});
|
||||||
|
state->setBlendFunction({ false });
|
||||||
|
pipeline = gpu::Pipeline::create(program, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeTextures();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TexturesTest::renderTest(size_t testId, const RenderArgs& args) {
|
||||||
|
stats.update((int)index, textures[index]);
|
||||||
|
gpu::Batch& batch = *(args._batch);
|
||||||
|
batch.setPipeline(pipeline);
|
||||||
|
batch.setInputFormat(vertexFormat);
|
||||||
|
for (const auto& texture : textures) {
|
||||||
|
batch.setResourceTexture(0, texture);
|
||||||
|
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
|
||||||
|
}
|
||||||
|
batch.setResourceTexture(0, textures[index]);
|
||||||
|
batch.draw(gpu::TRIANGLE_STRIP, 4, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOAD_TEXTURE_COUNT 64
|
||||||
|
|
||||||
|
void TexturesTest::onChangeTextures() {
|
||||||
|
static const QDir TEST_DIR("D:/ktx_texture_test");
|
||||||
|
static std::vector<std::string> ALL_TEXTURE_FILES;
|
||||||
|
if (ALL_TEXTURE_FILES.empty()) {
|
||||||
|
auto entryList = TEST_DIR.entryList({ "*.ktx" }, QDir::Filter::Files);
|
||||||
|
ALL_TEXTURE_FILES.reserve(entryList.size());
|
||||||
|
for (auto entry : entryList) {
|
||||||
|
auto textureFile = TEST_DIR.absoluteFilePath(entry).toStdString();
|
||||||
|
ALL_TEXTURE_FILES.push_back(textureFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
oldTextures.clear();
|
||||||
|
oldTextures.swap(textures);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static const std::string bad = "D:/ktx_texture_test/b4beed38675dbc7a827ecd576399c1f4.ktx";
|
||||||
|
auto texture = gpu::Texture::unserialize(bad);
|
||||||
|
auto texelFormat = texture->getTexelFormat();
|
||||||
|
qDebug() << texture->getTexelFormat().getSemantic();
|
||||||
|
qDebug() << texture->getTexelFormat().getScalarCount();
|
||||||
|
textures.push_back(texture);
|
||||||
|
#else
|
||||||
|
std::shuffle(ALL_TEXTURE_FILES.begin(), ALL_TEXTURE_FILES.end(), std::default_random_engine());
|
||||||
|
size_t newTextureCount = std::min<size_t>(ALL_TEXTURE_FILES.size(), LOAD_TEXTURE_COUNT);
|
||||||
|
for (size_t i = 0; i < newTextureCount; ++i) {
|
||||||
|
const auto& textureFile = ALL_TEXTURE_FILES[i];
|
||||||
|
auto texture = gpu::Texture::unserialize(textureFile);
|
||||||
|
qDebug() << textureFile.c_str();
|
||||||
|
qDebug() << texture->getTexelFormat().getSemantic();
|
||||||
|
qDebug() << texture->getTexelFormat().getScalarCount();
|
||||||
|
textures.push_back(texture);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
index = 0;
|
||||||
|
qDebug() << "Done";
|
||||||
|
}
|
||||||
|
|
||||||
|
void TexturesTest::onNextTexture() {
|
||||||
|
index += textures.size() + 1;
|
||||||
|
index %= textures.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TexturesTest::onPrevTexture() {
|
||||||
|
index += textures.size() - 1;
|
||||||
|
index %= textures.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TexturesTest::onMaxTextureMemory(int maxTextureMemory) {
|
||||||
|
gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(maxTextureMemory));
|
||||||
|
}
|
74
tests-manual/gpu-textures/src/TestTextures.h
Normal file
74
tests-manual/gpu-textures/src/TestTextures.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TestHelpers.h"
|
||||||
|
|
||||||
|
#define STATS_PROPERTY(type, name, initialValue) \
|
||||||
|
Q_PROPERTY(type name READ name NOTIFY name##Changed) \
|
||||||
|
public: \
|
||||||
|
type name() { return _##name; }; \
|
||||||
|
private: \
|
||||||
|
type _##name{ initialValue };
|
||||||
|
|
||||||
|
|
||||||
|
class TextureTestStats : public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
STATS_PROPERTY(int, pending, 0)
|
||||||
|
STATS_PROPERTY(int, total, 0)
|
||||||
|
STATS_PROPERTY(int, populated, 0)
|
||||||
|
STATS_PROPERTY(int, allocated, 0)
|
||||||
|
STATS_PROPERTY(int, index, 0)
|
||||||
|
|
||||||
|
STATS_PROPERTY(QString, source, QString())
|
||||||
|
STATS_PROPERTY(QSize, rez, QSize(0, 0))
|
||||||
|
|
||||||
|
public:
|
||||||
|
void update(int index, const gpu::TexturePointer& texture);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void pendingChanged();
|
||||||
|
void totalChanged();
|
||||||
|
void populatedChanged();
|
||||||
|
void allocatedChanged();
|
||||||
|
void changeTextures();
|
||||||
|
void rezChanged();
|
||||||
|
void indexChanged();
|
||||||
|
void sourceChanged();
|
||||||
|
void maxTextureMemory(int);
|
||||||
|
|
||||||
|
void nextTexture();
|
||||||
|
void prevTexture();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TexturesTest : public GpuTestBase {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
gpu::Stream::FormatPointer vertexFormat { std::make_shared<gpu::Stream::Format>() };
|
||||||
|
std::vector<gpu::TexturePointer> textures;
|
||||||
|
std::vector<gpu::TexturePointer> oldTextures;
|
||||||
|
gpu::PipelinePointer pipeline;
|
||||||
|
TextureTestStats stats;
|
||||||
|
size_t index{ 0 };
|
||||||
|
|
||||||
|
public:
|
||||||
|
TexturesTest();
|
||||||
|
QObject* statsObject() override { return &stats; }
|
||||||
|
QUrl statUrl() override { return QUrl::fromLocalFile(projectRootDir() + "/qml/textureStats.qml"); }
|
||||||
|
void renderTest(size_t testId, const RenderArgs& args) override;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void onChangeTextures();
|
||||||
|
void onMaxTextureMemory(int newValue);
|
||||||
|
void onNextTexture();
|
||||||
|
void onPrevTexture();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
117
tests-manual/gpu-textures/src/TestWindow.cpp
Normal file
117
tests-manual/gpu-textures/src/TestWindow.cpp
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 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 "TestWindow.h"
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtGui/QResizeEvent>
|
||||||
|
|
||||||
|
#include <gl/GLHelpers.h>
|
||||||
|
|
||||||
|
#include <gpu/gl/GLBackend.h>
|
||||||
|
|
||||||
|
TestWindow::TestWindow() {
|
||||||
|
|
||||||
|
auto timer = new QTimer(this);
|
||||||
|
timer->setTimerType(Qt::PreciseTimer);
|
||||||
|
timer->setInterval(5);
|
||||||
|
connect(timer, &QTimer::timeout, [&] { draw(); });
|
||||||
|
timer->start();
|
||||||
|
|
||||||
|
connect(qApp, &QCoreApplication::aboutToQuit, [this, timer] {
|
||||||
|
timer->stop();
|
||||||
|
_aboutToQuit = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
setSurfaceType(QSurface::OpenGLSurface);
|
||||||
|
|
||||||
|
QSurfaceFormat format = getDefaultOpenGLSurfaceFormat();
|
||||||
|
format.setOption(QSurfaceFormat::DebugContext);
|
||||||
|
setFormat(format);
|
||||||
|
_glContext.setFormat(format);
|
||||||
|
_glContext.create();
|
||||||
|
_glContext.makeCurrent(this);
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::initGl() {
|
||||||
|
_glContext.makeCurrent(this);
|
||||||
|
gl::initModuleGl();
|
||||||
|
gpu::Context::init<gpu::gl::GLBackend>();
|
||||||
|
_renderArgs->_context = std::make_shared<gpu::Context>();
|
||||||
|
_glContext.makeCurrent(this);
|
||||||
|
resize(QSize(800, 600));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::resizeWindow(const QSize& size) {
|
||||||
|
_size = size;
|
||||||
|
_renderArgs->_viewport = ivec4(0, 0, _size.width(), _size.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::beginFrame() {
|
||||||
|
_renderArgs->_context->recycle();
|
||||||
|
_renderArgs->_context->beginFrame();
|
||||||
|
gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
|
||||||
|
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f });
|
||||||
|
batch.clearDepthFramebuffer(1e4);
|
||||||
|
batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() });
|
||||||
|
});
|
||||||
|
|
||||||
|
gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
|
||||||
|
batch.setViewportTransform(_renderArgs->_viewport);
|
||||||
|
batch.setStateScissorRect(_renderArgs->_viewport);
|
||||||
|
batch.setProjectionTransform(_projectionMatrix);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::endFrame() {
|
||||||
|
gpu::doInBatch("TestWindow::endFrame::finish", _renderArgs->_context, [&](gpu::Batch& batch) {
|
||||||
|
batch.resetStages();
|
||||||
|
});
|
||||||
|
auto framePointer = _renderArgs->_context->endFrame();
|
||||||
|
_renderArgs->_context->consumeFrameUpdates(framePointer);
|
||||||
|
_renderArgs->_context->executeFrame(framePointer);
|
||||||
|
_glContext.swapBuffers(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::draw() {
|
||||||
|
if (_aboutToQuit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempting to draw before we're visible and have a valid size will
|
||||||
|
// produce GL errors.
|
||||||
|
if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_glContext.makeCurrent(this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::once_flag once;
|
||||||
|
std::call_once(once, [&] { initGl(); });
|
||||||
|
beginFrame();
|
||||||
|
|
||||||
|
renderFrame();
|
||||||
|
|
||||||
|
endFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWindow::resizeEvent(QResizeEvent* ev) {
|
||||||
|
resizeWindow(ev->size());
|
||||||
|
float fov_degrees = 60.0f;
|
||||||
|
float aspect_ratio = (float)_size.width() / _size.height();
|
||||||
|
float near_clip = 0.1f;
|
||||||
|
float far_clip = 1000.0f;
|
||||||
|
_projectionMatrix = glm::perspective(glm::radians(fov_degrees), aspect_ratio, near_clip, far_clip);
|
||||||
|
}
|
41
tests-manual/gpu-textures/src/TestWindow.h
Normal file
41
tests-manual/gpu-textures/src/TestWindow.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2016/05/16
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtCore/QTime>
|
||||||
|
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
#include <gl/QOpenGLContextWrapper.h>
|
||||||
|
#include <gpu/Forward.h>
|
||||||
|
#include "TestHelpers.h"
|
||||||
|
|
||||||
|
#define DEFERRED_LIGHTING
|
||||||
|
|
||||||
|
class TestWindow : public QWindow {
|
||||||
|
protected:
|
||||||
|
QOpenGLContextWrapper _glContext;
|
||||||
|
QSize _size;
|
||||||
|
glm::mat4 _projectionMatrix;
|
||||||
|
bool _aboutToQuit { false };
|
||||||
|
std::shared_ptr<RenderArgs> _renderArgs{ std::make_shared<RenderArgs>() };
|
||||||
|
|
||||||
|
TestWindow();
|
||||||
|
virtual void initGl();
|
||||||
|
virtual void renderFrame() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resizeWindow(const QSize& size);
|
||||||
|
|
||||||
|
void beginFrame();
|
||||||
|
void endFrame();
|
||||||
|
void draw();
|
||||||
|
void resizeEvent(QResizeEvent* ev) override;
|
||||||
|
};
|
||||||
|
|
170
tests-manual/gpu-textures/src/main.cpp
Normal file
170
tests-manual/gpu-textures/src/main.cpp
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
//
|
||||||
|
// main.cpp
|
||||||
|
// tests/gpu-test/src
|
||||||
|
//
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
#include <QtCore/QTime>
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
#include <QtCore/QElapsedTimer>
|
||||||
|
#include <QtCore/QFile>
|
||||||
|
#include <QtCore/QLoggingCategory>
|
||||||
|
|
||||||
|
#include <QtGui/QDesktopServices>
|
||||||
|
#include <QtGui/QResizeEvent>
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtGui/QGuiApplication>
|
||||||
|
#include <QtGui/QImage>
|
||||||
|
#include <QtGui/QScreen>
|
||||||
|
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
|
||||||
|
#include <gl/Config.h>
|
||||||
|
|
||||||
|
#include <QtQuick/QQuickWindow>
|
||||||
|
#include <QtQuick/QQuickView>
|
||||||
|
#include <QtQml/QQmlContext>
|
||||||
|
|
||||||
|
#include <gpu/Context.h>
|
||||||
|
#include <gpu/Batch.h>
|
||||||
|
#include <gpu/Stream.h>
|
||||||
|
#include <gpu/gl/GLBackend.h>
|
||||||
|
|
||||||
|
#include <gl/QOpenGLContextWrapper.h>
|
||||||
|
#include <gl/GLHelpers.h>
|
||||||
|
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
#include <PathUtils.h>
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
|
#include <PerfStat.h>
|
||||||
|
#include <PathUtils.h>
|
||||||
|
#include <SharedUtil.h>
|
||||||
|
#include <ViewFrustum.h>
|
||||||
|
|
||||||
|
#include <gpu/Pipeline.h>
|
||||||
|
#include <gpu/Context.h>
|
||||||
|
|
||||||
|
#include "TestWindow.h"
|
||||||
|
#include "TestTextures.h"
|
||||||
|
|
||||||
|
using TestBuilder = std::function<GpuTestBase*()>;
|
||||||
|
using TestBuilders = std::list<TestBuilder>;
|
||||||
|
|
||||||
|
#define INTERACTIVE
|
||||||
|
|
||||||
|
class MyTestWindow : public TestWindow {
|
||||||
|
using Parent = TestWindow;
|
||||||
|
TestBuilders _testBuilders;
|
||||||
|
GpuTestBase* _currentTest{ nullptr };
|
||||||
|
size_t _currentTestId{ 0 };
|
||||||
|
size_t _currentMaxTests{ 0 };
|
||||||
|
glm::mat4 _camera;
|
||||||
|
QTime _time;
|
||||||
|
|
||||||
|
void initGl() override {
|
||||||
|
Parent::initGl();
|
||||||
|
_time.start();
|
||||||
|
updateCamera();
|
||||||
|
_testBuilders = TestBuilders({
|
||||||
|
[] { return new TexturesTest(); },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCamera() {
|
||||||
|
float t = _time.elapsed() * 1e-3f;
|
||||||
|
glm::vec3 unitscale{ 1.0f };
|
||||||
|
glm::vec3 up{ 0.0f, 1.0f, 0.0f };
|
||||||
|
|
||||||
|
float distance = 3.0f;
|
||||||
|
glm::vec3 camera_position{ distance * sinf(t), 0.5f, distance * cosf(t) };
|
||||||
|
|
||||||
|
static const vec3 camera_focus(0);
|
||||||
|
static const vec3 camera_up(0, 1, 0);
|
||||||
|
_camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up));
|
||||||
|
|
||||||
|
ViewFrustum frustum;
|
||||||
|
frustum.setPosition(camera_position);
|
||||||
|
frustum.setOrientation(glm::quat_cast(_camera));
|
||||||
|
frustum.setProjection(_projectionMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderFrame() override {
|
||||||
|
updateCamera();
|
||||||
|
|
||||||
|
while ((!_currentTest || (_currentTestId >= _currentMaxTests)) && !_testBuilders.empty()) {
|
||||||
|
if (_currentTest) {
|
||||||
|
delete _currentTest;
|
||||||
|
_currentTest = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentTest = _testBuilders.front()();
|
||||||
|
_testBuilders.pop_front();
|
||||||
|
|
||||||
|
if (_currentTest) {
|
||||||
|
auto statsObject = _currentTest->statsObject();
|
||||||
|
QUrl url = _currentTest->statUrl();
|
||||||
|
if (statsObject) {
|
||||||
|
auto screens = qApp->screens();
|
||||||
|
auto primaryScreen = qApp->primaryScreen();
|
||||||
|
auto targetScreen = primaryScreen;
|
||||||
|
for (const auto& screen : screens) {
|
||||||
|
if (screen == primaryScreen) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
targetScreen = screen;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto destPoint = targetScreen->availableGeometry().topLeft();
|
||||||
|
QQuickView* view = new QQuickView();
|
||||||
|
view->rootContext()->setContextProperty("Stats", statsObject);
|
||||||
|
view->setSource(url);
|
||||||
|
view->show();
|
||||||
|
view->setPosition({ destPoint.x() + 100, destPoint.y() + 100 });
|
||||||
|
}
|
||||||
|
_currentMaxTests = _currentTest->getTestCount();
|
||||||
|
_currentTestId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_currentTest && _testBuilders.empty()) {
|
||||||
|
qApp->quit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests might need to wait for resources to download
|
||||||
|
if (!_currentTest->isReady()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu::doInBatch("main::renderFrame", _renderArgs->_context, [&](gpu::Batch& batch) {
|
||||||
|
batch.setViewTransform(_camera);
|
||||||
|
_renderArgs->_batch = &batch;
|
||||||
|
_currentTest->renderTest(_currentTestId, *_renderArgs);
|
||||||
|
_renderArgs->_batch = nullptr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
setupHifiApplication("GPU Test");
|
||||||
|
qputenv("HIFI_DEBUG_OPENGL", QByteArray("1"));
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
MyTestWindow window;
|
||||||
|
app.exec();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue