From 288f69ad6ad5ee63fc97e1a70e6bb9b5805c6695 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Tue, 27 Mar 2018 15:28:12 -0300 Subject: [PATCH 001/151] Detect if Interface is running on an android testing device --- interface/src/Application.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 968af3e298..d945ddf6d8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -377,6 +377,9 @@ static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; +#if defined(Q_OS_ANDROID) +static const QString TESTER_FILE = "/sdcard/_hifi_test_device.txt"; +#endif const std::vector> Application::_acceptedExtensions { { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, @@ -1281,11 +1284,20 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. // The value will be 0 if the user blew away settings this session, which is both a feature and a bug. static const QString TESTER = "HIFI_TESTER"; + + bool isTester = false; +#if defined (Q_OS_ANDROID) + // Since we cannot set environment variables in Android we use a file presence + // to denote that this is a testing device + QFileInfo check_tester_file(TESTER_FILE); + isTester = check_tester_file.exists() && check_tester_file.isFile(); +#endif + QProcessEnvironment::systemEnvironment().insert(TESTER, "This Value is Great"); auto gpuIdent = GPUIdent::getInstance(); auto glContextData = getGLContextData(); QJsonObject properties = { { "version", applicationVersion() }, - { "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) }, + { "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) || isTester }, { "previousSessionCrashed", _previousSessionCrashed }, { "previousSessionRuntime", sessionRunTime.get() }, { "cpu_architecture", QSysInfo::currentCpuArchitecture() }, @@ -1341,7 +1353,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo auto glContextData = getGLContextData(); QJsonObject properties = { { "version", applicationVersion() }, - { "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) }, + { "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) || isTester }, { "previousSessionCrashed", _previousSessionCrashed }, { "previousSessionRuntime", sessionRunTime.get() }, { "cpu_architecture", QSysInfo::currentCpuArchitecture() }, From 6ccffb3c4dac878cda1b2f2ccad499f3c98c1b1f Mon Sep 17 00:00:00 2001 From: Gabriel Date: Tue, 10 Apr 2018 08:24:38 -0300 Subject: [PATCH 002/151] Remove testing line --- interface/src/Application.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d945ddf6d8..310bbf0b8e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1292,7 +1292,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QFileInfo check_tester_file(TESTER_FILE); isTester = check_tester_file.exists() && check_tester_file.isFile(); #endif - QProcessEnvironment::systemEnvironment().insert(TESTER, "This Value is Great"); auto gpuIdent = GPUIdent::getInstance(); auto glContextData = getGLContextData(); QJsonObject properties = { From 08b07e57d0c2a1b27415dfa99c26fc498dd2736f Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Thu, 20 Sep 2018 00:42:39 +0300 Subject: [PATCH 003/151] FB12459 - Keyboard shouldn't lower when move between property values in Create app in HMD mode --- .../resources/html/raiseAndLowerKeyboard.js | 9 +++++- .../qml/controls/FlickableWebViewCore.qml | 30 +++++++++++++++++-- .../qml/controls/TabletWebScreen.qml | 2 ++ .../resources/qml/controls/TabletWebView.qml | 2 ++ interface/resources/qml/controls/WebView.qml | 2 ++ 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/interface/resources/html/raiseAndLowerKeyboard.js b/interface/resources/html/raiseAndLowerKeyboard.js index f40c0d7376..8cdb3c2327 100644 --- a/interface/resources/html/raiseAndLowerKeyboard.js +++ b/interface/resources/html/raiseAndLowerKeyboard.js @@ -18,6 +18,11 @@ window.isKeyboardRaised = false; window.isNumericKeyboard = false; window.isPasswordField = false; + window.lastActiveElement = null; + + function getActiveElement() { + return document.activeElement; + } function shouldSetPasswordField() { var nodeType = document.activeElement.type; @@ -65,10 +70,11 @@ var keyboardRaised = shouldRaiseKeyboard(); var numericKeyboard = shouldSetNumeric(); var passwordField = shouldSetPasswordField(); + var activeElement = getActiveElement(); if (isWindowFocused && (keyboardRaised !== window.isKeyboardRaised || numericKeyboard !== window.isNumericKeyboard - || passwordField !== window.isPasswordField)) { + || passwordField !== window.isPasswordField || activeElement !== window.lastActiveElement)) { if (typeof EventBridge !== "undefined" && EventBridge !== null) { EventBridge.emitWebEvent( @@ -90,6 +96,7 @@ window.isKeyboardRaised = keyboardRaised; window.isNumericKeyboard = numericKeyboard; window.isPasswordField = passwordField; + window.lastActiveElement = activeElement; } }, POLL_FREQUENCY); diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index 943f15e1de..bc5883425f 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -34,10 +34,34 @@ Item { webViewCore.stop(); } + Timer { + id: delayedUnfocuser + repeat: false + interval: 200 + onTriggered: { + + // The idea behind this is to delay unfocusing, so that fast lower/raise will not result actual unfocusing. + // Fast lower/raise happens every time keyboard is being re-raised (see the code below in OffscreenQmlSurface::setKeyboardRaised) + // + // if (raised) { + // item->setProperty("keyboardRaised", QVariant(!raised)); + // } + // + // item->setProperty("keyboardRaised", QVariant(raised)); + // + + webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) { + console.log('unfocus completed: ', result); + }); + } + } + function unfocus() { - webViewCore.runJavaScript("if (document.activeElement) document.activeElement.blur();", function(result) { - console.log('unfocus completed: ', result); - }); + delayedUnfocuser.start(); + } + + function stopUnfocus() { + delayedUnfocuser.stop(); } function onLoadingChanged(loadRequest) { diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml index bb037ad478..a33dc8a1d7 100644 --- a/interface/resources/qml/controls/TabletWebScreen.qml +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -13,6 +13,8 @@ Item { onKeyboardRaisedChanged: { if(!keyboardRaised) { webroot.unfocus(); + } else { + webroot.stopUnfocus(); } } property bool punctuationMode: false diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index db695dbfb2..41127c2190 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -17,6 +17,8 @@ Item { onKeyboardRaisedChanged: { if(!keyboardRaised) { webroot.unfocus(); + } else { + webroot.stopUnfocus(); } } property bool punctuationMode: false diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 71bf69fdc8..8e7e5493ae 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -15,6 +15,8 @@ Item { onKeyboardRaisedChanged: { if(!keyboardRaised) { webroot.unfocus(); + } else { + webroot.stopUnfocus(); } } property bool punctuationMode: false From 09a6053044f065ae33d49fca51fdd78b94ee723a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 26 Sep 2018 14:10:47 -0700 Subject: [PATCH 004/151] cleaning up fragment shaders --- .../entities-renderer/src/paintStroke.slf | 1 - .../src/paintStroke_fade.slf | 1 - libraries/fbx/src/OBJReader.cpp | 2 +- libraries/graphics/src/graphics/Material.cpp | 7 +--- libraries/graphics/src/graphics/Material.h | 23 +++++------ libraries/graphics/src/graphics/Material.slh | 13 +++--- .../src/graphics/MaterialTextures.slh | 4 +- .../render-utils/src/DeferredBufferWrite.slh | 5 ++- libraries/render-utils/src/model.slf | 13 +++--- libraries/render-utils/src/model_fade.slf | 29 +++++++------- libraries/render-utils/src/model_lightmap.slf | 28 ++++++++----- .../render-utils/src/model_lightmap_fade.slf | 34 +++++++++------- .../src/model_lightmap_normal_map.slf | 31 ++++++++------ .../src/model_lightmap_normal_map_fade.slf | 40 ++++++++++--------- .../render-utils/src/model_normal_map.slf | 21 +++++----- .../src/model_normal_map_fade.slf | 31 +++++++------- libraries/render-utils/src/model_shadow.slf | 4 +- .../render-utils/src/model_shadow_fade.slf | 8 +--- .../render-utils/src/model_translucent.slf | 21 +++++----- .../src/model_translucent_fade.slf | 29 +++++++++----- .../src/model_translucent_normal_map.slf | 27 +++++++------ .../src/model_translucent_normal_map_fade.slf | 31 +++++++------- .../src/model_translucent_unlit.slf | 10 ++--- .../src/model_translucent_unlit_fade.slf | 14 +++---- libraries/render-utils/src/model_unlit.slf | 10 ++--- .../render-utils/src/model_unlit_fade.slf | 13 +++--- .../render-utils/src/parabola_translucent.slf | 2 +- .../src/sdf_text3D_transparent.slf | 1 - libraries/render-utils/src/simple.slf | 1 - libraries/render-utils/src/simple_fade.slf | 3 -- .../render-utils/src/simple_textured_fade.slf | 1 - .../src/simple_textured_unlit.slf | 1 - .../src/simple_textured_unlit_fade.slf | 1 - .../src/simple_transparent_textured.slf | 1 - .../src/simple_transparent_web_browser.slf | 1 - 35 files changed, 229 insertions(+), 233 deletions(-) diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 211685a9ba..0ab47045e5 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -30,6 +30,5 @@ void main(void) { float(frontCondition) * interpolatedNormal, texel.a * varColor.a, color * texel.rgb, - vec3(0.01, 0.01, 0.01), 10.0); } diff --git a/libraries/entities-renderer/src/paintStroke_fade.slf b/libraries/entities-renderer/src/paintStroke_fade.slf index e5f70c8038..6109207774 100644 --- a/libraries/entities-renderer/src/paintStroke_fade.slf +++ b/libraries/entities-renderer/src/paintStroke_fade.slf @@ -48,6 +48,5 @@ void main(void) { interpolatedNormal * float(frontCondition), texel.a * varColor.a, polyline.color * texel.rgb + fadeEmissive, - vec3(0.01, 0.01, 0.01), 10.0); } diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index c46a1e234c..37809585d2 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -981,7 +981,7 @@ FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& m modelMaterial->setMetallic(ILLUMINATION_MODEL_APPLY_NON_METALLIC); } if (fresnelOn) { - modelMaterial->setFresnel(glm::vec3(1.0f)); + // TODO: how to turn fresnel on? } modelMaterial->setOpacity(fbxMaterial.opacity); diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index 2300bc5098..aa3716b3ee 100755 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -70,7 +70,7 @@ Material& Material::operator= (const Material& material) { Material::~Material() { } -void Material::setEmissive(const Color& emissive, bool isSRGB) { +void Material::setEmissive(const Color& emissive, bool isSRGB) { _key.setEmissive(glm::any(glm::greaterThan(emissive, Color(0.0f)))); _schemaBuffer.edit()._key = (uint32) _key._flags.to_ulong(); _schemaBuffer.edit()._emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive); @@ -100,11 +100,6 @@ void Material::setRoughness(float roughness) { _schemaBuffer.edit()._roughness = roughness; } -void Material::setFresnel(const Color& fresnel, bool isSRGB) { - //_key.setAlbedo(glm::any(glm::greaterThan(albedo, Color(0.0f)))); - _schemaBuffer.edit()._fresnel = (isSRGB ? ColorUtils::sRGBToLinearVec3(fresnel) : fresnel); -} - void Material::setMetallic(float metallic) { metallic = glm::clamp(metallic, 0.0f, 1.0f); _key.setMetallic(metallic > 0.0f); diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index fd3f12e865..f1f209eeb2 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -291,9 +291,6 @@ public: void setAlbedo(const Color& albedo, bool isSRGB = true); Color getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get()._albedo) : _schemaBuffer.get()._albedo); } - void setFresnel(const Color& fresnel, bool isSRGB = true); - Color getFresnel(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get()._fresnel) : _schemaBuffer.get()._fresnel); } - void setMetallic(float metallic); float getMetallic() const { return _schemaBuffer.get()._metallic; } @@ -306,20 +303,18 @@ public: // Schema to access the attribute values of the material class Schema { public: - glm::vec3 _emissive{ 0.0f }; // No Emissive - float _opacity{ 1.0f }; // Opacity = 1 => Not Transparent + glm::vec3 _emissive { 0.0f }; // No Emissive + float _opacity { 1.0f }; // Opacity = 1 => Not Transparent - glm::vec3 _albedo{ 0.5f }; // Grey albedo => isAlbedo - float _roughness{ 1.0f }; // Roughness = 1 => Not Glossy + glm::vec3 _albedo { 0.5f }; // Grey albedo => isAlbedo + float _roughness { 1.0f }; // Roughness = 1 => Not Glossy - glm::vec3 _fresnel{ 0.03f }; // Fresnel value for a default non metallic - float _metallic{ 0.0f }; // Not Metallic + float _metallic { 0.0f }; // Not Metallic + float _scattering { 0.0f }; // Scattering info + glm::vec2 _spare { 0.0f }; - float _scattering{ 0.0f }; // Scattering info - - glm::vec2 _spare{ 0.0f }; - - uint32_t _key{ 0 }; // a copy of the materialKey + uint32_t _key { 0 }; // a copy of the materialKey + glm::vec3 _spare2 { 0.0f }; // for alignment beauty, Material size == Mat4x4 diff --git a/libraries/graphics/src/graphics/Material.slh b/libraries/graphics/src/graphics/Material.slh index fe273ed2a9..f0c6f5be3b 100644 --- a/libraries/graphics/src/graphics/Material.slh +++ b/libraries/graphics/src/graphics/Material.slh @@ -19,8 +19,8 @@ struct Material { vec4 _emissiveOpacity; vec4 _albedoRoughness; - vec4 _fresnelMetallic; - vec4 _scatteringSpare2Key; + vec4 _metallicScatteringSpare2; + vec4 _keySpare3; }; layout(binding=GRAPHICS_BUFFER_MATERIAL) uniform materialBuffer { @@ -36,15 +36,12 @@ float getMaterialOpacity(Material m) { return m._emissiveOpacity.a; } vec3 getMaterialAlbedo(Material m) { return m._albedoRoughness.rgb; } float getMaterialRoughness(Material m) { return m._albedoRoughness.a; } - -vec3 getMaterialFresnel(Material m) { return m._fresnelMetallic.rgb; } -float getMaterialMetallic(Material m) { return m._fresnelMetallic.a; } - float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); } -float getMaterialScattering(Material m) { return m._scatteringSpare2Key.x; } +float getMaterialMetallic(Material m) { return m._metallicScatteringSpare2.x; } +float getMaterialScattering(Material m) { return m._metallicScatteringSpare2.y; } -BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._scatteringSpare2Key.w); } +BITFIELD getMaterialKey(Material m) { return floatBitsToInt(m._keySpare3.x); } const BITFIELD EMISSIVE_VAL_BIT = 0x00000001; const BITFIELD UNLIT_VAL_BIT = 0x00000002; diff --git a/libraries/graphics/src/graphics/MaterialTextures.slh b/libraries/graphics/src/graphics/MaterialTextures.slh index f76d65da96..106453877c 100644 --- a/libraries/graphics/src/graphics/MaterialTextures.slh +++ b/libraries/graphics/src/graphics/MaterialTextures.slh @@ -204,12 +204,12 @@ float fetchScatteringMap(vec2 uv) { <@endif@> <@endfunc@> -<@func fetchMaterialTexturesCoord1(matKey, texcoord1, occlusion, lightmapVal)@> +<@func fetchMaterialTexturesCoord1(matKey, texcoord1, occlusion, lightmap)@> <@if occlusion@> float <$occlusion$> = (((<$matKey$> & OCCLUSION_MAP_BIT) != 0) ? fetchOcclusionMap(<$texcoord1$>) : 1.0); <@endif@> <@if lightmapVal@> - vec3 <$lightmapVal$> = fetchLightmapMap(<$texcoord1$>); + vec3 <$lightmap$> = fetchLightmapMap(<$texcoord1$>); <@endif@> <@endfunc@> diff --git a/libraries/render-utils/src/DeferredBufferWrite.slh b/libraries/render-utils/src/DeferredBufferWrite.slh index 769e602dc5..04a65477b2 100644 --- a/libraries/render-utils/src/DeferredBufferWrite.slh +++ b/libraries/render-utils/src/DeferredBufferWrite.slh @@ -21,6 +21,7 @@ layout(location=3) out vec4 _fragColor3; // emissive // the alpha threshold const float alphaThreshold = 0.5; +// FIXME: is this correct? float evalOpaqueFinalAlpha(float alpha, float mapAlpha) { return mix(alpha, 1.0 - alpha, step(mapAlpha, alphaThreshold)); } @@ -39,7 +40,7 @@ void packDeferredFragment(vec3 normal, float alpha, vec3 albedo, float roughness _fragColor3 = vec4(isEmissiveEnabled() * emissive, 1.0); } -void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 albedo, float roughness, float metallic, vec3 fresnel, vec3 lightmap) { +void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 albedo, float roughness, float metallic, vec3 lightmap) { if (alpha != 1.0) { discard; } @@ -61,7 +62,7 @@ void packDeferredFragmentUnlit(vec3 normal, float alpha, vec3 color) { _fragColor3 = vec4(color, 1.0); } -void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 albedo, vec3 fresnel, float roughness) { +void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 albedo, float roughness) { if (alpha <= 0.0) { discard; } diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index 2c42ed6083..edf5064324 100644 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model.frag -// fragment shader -// // Created by Andrzej Kapolka on 5/6/14. // Copyright 2014 High Fidelity, Inc. // @@ -42,11 +40,14 @@ void main(void) { float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; - float metallic = getMaterialMetallic(mat); - <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; float scattering = getMaterialScattering(mat); <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; @@ -58,6 +59,6 @@ void main(void) { roughness, metallic, emissive, - occlusionTex, + occlusion, scattering); } diff --git a/libraries/render-utils/src/model_fade.slf b/libraries/render-utils/src/model_fade.slf index b5a2c8d3ef..e931ec4cf0 100644 --- a/libraries/render-utils/src/model_fade.slf +++ b/libraries/render-utils/src/model_fade.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_fade.frag -// fragment shader -// // Created by Olivier Prat on 06/05/17. // Copyright 2017 High Fidelity, Inc. // @@ -13,17 +11,15 @@ // <@include DeferredBufferWrite.slh@> - <@include graphics/Material.slh@> - <@include graphics/MaterialTextures.slh@> -<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$> +<@include render-utils/ShaderConstants.h@> + +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION, SCATTERING)$> <@include Fade.slh@> <$declareFadeFragment()$> -<@include render-utils/ShaderConstants.h@> - layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _positionWS; layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord0 _texCoord01.xy @@ -34,13 +30,12 @@ layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; void main(void) { vec3 fadeEmissive; FadeObjectParams fadeParams; - <$fetchFadeObjectParams(fadeParams)$> applyFade(fadeParams, _positionWS.xyz, fadeEmissive); Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); - <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex)$> + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex, scatteringTex)$> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> float opacity = 1.0; @@ -54,13 +49,17 @@ void main(void) { float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; - vec3 emissive = getMaterialEmissive(mat); - <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; - float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; + float scattering = getMaterialScattering(mat); + <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; packDeferredFragment( normalize(_normalWS), @@ -68,7 +67,7 @@ void main(void) { albedo, roughness, metallic, - emissive+fadeEmissive, - occlusionTex, + emissive + fadeEmissive, + occlusion, scattering); } diff --git a/libraries/render-utils/src/model_lightmap.slf b/libraries/render-utils/src/model_lightmap.slf index efc36cc14a..ada49fdd08 100644 --- a/libraries/render-utils/src/model_lightmap.slf +++ b/libraries/render-utils/src/model_lightmap.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_lightmap.frag -// fragment shader -// // Created by Samuel Gateau on 11/19/14. // Copyright 2014 High Fidelity, Inc. // @@ -20,7 +18,6 @@ <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC)$> <$declareMaterialLightmap()$> - layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord0 _texCoord01.xy #define _texCoord1 _texCoord01.zw @@ -30,15 +27,24 @@ layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; void main(void) { Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); - <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, _SCRIBE_NULL, metallicTex)$> - <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$> + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmap)$> + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color.rgb; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; packDeferredFragmentLightmap( normalize(_normalWS), evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), - getMaterialAlbedo(mat) * albedo.rgb * _color.rgb, - getMaterialRoughness(mat) * roughness, - getMaterialMetallic(mat) * metallicTex, - /*metallicTex, // no use of */getMaterialFresnel(mat), - lightmapVal); + albedo, + roughness, + metallic, + lightmap); } diff --git a/libraries/render-utils/src/model_lightmap_fade.slf b/libraries/render-utils/src/model_lightmap_fade.slf index 4cbf3dcdea..3819c4d4fa 100644 --- a/libraries/render-utils/src/model_lightmap_fade.slf +++ b/libraries/render-utils/src/model_lightmap_fade.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_lightmap_fade.frag -// fragment shader -// // Created by Olivier Prat on 06/05/17. // Copyright 2017 High Fidelity, Inc. // @@ -13,18 +11,16 @@ // <@include DeferredBufferWrite.slh@> - <@include graphics/Material.slh@> - <@include graphics/MaterialTextures.slh@> +<@include render-utils/ShaderConstants.h@> + <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC)$> <$declareMaterialLightmap()$> <@include Fade.slh@> <$declareFadeFragment()$> -<@include render-utils/ShaderConstants.h@> - layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord0 _texCoord01.xy #define _texCoord1 _texCoord01.zw @@ -35,21 +31,29 @@ layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _positionWS; void main(void) { vec3 fadeEmissive; FadeObjectParams fadeParams; - <$fetchFadeObjectParams(fadeParams)$> applyFade(fadeParams, _positionWS.xyz, fadeEmissive); Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); - <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, _SCRIBE_NULL, metallicTex)$> - <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$> + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmap)$> + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color.rgb; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; packDeferredFragmentLightmap( normalize(_normalWS), evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), - getMaterialAlbedo(mat) * albedo.rgb * _color.rgb, - getMaterialRoughness(mat) * roughness, - getMaterialMetallic(mat) * metallicTex, - /*metallicTex, // no use of */getMaterialFresnel(mat), - lightmapVal+fadeEmissive); + albedo, + roughness, + metallic, + lightmap + fadeEmissive); } diff --git a/libraries/render-utils/src/model_lightmap_normal_map.slf b/libraries/render-utils/src/model_lightmap_normal_map.slf index ebafc6dfe2..adeff463b5 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map.slf +++ b/libraries/render-utils/src/model_lightmap_normal_map.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_lightmap_normal_map.frag -// fragment shader -// // Created by Samuel Gateau on 11/19/14. // Copyright 2014 High Fidelity, Inc. // @@ -31,18 +29,27 @@ layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; void main(void) { Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); - <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, normalTexel, metallicTex)$> - <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$> + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmap)$> + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color.rgb; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; vec3 fragNormal; - <$evalMaterialNormalLOD(_positionES, normalTexel, _normalWS, _tangentWS, fragNormal)$> + <$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormal)$> packDeferredFragmentLightmap( - normalize(fragNormal.xyz), + normalize(fragNormal), evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), - getMaterialAlbedo(mat) * albedo.rgb * _color.rgb, - getMaterialRoughness(mat) * roughness, - getMaterialMetallic(mat) * metallicTex, - /*specular, // no use of */ getMaterialFresnel(mat), - lightmapVal); + albedo, + roughness, + metallic, + lightmap); } diff --git a/libraries/render-utils/src/model_lightmap_normal_map_fade.slf b/libraries/render-utils/src/model_lightmap_normal_map_fade.slf index a9bac0e051..c46308fcf3 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map_fade.slf +++ b/libraries/render-utils/src/model_lightmap_normal_map_fade.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_lightmap_normal_map_fade.frag -// fragment shader -// // Created by Olivier Prat on 06/05/17. // Copyright 2017 High Fidelity, Inc. // @@ -13,18 +11,17 @@ // <@include DeferredBufferWrite.slh@> - <@include graphics/Material.slh@> - <@include graphics/MaterialTextures.slh@> +<@include render-utils/ShaderConstants.h@> + <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC)$> <$declareMaterialLightmap()$> <@include Fade.slh@> <$declareFadeFragment()$> -<@include render-utils/ShaderConstants.h@> - +layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _positionWS; layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord0 _texCoord01.xy @@ -32,29 +29,36 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; layout(location=RENDER_UTILS_ATTR_TANGENT_WS) in vec3 _tangentWS; layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; -layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _positionWS; void main(void) { vec3 fadeEmissive; FadeObjectParams fadeParams; - <$fetchFadeObjectParams(fadeParams)$> applyFade(fadeParams, _positionWS.xyz, fadeEmissive); Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); - <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedo, roughness, normalTexel, metallicTex)$> - <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmapVal)$> + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, _SCRIBE_NULL, lightmap)$> + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color.rgb; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; vec3 fragNormal; - <$evalMaterialNormalLOD(_positionES, normalTexel, _normalWS, _tangentWS, fragNormal)$> + <$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormal)$> packDeferredFragmentLightmap( - normalize(fragNormal.xyz), + normalize(fragNormal), evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), - getMaterialAlbedo(mat) * albedo.rgb * _color.rgb, - getMaterialRoughness(mat) * roughness, - getMaterialMetallic(mat) * metallicTex, - /*specular, // no use of */ getMaterialFresnel(mat), - lightmapVal+fadeEmissive); + albedo, + roughness, + metallic, + lightmap + fadeEmissive); } diff --git a/libraries/render-utils/src/model_normal_map.slf b/libraries/render-utils/src/model_normal_map.slf index 5f30830511..5fbc81e35b 100644 --- a/libraries/render-utils/src/model_normal_map.slf +++ b/libraries/render-utils/src/model_normal_map.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_normal_map.frag -// fragment shader -// // Created by Andrzej Kapolka on 5/6/14. // Copyright 2014 High Fidelity, Inc. // @@ -44,25 +42,28 @@ void main(void) { float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; - vec3 fragNormalWS; - <$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormalWS)$> - - float metallic = getMaterialMetallic(mat); - <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; float scattering = getMaterialScattering(mat); <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; + vec3 fragNormalWS; + <$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormalWS)$> + packDeferredFragment( - normalize(fragNormalWS.xyz), + normalize(fragNormalWS), opacity, albedo, roughness, metallic, emissive, - occlusionTex, + occlusion, scattering); } diff --git a/libraries/render-utils/src/model_normal_map_fade.slf b/libraries/render-utils/src/model_normal_map_fade.slf index 499f376efa..07b6f47b55 100644 --- a/libraries/render-utils/src/model_normal_map_fade.slf +++ b/libraries/render-utils/src/model_normal_map_fade.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_normal_map_fade.frag -// fragment shader -// // Created by Olivier Prat on 06/05/17. // Copyright 2017 High Fidelity, Inc. // @@ -16,10 +14,10 @@ <@include graphics/Material.slh@> <@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> + +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION, SCATTERING)$> + <@include Fade.slh@> - -<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$> - <$declareFadeFragment()$> layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; @@ -34,13 +32,12 @@ layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; void main(void) { vec3 fadeEmissive; FadeObjectParams fadeParams; - <$fetchFadeObjectParams(fadeParams)$> applyFade(fadeParams, _positionWS.xyz, fadeEmissive); Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); - <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex)$> + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex, scatteringTex)$> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> float opacity = 1.0; @@ -54,24 +51,28 @@ void main(void) { float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; + + float scattering = getMaterialScattering(mat); + <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; + vec3 fragNormalWS; <$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormalWS)$> - float metallic = getMaterialMetallic(mat); - <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; - - float scattering = getMaterialScattering(mat); - packDeferredFragment( - normalize(fragNormalWS.xyz), + normalize(fragNormalWS), opacity, albedo, roughness, metallic, emissive + fadeEmissive, - occlusionTex, + occlusion, scattering); } diff --git a/libraries/render-utils/src/model_shadow.slf b/libraries/render-utils/src/model_shadow.slf index 6426759ec7..862fcd0cf6 100644 --- a/libraries/render-utils/src/model_shadow.slf +++ b/libraries/render-utils/src/model_shadow.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_shadow.frag -// fragment shader -// // Created by Andrzej Kapolka on 3/24/14. // Copyright 2013 High Fidelity, Inc. // diff --git a/libraries/render-utils/src/model_shadow_fade.slf b/libraries/render-utils/src/model_shadow_fade.slf index c6c8c23f65..210b5482c8 100644 --- a/libraries/render-utils/src/model_shadow_fade.slf +++ b/libraries/render-utils/src/model_shadow_fade.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_shadow_fade.frag -// fragment shader -// // Created by Olivier Prat on 06/05/17. // Copyright 2017 High Fidelity, Inc. // @@ -12,19 +10,17 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include Fade.slh@> <@include render-utils/ShaderConstants.h@> +<@include Fade.slh@> <$declareFadeFragment()$> - layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _positionWS; layout(location=0) out vec4 _fragColor; void main(void) { FadeObjectParams fadeParams; - <$fetchFadeObjectParams(fadeParams)$> applyFadeClip(fadeParams, _positionWS.xyz); diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 00a871ace1..72f6b4c187 100644 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_translucent.frag -// fragment shader -// // Created by Sam Gateau on 2/15/2016. // Copyright 2014 High Fidelity, Inc. // @@ -12,19 +10,20 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include gpu/Transform.slh@> +<@include DefaultMaterials.slh@> <@include graphics/Material.slh@> <@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> + <@include LightLocal.slh@> -<@include ShadingModel.slh@> <@include DeferredGlobalLight.slh@> <$declareEvalGlobalLightingAlphaBlendedWithHaze()$> +<@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> -<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$> layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord0 _texCoord01.xy @@ -39,7 +38,7 @@ layout(location=0) out vec4 _fragColor; void main(void) { Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); - <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex)$> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> float opacity = getMaterialOpacity(mat) * _color.a; @@ -54,11 +53,16 @@ void main(void) { <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 fresnel = getFresnelF0(metallic, albedo); vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; + vec3 fragPositionES = _positionES.xyz; vec3 fragPositionWS = _positionWS.xyz; // Lighting is done in world space @@ -70,7 +74,6 @@ void main(void) { SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragToEyeDirWS); vec4 localLighting = vec4(0.0); - <$fetchClusterInfo(_positionWS)$>; if (hasLocalLights(numLights, clusterPos, dims)) { localLighting = evalLocalLighting(cluster, numLights, fragPositionWS, surfaceWS, @@ -81,7 +84,7 @@ void main(void) { _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( cam._viewInverse, 1.0, - occlusionTex, + occlusion, fragPositionES, fragPositionWS, albedo, diff --git a/libraries/render-utils/src/model_translucent_fade.slf b/libraries/render-utils/src/model_translucent_fade.slf index 3cebc59ea7..7e170759c4 100644 --- a/libraries/render-utils/src/model_translucent_fade.slf +++ b/libraries/render-utils/src/model_translucent_fade.slf @@ -1,29 +1,33 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> -// model_translucent_fade.frag +// // Created by Olivier Prat on 06/05/17. // Copyright 2017 High Fidelity, Inc. +// // Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +<@include DefaultMaterials.slh@> <@include graphics/Material.slh@> +<@include graphics/MaterialTextures.slh@> +<@include render-utils/ShaderConstants.h@> + +<@include LightLocal.slh@> <@include DeferredGlobalLight.slh@> <$declareEvalGlobalLightingAlphaBlendedWithHaze()$> -<@include LightLocal.slh@> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> -<@include graphics/MaterialTextures.slh@> -<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$> <@include Fade.slh@> <$declareFadeFragment()$> -<@include render-utils/ShaderConstants.h@> - layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord0 _texCoord01.xy #define _texCoord1 _texCoord01.zw @@ -37,13 +41,12 @@ layout(location=0) out vec4 _fragColor; void main(void) { vec3 fadeEmissive; FadeObjectParams fadeParams; - <$fetchFadeObjectParams(fadeParams)$> applyFade(fadeParams, _positionWS.xyz, fadeEmissive); Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); - <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex)$> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> float opacity = getMaterialOpacity(mat) * _color.a; @@ -58,11 +61,16 @@ void main(void) { <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 fresnel = getFresnelF0(metallic, albedo); vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; + vec3 fragPositionES = _positionES.xyz; vec3 fragPositionWS = _positionWS.xyz; // Lighting is done in world space @@ -74,7 +82,6 @@ void main(void) { SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragToEyeDirWS); vec4 localLighting = vec4(0.0); - <$fetchClusterInfo(_positionWS)$>; if (hasLocalLights(numLights, clusterPos, dims)) { localLighting = evalLocalLighting(cluster, numLights, fragPositionWS, surfaceWS, @@ -85,7 +92,7 @@ void main(void) { _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( cam._viewInverse, 1.0, - occlusionTex, + occlusion, fragPositionES, fragPositionWS, albedo, diff --git a/libraries/render-utils/src/model_translucent_normal_map.slf b/libraries/render-utils/src/model_translucent_normal_map.slf index 7ac6982cfa..b3a9127acf 100644 --- a/libraries/render-utils/src/model_translucent_normal_map.slf +++ b/libraries/render-utils/src/model_translucent_normal_map.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_translucent_normal_map.frag -// fragment shader -// // Created by Olivier Prat on 23/01/2018. // Copyright 2018 High Fidelity, Inc. // @@ -12,21 +10,20 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +<@include DefaultMaterials.slh@> <@include graphics/Material.slh@> +<@include graphics/MaterialTextures.slh@> +<@include render-utils/ShaderConstants.h@> +<@include LightLocal.slh@> <@include DeferredGlobalLight.slh@> <$declareEvalGlobalLightingAlphaBlendedWithHaze()$> -<@include LightLocal.slh@> - <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> -<@include graphics/MaterialTextures.slh@> -<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> - -<@include render-utils/ShaderConstants.h@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$> layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord0 _texCoord01.xy @@ -42,7 +39,7 @@ layout(location=0) out vec4 _fragColor; void main(void) { Material mat = getMaterial(); int matKey = getMaterialKey(mat); - <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex)$> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> float opacity = getMaterialOpacity(mat) * _color.a; @@ -57,11 +54,16 @@ void main(void) { <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 fresnel = getFresnelF0(metallic, albedo); vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; + vec3 fragPositionES = _positionES.xyz; vec3 fragPositionWS = _positionWS.xyz; // Lighting is done in world space @@ -71,10 +73,9 @@ void main(void) { TransformCamera cam = getTransformCamera(); vec3 fragToEyeWS = cam._viewInverse[3].xyz - fragPositionWS; vec3 fragToEyeDirWS = normalize(fragToEyeWS); - SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragToEyeDirWS); + SurfaceData surfaceWS = initSurfaceData(roughness, normalize(fragNormalWS), fragToEyeDirWS); vec4 localLighting = vec4(0.0); - <$fetchClusterInfo(_positionWS)$>; if (hasLocalLights(numLights, clusterPos, dims)) { localLighting = evalLocalLighting(cluster, numLights, fragPositionWS, surfaceWS, @@ -85,7 +86,7 @@ void main(void) { _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( cam._viewInverse, 1.0, - occlusionTex, + occlusion, fragPositionES, fragPositionWS, albedo, diff --git a/libraries/render-utils/src/model_translucent_normal_map_fade.slf b/libraries/render-utils/src/model_translucent_normal_map_fade.slf index 2c182aeb19..4c56ebda2e 100644 --- a/libraries/render-utils/src/model_translucent_normal_map_fade.slf +++ b/libraries/render-utils/src/model_translucent_normal_map_fade.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_translucent_normal_map_fade.frag -// fragment shader -// // Created by Olivier Prat on 23/01/18. // Copyright 2018 High Fidelity, Inc. // @@ -12,47 +10,44 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include render-utils/ShaderConstants.h@> +<@include DefaultMaterials.slh@> <@include graphics/Material.slh@> +<@include graphics/MaterialTextures.slh@> +<@include render-utils/ShaderConstants.h@> +<@include LightLocal.slh@> <@include DeferredGlobalLight.slh@> <$declareEvalGlobalLightingAlphaBlendedWithHaze()$> -<@include LightLocal.slh@> - <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> -<@include graphics/MaterialTextures.slh@> -<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$> <@include Fade.slh@> <$declareFadeFragment()$> -<@include render-utils/ShaderConstants.h@> - layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord0 _texCoord01.xy #define _texCoord1 _texCoord01.zw layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES; +layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _positionWS; layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; layout(location=RENDER_UTILS_ATTR_TANGENT_WS) in vec3 _tangentWS; layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; -layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _positionWS; layout(location=0) out vec4 _fragColor; void main(void) { vec3 fadeEmissive; FadeObjectParams fadeParams; - <$fetchFadeObjectParams(fadeParams)$> applyFade(fadeParams, _positionWS.xyz, fadeEmissive); Material mat = getMaterial(); int matKey = getMaterialKey(mat); - <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, metallicTex, emissiveTex)$> <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> float opacity = getMaterialOpacity(mat) * _color.a; @@ -67,11 +62,16 @@ void main(void) { <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; float metallic = getMaterialMetallic(mat); + <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 fresnel = getFresnelF0(metallic, albedo); vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; + vec3 fragPositionES = _positionES.xyz; vec3 fragPositionWS = _positionWS.xyz; // Lighting is done in world space @@ -81,10 +81,9 @@ void main(void) { TransformCamera cam = getTransformCamera(); vec3 fragToEyeWS = cam._viewInverse[3].xyz - fragPositionWS; vec3 fragToEyeDirWS = normalize(fragToEyeWS); - SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragToEyeDirWS); + SurfaceData surfaceWS = initSurfaceData(roughness, normalize(fragNormalWS), fragToEyeDirWS); vec4 localLighting = vec4(0.0); - <$fetchClusterInfo(_positionWS)$>; if (hasLocalLights(numLights, clusterPos, dims)) { localLighting = evalLocalLighting(cluster, numLights, fragPositionWS, surfaceWS, @@ -95,7 +94,7 @@ void main(void) { _fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze( cam._viewInverse, 1.0, - occlusionTex, + occlusion, fragPositionES, fragPositionWS, albedo, diff --git a/libraries/render-utils/src/model_translucent_unlit.slf b/libraries/render-utils/src/model_translucent_unlit.slf index 1e468791f4..37f58d3da9 100644 --- a/libraries/render-utils/src/model_translucent_unlit.slf +++ b/libraries/render-utils/src/model_translucent_unlit.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_translucent_unlit.frag -// fragment shader -// // Created by Zach Pomerantz on 2/3/2016. // Copyright 2016 High Fidelity, Inc. // @@ -13,12 +11,12 @@ // <@include graphics/Material.slh@> - <@include graphics/MaterialTextures.slh@> -<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> +<@include render-utils/ShaderConstants.h@> + <@include LightingModel.slh@> -<@include render-utils/ShaderConstants.h@> +<$declareMaterialTextures(ALBEDO)$> layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord0 _texCoord01.xy diff --git a/libraries/render-utils/src/model_translucent_unlit_fade.slf b/libraries/render-utils/src/model_translucent_unlit_fade.slf index cbbaae8641..04d57b7c47 100644 --- a/libraries/render-utils/src/model_translucent_unlit_fade.slf +++ b/libraries/render-utils/src/model_translucent_unlit_fade.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_translucent_unlit_fade.frag -// fragment shader -// // Created by Olivier Prat on 06/05/17. // Copyright 2017 High Fidelity, Inc. // @@ -13,16 +11,16 @@ // <@include graphics/Material.slh@> - <@include graphics/MaterialTextures.slh@> -<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> +<@include render-utils/ShaderConstants.h@> + <@include LightingModel.slh@> +<$declareMaterialTextures(ALBEDO)$> + <@include Fade.slh@> <$declareFadeFragment()$> -<@include render-utils/ShaderConstants.h@> - layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; #define _texCoord0 _texCoord01.xy #define _texCoord1 _texCoord01.zw @@ -34,7 +32,6 @@ layout(location=0) out vec4 _fragColor; void main(void) { vec3 fadeEmissive; FadeObjectParams fadeParams; - <$fetchFadeObjectParams(fadeParams)$> applyFade(fadeParams, _positionWS.xyz, fadeEmissive); @@ -50,5 +47,6 @@ void main(void) { <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; albedo *= _color.rgb; albedo += fadeEmissive; + _fragColor = vec4(albedo * isUnlitEnabled(), opacity); } diff --git a/libraries/render-utils/src/model_unlit.slf b/libraries/render-utils/src/model_unlit.slf index b14a807eb5..cbac67a72e 100644 --- a/libraries/render-utils/src/model_unlit.slf +++ b/libraries/render-utils/src/model_unlit.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// material_opaque_unlit.frag -// fragment shader -// // Created by Sam Gateau on 5/5/2016. // Copyright 2016 High Fidelity, Inc. // @@ -12,12 +10,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> -<@include LightingModel.slh@> <@include graphics/Material.slh@> <@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> +<@include DeferredBufferWrite.slh@> +<@include LightingModel.slh@> + <$declareMaterialTextures(ALBEDO)$> layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; @@ -27,7 +26,6 @@ layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS; layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; void main(void) { - Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$> diff --git a/libraries/render-utils/src/model_unlit_fade.slf b/libraries/render-utils/src/model_unlit_fade.slf index cb5c72bdf2..3097c04c26 100644 --- a/libraries/render-utils/src/model_unlit_fade.slf +++ b/libraries/render-utils/src/model_unlit_fade.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// model_unlit_fade.frag -// fragment shader -// // Created by Olivier Prat on 06/05/17. // Copyright 2017 High Fidelity, Inc. // @@ -12,13 +10,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include DeferredBufferWrite.slh@> -<@include LightingModel.slh@> <@include graphics/Material.slh@> <@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> -<@include Fade.slh@> +<@include DeferredBufferWrite.slh@> +<@include LightingModel.slh@> + +<@include Fade.slh@> <$declareFadeFragment()$> <$declareMaterialTextures(ALBEDO)$> @@ -33,7 +32,6 @@ layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; void main(void) { vec3 fadeEmissive; FadeObjectParams fadeParams; - <$fetchFadeObjectParams(fadeParams)$> applyFade(fadeParams, _positionWS.xyz, fadeEmissive); @@ -49,6 +47,7 @@ void main(void) { <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; albedo *= _color.rgb; albedo += fadeEmissive; + packDeferredFragmentUnlit( normalize(_normalWS), opacity, diff --git a/libraries/render-utils/src/parabola_translucent.slf b/libraries/render-utils/src/parabola_translucent.slf index 62b5b15193..01f4614172 100644 --- a/libraries/render-utils/src/parabola_translucent.slf +++ b/libraries/render-utils/src/parabola_translucent.slf @@ -14,5 +14,5 @@ layout(location=0) in vec4 _color; void main(void) { - packDeferredFragmentTranslucent(vec3(1.0, 0.0, 0.0), _color.a, _color.rgb, DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); + packDeferredFragmentTranslucent(vec3(1.0, 0.0, 0.0), _color.a, _color.rgb, DEFAULT_ROUGHNESS); } diff --git a/libraries/render-utils/src/sdf_text3D_transparent.slf b/libraries/render-utils/src/sdf_text3D_transparent.slf index 9dffca2038..615f4e010f 100644 --- a/libraries/render-utils/src/sdf_text3D_transparent.slf +++ b/libraries/render-utils/src/sdf_text3D_transparent.slf @@ -60,6 +60,5 @@ void main() { normalize(_normalWS), a * params.color.a, params.color.rgb, - DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); } \ No newline at end of file diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index a7f5151880..5cceec0ac2 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -65,7 +65,6 @@ void main(void) { diffuse, max(0.0, 1.0 - shininess / 128.0), DEFAULT_METALLIC, - specular, vec3(clamp(emissiveAmount, 0.0, 1.0))); } else { packDeferredFragment( diff --git a/libraries/render-utils/src/simple_fade.slf b/libraries/render-utils/src/simple_fade.slf index 97ed0c570c..140c6b698b 100644 --- a/libraries/render-utils/src/simple_fade.slf +++ b/libraries/render-utils/src/simple_fade.slf @@ -75,14 +75,12 @@ void main(void) { normal, _color.a, specular+fadeEmissive, - DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); } else { packDeferredFragmentTranslucent( normal, _color.a, diffuse+fadeEmissive, - DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); } } else { @@ -93,7 +91,6 @@ void main(void) { diffuse+fadeEmissive, max(0.0, 1.0 - shininess / 128.0), DEFAULT_METALLIC, - specular, specular); } else { packDeferredFragment( diff --git a/libraries/render-utils/src/simple_textured_fade.slf b/libraries/render-utils/src/simple_textured_fade.slf index 600f19be0f..79ba571671 100644 --- a/libraries/render-utils/src/simple_textured_fade.slf +++ b/libraries/render-utils/src/simple_textured_fade.slf @@ -53,7 +53,6 @@ void main(void) { normalize(_normalWS), colorAlpha * texel.a, _color.rgb * texel.rgb + fadeEmissive, - DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); } else { packDeferredFragment( diff --git a/libraries/render-utils/src/simple_textured_unlit.slf b/libraries/render-utils/src/simple_textured_unlit.slf index e3d9b9daf6..c0d86e9b99 100644 --- a/libraries/render-utils/src/simple_textured_unlit.slf +++ b/libraries/render-utils/src/simple_textured_unlit.slf @@ -41,7 +41,6 @@ void main(void) { normalize(_normalWS), colorAlpha * texel.a, _color.rgb * texel.rgb, - DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); } else { packDeferredFragmentUnlit( diff --git a/libraries/render-utils/src/simple_textured_unlit_fade.slf b/libraries/render-utils/src/simple_textured_unlit_fade.slf index bffadbe819..b7f03c5df5 100644 --- a/libraries/render-utils/src/simple_textured_unlit_fade.slf +++ b/libraries/render-utils/src/simple_textured_unlit_fade.slf @@ -53,7 +53,6 @@ void main(void) { normalize(_normalWS), colorAlpha * texel.a, _color.rgb * texel.rgb+fadeEmissive, - DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); } else { packDeferredFragmentUnlit( diff --git a/libraries/render-utils/src/simple_transparent_textured.slf b/libraries/render-utils/src/simple_transparent_textured.slf index 5573a7aa22..39e40bb9a6 100644 --- a/libraries/render-utils/src/simple_transparent_textured.slf +++ b/libraries/render-utils/src/simple_transparent_textured.slf @@ -34,6 +34,5 @@ void main(void) { normalize(_normalWS), colorAlpha, _color.rgb * texel.rgb, - DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); } \ No newline at end of file diff --git a/libraries/render-utils/src/simple_transparent_web_browser.slf b/libraries/render-utils/src/simple_transparent_web_browser.slf index df92d238bf..3c9b34fbfa 100644 --- a/libraries/render-utils/src/simple_transparent_web_browser.slf +++ b/libraries/render-utils/src/simple_transparent_web_browser.slf @@ -34,6 +34,5 @@ void main(void) { normalize(_normalWS), _color.a, _color.rgb * texel.rgb, - DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); } From 0f24c18de538d58601a083b71b727963786f8591 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 26 Sep 2018 14:44:19 -0700 Subject: [PATCH 005/151] slightly fix forward pipelines --- .../graphics/src/graphics/MaterialTextures.slh | 10 ---------- libraries/render-utils/src/RenderPipelines.cpp | 4 +++- libraries/render-utils/src/forward_model.slf | 15 +++++++++------ .../src/forward_model_normal_map.slf | 18 +++++++++++------- .../src/forward_model_translucent.slf | 15 ++++++++++----- .../render-utils/src/forward_model_unlit.slf | 11 +++-------- 6 files changed, 36 insertions(+), 37 deletions(-) diff --git a/libraries/graphics/src/graphics/MaterialTextures.slh b/libraries/graphics/src/graphics/MaterialTextures.slh index 106453877c..a01eb5dcd0 100644 --- a/libraries/graphics/src/graphics/MaterialTextures.slh +++ b/libraries/graphics/src/graphics/MaterialTextures.slh @@ -226,16 +226,6 @@ vec3 fetchLightmapMap(vec2 uv) { } <@endfunc@> -<@func evalMaterialNormal(fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> -{ - vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz); - vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz); - vec3 normalizedBitangent = cross(normalizedNormal, normalizedTangent); - vec3 localNormal = <$fetchedNormal$>; - <$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z); -} -<@endfunc@> - <@func evalMaterialNormalLOD(fragPosES, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@> { vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz); diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 704b1d7663..84f0835651 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -254,7 +254,7 @@ void initForwardPipelines(ShapePlumber& plumber) { // Opaques addPipeline(Key::Builder().withMaterial(), forward_model); addPipeline(Key::Builder().withMaterial().withUnlit(), forward_model_unlit); - addPipeline(Key::Builder().withMaterial().withTangents(), forward_model_translucent); + addPipeline(Key::Builder().withMaterial().withTangents(), forward_model_normal_map); // Skinned Opaques addPipeline(Key::Builder().withMaterial().withSkinned(), forward_skin_model); @@ -272,6 +272,8 @@ void initForwardPipelines(ShapePlumber& plumber) { addPipeline(Key::Builder().withMaterial().withSkinned().withTranslucent().withDualQuatSkinned(), forward_skin_translucent_dq); addPipeline(Key::Builder().withMaterial().withSkinned().withTranslucent().withTangents().withDualQuatSkinned(), forward_skin_translucent_normal_map_dq); + // FIXME: incorrent pipelines for normal mapped + translucent models + forceLightBatchSetter = false; } diff --git a/libraries/render-utils/src/forward_model.slf b/libraries/render-utils/src/forward_model.slf index f77ab358f2..daccd9c6d6 100644 --- a/libraries/render-utils/src/forward_model.slf +++ b/libraries/render-utils/src/forward_model.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// forward_model.frag -// fragment shader -// // Created by Sam Gateau on 2/15/2016. // Copyright 2014 High Fidelity, Inc. // @@ -12,15 +10,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include gpu/Transform.slh@> +<@include DefaultMaterials.slh@> <@include graphics/Material.slh@> <@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> + <@include ForwardGlobalLight.slh@> <$declareEvalSkyboxGlobalColor()$> - +<@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$> @@ -56,8 +55,12 @@ void main(void) { float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 fresnel = getFresnelF0(metallic, albedo); + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; + vec3 fragPosition = _positionES.xyz; vec3 fragNormal = normalize(_normalWS); @@ -66,7 +69,7 @@ void main(void) { vec4 color = vec4(evalSkyboxGlobalColor( cam._viewInverse, 1.0, - occlusionTex, + occlusion, fragPosition, fragNormal, albedo, diff --git a/libraries/render-utils/src/forward_model_normal_map.slf b/libraries/render-utils/src/forward_model_normal_map.slf index 73fae33fb0..33e375c495 100644 --- a/libraries/render-utils/src/forward_model_normal_map.slf +++ b/libraries/render-utils/src/forward_model_normal_map.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// forward_model_normal_map.frag -// fragment shader -// // Created by Sam Gateau on 2/15/2016. // Copyright 2014 High Fidelity, Inc. // @@ -12,13 +10,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include gpu/Transform.slh@> +<@include DefaultMaterials.slh@> <@include graphics/Material.slh@> <@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> + <@include ForwardGlobalLight.slh@> <$declareEvalSkyboxGlobalColor()$> + +<@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$> @@ -55,18 +56,22 @@ void main(void) { float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 fresnel = getFresnelF0(metallic, albedo); + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; + vec3 fragPosition = _positionES.xyz; vec3 fragNormal; - <$evalMaterialNormal(normalTex, _normalWS, _tangentWS, fragNormal)$> + <$evalMaterialNormalLOD(fragPosition, normalTex, _normalWS, _tangentWS, fragNormal)$> TransformCamera cam = getTransformCamera(); vec4 color = vec4(evalSkyboxGlobalColor( cam._viewInverse, 1.0, - occlusionTex, + occlusion, fragPosition, fragNormal, albedo, @@ -74,7 +79,6 @@ void main(void) { metallic, roughness), opacity); - color.rgb += emissive * isEmissiveEnabled(); _fragColor0 = color; diff --git a/libraries/render-utils/src/forward_model_translucent.slf b/libraries/render-utils/src/forward_model_translucent.slf index 5fb2c7c1a7..080ed7eea3 100644 --- a/libraries/render-utils/src/forward_model_translucent.slf +++ b/libraries/render-utils/src/forward_model_translucent.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// forward_model_translucent.frag -// fragment shader -// // Created by Sam Gateau on 2/15/2016. // Copyright 2014 High Fidelity, Inc. // @@ -12,13 +10,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include gpu/Transform.slh@> +<@include DefaultMaterials.slh@> <@include graphics/Material.slh@> <@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> + <@include ForwardGlobalLight.slh@> <$declareEvalGlobalLightingAlphaBlended()$> + +<@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$> @@ -51,8 +52,12 @@ void main(void) { float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 fresnel = getFresnelF0(metallic, albedo); + float occlusion = DEFAULT_OCCLUSION; + <$evalMaterialOcclusion(occlusionTex, matKey, occlusion)$>; + vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; @@ -64,7 +69,7 @@ void main(void) { _fragColor0 = vec4(evalGlobalLightingAlphaBlendedWithHaze( cam._viewInverse, 1.0, - occlusionTex, + occlusion, fragPosition, fragNormal, albedo, diff --git a/libraries/render-utils/src/forward_model_unlit.slf b/libraries/render-utils/src/forward_model_unlit.slf index 19b40d884c..ccd264f0bf 100644 --- a/libraries/render-utils/src/forward_model_unlit.slf +++ b/libraries/render-utils/src/forward_model_unlit.slf @@ -1,10 +1,8 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> +// <$_SCRIBE_FILENAME$> // Generated on <$_SCRIBE_DATE$> // -// forward_model_unlit.frag -// fragment shader -// // Created by Sam Gateau on 5/5/2016. // Copyright 2016 High Fidelity, Inc. // @@ -12,11 +10,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include LightingModel.slh@> <@include graphics/Material.slh@> <@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> +<@include LightingModel.slh@> + <$declareMaterialTextures(ALBEDO)$> layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; @@ -27,7 +26,6 @@ layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color; layout(location=0) out vec4 _fragColor0; void main(void) { - Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex)$> @@ -40,8 +38,5 @@ void main(void) { <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; albedo *= _color.rgb; - if (opacity != 1.0) { - discard; - } _fragColor0 = vec4(albedo * isUnlitEnabled(), 1.0); } From e0698179782a31e8ac823f9e32e8f0367cd81169 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 27 Sep 2018 14:18:43 -0700 Subject: [PATCH 006/151] more fixes --- libraries/gpu/src/gpu/TextureTable.slh | 2 +- libraries/graphics/src/graphics/BufferViewHelpers.cpp | 2 +- libraries/graphics/src/graphics/MaterialTextures.slh | 2 +- libraries/render-utils/src/model_lightmap.slf | 2 +- libraries/render-utils/src/model_lightmap_fade.slf | 2 +- libraries/render-utils/src/model_lightmap_normal_map.slf | 2 +- libraries/render-utils/src/model_lightmap_normal_map_fade.slf | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/gpu/src/gpu/TextureTable.slh b/libraries/gpu/src/gpu/TextureTable.slh index bedbff954b..f8cc4b0599 100644 --- a/libraries/gpu/src/gpu/TextureTable.slh +++ b/libraries/gpu/src/gpu/TextureTable.slh @@ -22,7 +22,7 @@ struct GPUTextureTable { #define tableTex(name, slot) sampler2D(name._textures[slot].xy) #define tableTexMinLod(name, slot) float(name._textures[slot].z) -#define tableTexValue(name, slot, uv) tableTexValueLod(tableTex(matTex, albedoMap), tableTexMinLod(matTex, albedoMap), uv) +#define tableTexValue(name, slot, uv) tableTexValueLod(tableTex(matTex, slot), tableTexMinLod(matTex, slot), uv) vec4 tableTexValueLod(sampler2D sampler, float minLod, vec2 uv) { float queryLod = textureQueryLod(sampler, uv).x; diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp index 2fd0d90da3..4c57abdfd4 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -38,7 +38,7 @@ QMap ATTRIBUTES{ {"position", gpu::Stream::POSITION }, {"normal", gpu::Stream::NORMAL }, {"color", gpu::Stream::COLOR }, - {"tangent", gpu::Stream::TEXCOORD0 }, + {"tangent", gpu::Stream::TANGENT }, {"skin_cluster_index", gpu::Stream::SKIN_CLUSTER_INDEX }, {"skin_cluster_weight", gpu::Stream::SKIN_CLUSTER_WEIGHT }, {"texcoord0", gpu::Stream::TEXCOORD0 }, diff --git a/libraries/graphics/src/graphics/MaterialTextures.slh b/libraries/graphics/src/graphics/MaterialTextures.slh index a01eb5dcd0..555bfa9cf0 100644 --- a/libraries/graphics/src/graphics/MaterialTextures.slh +++ b/libraries/graphics/src/graphics/MaterialTextures.slh @@ -208,7 +208,7 @@ float fetchScatteringMap(vec2 uv) { <@if occlusion@> float <$occlusion$> = (((<$matKey$> & OCCLUSION_MAP_BIT) != 0) ? fetchOcclusionMap(<$texcoord1$>) : 1.0); <@endif@> -<@if lightmapVal@> +<@if lightmap@> vec3 <$lightmap$> = fetchLightmapMap(<$texcoord1$>); <@endif@> <@endfunc@> diff --git a/libraries/render-utils/src/model_lightmap.slf b/libraries/render-utils/src/model_lightmap.slf index ada49fdd08..1be247e3e9 100644 --- a/libraries/render-utils/src/model_lightmap.slf +++ b/libraries/render-utils/src/model_lightmap.slf @@ -42,7 +42,7 @@ void main(void) { packDeferredFragmentLightmap( normalize(_normalWS), - evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), + evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a), albedo, roughness, metallic, diff --git a/libraries/render-utils/src/model_lightmap_fade.slf b/libraries/render-utils/src/model_lightmap_fade.slf index 3819c4d4fa..61568463a7 100644 --- a/libraries/render-utils/src/model_lightmap_fade.slf +++ b/libraries/render-utils/src/model_lightmap_fade.slf @@ -51,7 +51,7 @@ void main(void) { packDeferredFragmentLightmap( normalize(_normalWS), - evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), + evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a), albedo, roughness, metallic, diff --git a/libraries/render-utils/src/model_lightmap_normal_map.slf b/libraries/render-utils/src/model_lightmap_normal_map.slf index adeff463b5..3d961584c2 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map.slf +++ b/libraries/render-utils/src/model_lightmap_normal_map.slf @@ -47,7 +47,7 @@ void main(void) { packDeferredFragmentLightmap( normalize(fragNormal), - evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), + evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a), albedo, roughness, metallic, diff --git a/libraries/render-utils/src/model_lightmap_normal_map_fade.slf b/libraries/render-utils/src/model_lightmap_normal_map_fade.slf index c46308fcf3..f873847474 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map_fade.slf +++ b/libraries/render-utils/src/model_lightmap_normal_map_fade.slf @@ -56,7 +56,7 @@ void main(void) { packDeferredFragmentLightmap( normalize(fragNormal), - evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedo.a), + evalOpaqueFinalAlpha(getMaterialOpacity(mat), albedoTex.a), albedo, roughness, metallic, From f8285ab3d82d5fae924041a1f1b03c1b4db2ff54 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 5 Oct 2018 00:14:13 +0300 Subject: [PATCH 007/151] FB18673 - Last line of GOTO desktop notification text cropped off on bottom --- scripts/system/notifications.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 36fe264274..87a70fa5fe 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -343,6 +343,7 @@ } var CLOSE_NOTIFICATION_ICON = Script.resolvePath("assets/images/close-small-light.svg"); + var TEXT_OVERLAY_FONT_SIZE_IN_PIXELS = 18.0; // taken from TextOverlay::textSize // This function creates and sizes the overlays function createNotification(text, notificationType, imageProperties) { @@ -362,7 +363,7 @@ if (text.length >= breakPoint) { breaks = count; } - extraLine = breaks * 16.0; + extraLine = breaks * TEXT_OVERLAY_FONT_SIZE_IN_PIXELS; for (i = 0; i < heights.length; i += 1) { stack = stack + heights[i]; } From c938c595c017147b9210b6a0b03d0e891cfb05bb Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 10 Oct 2018 17:11:42 -0700 Subject: [PATCH 008/151] Detect failed conversion from QVariant to JSON better --- libraries/octree/src/Octree.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 3d387e0956..37dd4e410f 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -878,14 +878,24 @@ bool Octree::toJSONDocument(QJsonDocument* doc, const OctreeElementPointer& elem return false; } - *doc = QJsonDocument::fromVariant(entityDescription); + + bool noEntities = entityDescription["Entities"].toList().empty(); + QJsonDocument jsonDocTree = QJsonDocument::fromVariant(entityDescription); + QJsonValue entitiesJson = jsonDocTree["Entities"]; + if (entitiesJson.isNull() || (entitiesJson.toArray().empty() && !noEntities)) { + // Json version of entities too large. + return false; + } else { + *doc = jsonDocTree; + } + return true; } bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool doGzip) { QJsonDocument doc; if (!toJSONDocument(&doc, element)) { - qCritical("Failed to convert Entities to QVariantMap while converting to json."); + qCritical("Failed to convert Entities to JSON document."); return false; } From 0eec0e376c8102ebc931f2cddbf9bdede0b082da Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 11 Oct 2018 18:13:26 -0700 Subject: [PATCH 009/151] Convert EntityTree direct to JSON, not via QJsonDocument --- libraries/entities/src/EntityTree.cpp | 12 +++++ libraries/entities/src/EntityTree.h | 2 + .../src/RecurseOctreeToJSONOperator.cpp | 45 +++++++++++++++++ .../src/RecurseOctreeToJSONOperator.h | 31 ++++++++++++ libraries/octree/src/Octree.cpp | 50 ++++++++++++++++++- libraries/octree/src/Octree.h | 2 + 6 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 libraries/entities/src/RecurseOctreeToJSONOperator.cpp create mode 100644 libraries/entities/src/RecurseOctreeToJSONOperator.h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a7c88ddc7d..aa2167029d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -35,6 +35,7 @@ #include "QVariantGLM.h" #include "EntitiesLogging.h" #include "RecurseOctreeToMapOperator.h" +#include "RecurseOctreeToJsonOperator.h" #include "LogHandler.h" #include "EntityEditFilters.h" #include "EntityDynamicFactoryInterface.h" @@ -2658,6 +2659,17 @@ bool EntityTree::readFromMap(QVariantMap& map) { return success; } +bool EntityTree::writeToJSON(QString & jsonString, const OctreeElementPointer & element) { + QScriptEngine scriptEngine; + RecurseOctreeToJSONOperator theOperator(element, &scriptEngine, jsonString); + withReadLock([&] { + recurseTreeWithOperator(&theOperator); + }); + + jsonString = theOperator.getJson(); + return true; +} + void EntityTree::resetClientEditStats() { _treeResetTime = usecTimestampNow(); _maxEditDelta = 0; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 2f971b8566..b852a695d8 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -224,6 +224,8 @@ public: virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues, bool skipThoseWithBadParents) override; virtual bool readFromMap(QVariantMap& entityDescription) override; + virtual bool writeToJSON(QString& jsonString, const OctreeElementPointer& element) override; + glm::vec3 getContentsDimensions(); float getContentsLargestDimension(); diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp new file mode 100644 index 0000000000..2db1a2b228 --- /dev/null +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp @@ -0,0 +1,45 @@ +// +// RecurseOctreeToJSONOperator.cpp +// libraries/entities/src +// +// Created by Simon Walton on Oct 11, 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 "RecurseOctreeToJSONOperator.h" + +#include "EntityItemProperties.h" + +RecurseOctreeToJSONOperator::RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, + QString jsonPrefix /* = QString() */) + : _top(top) + , _engine(engine) + , _json(jsonPrefix) +{ + _toStringMethod = _engine->evaluate("(function() { return JSON.stringify(this, null, ' ') })"); +} + +bool RecurseOctreeToJSONOperator::postRecursion(const OctreeElementPointer& element) { + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + + entityTreeElement->forEachEntity([&](const EntityItemPointer& entity) { processEntity(entity); } ); + return true; +} + +void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) { + QScriptValue qScriptValues = EntityItemNonDefaultPropertiesToScriptValue(_engine, entity->getProperties()); + if (comma) { + _json += ','; + }; + comma = true; + _json += "\n "; + + // Override default toString(): + qScriptValues.setProperty("toString", _toStringMethod); + QString jsonResult = qScriptValues.toString(); + //auto exceptionString2 = _engine->uncaughtException().toString(); + _json += jsonResult; +} diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.h b/libraries/entities/src/RecurseOctreeToJSONOperator.h new file mode 100644 index 0000000000..92ce6bfdca --- /dev/null +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.h @@ -0,0 +1,31 @@ +// +// RecurseOctreeToJSONOperator.h +// libraries/entities/src +// +// Created by Simon Walton on Oct 11, 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 "EntityTree.h" + +class RecurseOctreeToJSONOperator : public RecurseOctreeOperator { +public: + RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, QString jsonPrefix = QString()); + virtual bool preRecursion(const OctreeElementPointer& element) override { return true; }; + virtual bool postRecursion(const OctreeElementPointer& element) override; + + QString getJson() const { return _json; } + +private: + void processEntity(const EntityItemPointer& entity); + + const OctreeElementPointer& _top; + QScriptEngine* _engine; + QScriptValue _toStringMethod; + + QString _json; + bool comma { false }; +}; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 37dd4e410f..40d2449a38 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -51,7 +51,6 @@ #include "OctreeQueryNode.h" #include "OctreeUtils.h" - QVector PERSIST_EXTENSIONS = {"json", "json.gz"}; Octree::Octree(bool shouldReaverage) : @@ -892,13 +891,61 @@ bool Octree::toJSONDocument(QJsonDocument* doc, const OctreeElementPointer& elem return true; } +bool Octree::toJSONString(QString& jsonString, const OctreeElementPointer& element) { + OctreeElementPointer top; + if (element) { + top = element; + } else { + top = _rootElement; + } + + jsonString += QString(R"({ + "DataVersion": %1, + "Entities": [)").arg(_persistDataVersion); + + writeToJSON(jsonString, top); + + // include the "bitstream" version + PacketType expectedType = expectedDataPacketType(); + PacketVersion expectedVersion = versionForPacketType(expectedType); + + jsonString += QString(R"( + ], + "Id": "%1", + "Version": %2 +} +)").arg(_persistID.toString()).arg((int)expectedVersion); + + return true; +} + bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool doGzip) { +#define HIFI_USE_DIRECT_TO_JSON +#ifdef HIFI_USE_DIRECT_TO_JSON + + QString jsonString; + toJSONString(jsonString); + + if (doGzip) { + if (!gzip(jsonString.toUtf8(), *data, -1)) { + qCritical("Unable to gzip data while saving to json."); + return false; + } + } else { + *data = jsonString.toUtf8(); + } + +#else + QJsonDocument doc; if (!toJSONDocument(&doc, element)) { qCritical("Failed to convert Entities to JSON document."); return false; } + QString jsonString; + toJSONString(jsonString); + if (doGzip) { QByteArray jsonData = doc.toJson(); @@ -909,6 +956,7 @@ bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool } else { *data = doc.toJson(); } +#endif // HIFI_USE_DIRECT_TO_JSON return true; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index a2b2f227cb..678d8aa5de 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -202,11 +202,13 @@ public: // Octree exporters bool toJSONDocument(QJsonDocument* doc, const OctreeElementPointer& element = nullptr); + bool toJSONString(QString& jsonString, const OctreeElementPointer& element = nullptr); bool toJSON(QByteArray* data, const OctreeElementPointer& element = nullptr, bool doGzip = false); bool writeToFile(const char* filename, const OctreeElementPointer& element = nullptr, QString persistAsFileType = "json.gz"); bool writeToJSONFile(const char* filename, const OctreeElementPointer& element = nullptr, bool doGzip = false); virtual bool writeToMap(QVariantMap& entityDescription, OctreeElementPointer element, bool skipDefaultValues, bool skipThoseWithBadParents) = 0; + virtual bool writeToJSON(QString& jsonString, const OctreeElementPointer& element) = 0; // Octree importers bool readFromFile(const char* filename); From ad9f7f3a1d97d1a3fe766fcdb2b8190764d5e44b Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 12 Oct 2018 11:18:38 -0700 Subject: [PATCH 010/151] Include the skipDefaults option in case we ever need it Also print errors from parsing entities file; other tweaks --- libraries/entities/src/EntityTree.cpp | 2 +- .../entities/src/RecurseOctreeToJSONOperator.cpp | 12 +++++++----- libraries/entities/src/RecurseOctreeToJSONOperator.h | 3 ++- libraries/octree/src/Octree.cpp | 2 +- libraries/octree/src/OctreeDataUtils.cpp | 6 +++++- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index aa2167029d..19182d8b22 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -2659,7 +2659,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { return success; } -bool EntityTree::writeToJSON(QString & jsonString, const OctreeElementPointer & element) { +bool EntityTree::writeToJSON(QString& jsonString, const OctreeElementPointer& element) { QScriptEngine scriptEngine; RecurseOctreeToJSONOperator theOperator(element, &scriptEngine, jsonString); withReadLock([&] { diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp index 2db1a2b228..d67e7176a3 100644 --- a/libraries/entities/src/RecurseOctreeToJSONOperator.cpp +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.cpp @@ -10,14 +10,14 @@ // #include "RecurseOctreeToJSONOperator.h" - #include "EntityItemProperties.h" RecurseOctreeToJSONOperator::RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, - QString jsonPrefix /* = QString() */) + QString jsonPrefix /* = QString() */, bool skipDefaults /* = true */) : _top(top) , _engine(engine) , _json(jsonPrefix) + , _skipDefaults(skipDefaults) { _toStringMethod = _engine->evaluate("(function() { return JSON.stringify(this, null, ' ') })"); } @@ -30,7 +30,10 @@ bool RecurseOctreeToJSONOperator::postRecursion(const OctreeElementPointer& elem } void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) { - QScriptValue qScriptValues = EntityItemNonDefaultPropertiesToScriptValue(_engine, entity->getProperties()); + QScriptValue qScriptValues = _skipDefaults + ? EntityItemNonDefaultPropertiesToScriptValue(_engine, entity->getProperties()) + : EntityItemPropertiesToScriptValue(_engine, entity->getProperties()); + if (comma) { _json += ','; }; @@ -39,7 +42,6 @@ void RecurseOctreeToJSONOperator::processEntity(const EntityItemPointer& entity) // Override default toString(): qScriptValues.setProperty("toString", _toStringMethod); - QString jsonResult = qScriptValues.toString(); + _json += qScriptValues.toString(); //auto exceptionString2 = _engine->uncaughtException().toString(); - _json += jsonResult; } diff --git a/libraries/entities/src/RecurseOctreeToJSONOperator.h b/libraries/entities/src/RecurseOctreeToJSONOperator.h index 92ce6bfdca..f661e08d5b 100644 --- a/libraries/entities/src/RecurseOctreeToJSONOperator.h +++ b/libraries/entities/src/RecurseOctreeToJSONOperator.h @@ -13,7 +13,7 @@ class RecurseOctreeToJSONOperator : public RecurseOctreeOperator { public: - RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, QString jsonPrefix = QString()); + RecurseOctreeToJSONOperator(const OctreeElementPointer& top, QScriptEngine* engine, QString jsonPrefix = QString(), bool skipDefaults = true); virtual bool preRecursion(const OctreeElementPointer& element) override { return true; }; virtual bool postRecursion(const OctreeElementPointer& element) override; @@ -27,5 +27,6 @@ private: QScriptValue _toStringMethod; QString _json; + const bool _skipDefaults; bool comma { false }; }; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 40d2449a38..d583901b41 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -956,8 +956,8 @@ bool Octree::toJSON(QByteArray* data, const OctreeElementPointer& element, bool } else { *data = doc.toJson(); } -#endif // HIFI_USE_DIRECT_TO_JSON +#endif // HIFI_USE_DIRECT_TO_JSON return true; } diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index 44a56fe97a..4b1700d593 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -35,7 +35,11 @@ bool readOctreeFile(QString path, QJsonDocument* doc) { jsonData = data; } - *doc = QJsonDocument::fromJson(jsonData); + QJsonParseError parserError; + *doc = QJsonDocument::fromJson(jsonData, &parserError); + if (parserError.error != QJsonParseError::NoError) { + qWarning() << "Error reading JSON file" << path << "-" << parserError.errorString(); + } return !doc->isNull(); } From ca4fe0682e1d555e4f492a6994115dd4ec4d3b91 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 13 Oct 2018 10:22:05 +1300 Subject: [PATCH 011/151] Fix collisionless entities not being able to be far grabbed --- scripts/system/controllers/controllerDispatcher.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index db622a6724..417a2a49bb 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -425,7 +425,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.leftPointer = this.pointerManager.createPointer(false, PickType.Ray, { joint: "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", - filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES, + filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, triggers: [{action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"}], posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true), hover: true, @@ -435,7 +435,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }); this.rightPointer = this.pointerManager.createPointer(false, PickType.Ray, { joint: "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND", - filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES, + filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, triggers: [{action: Controller.Standard.RTClick, button: "Focus"}, {action: Controller.Standard.RTClick, button: "Primary"}], posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true), hover: true, From 3bc4581f82fa3b9e7bf94007223f151c54190bc0 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 13 Oct 2018 10:22:27 +1300 Subject: [PATCH 012/151] Fix far grabbing collisionless entity removing collisionless property --- .../controllers/controllerModules/farActionGrabEntity.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 2e73526728..d9b63ad534 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -102,7 +102,6 @@ Script.include("/~/system/libraries/Xform.js"); this.potentialEntityWithContextOverlay = false; this.entityWithContextOverlay = false; this.contextOverlayTimer = false; - this.previousCollisionStatus = false; this.locked = false; this.highlightedEntity = null; this.reticleMinX = MARGIN; @@ -490,7 +489,8 @@ Script.include("/~/system/libraries/Xform.js"); var targetProps = Entities.getEntityProperties(entityID, [ "dynamic", "shapeType", "position", "rotation", "dimensions", "density", - "userData", "locked", "type", "href" + "userData", "locked", "type", "href", + "collisionless" ]); if (targetProps.href !== "") { AddressManager.handleLookupString(targetProps.href); @@ -543,7 +543,8 @@ Script.include("/~/system/libraries/Xform.js"); var selectionTargetProps = Entities.getEntityProperties(targetEntityID, [ "dynamic", "shapeType", "position", "rotation", "dimensions", "density", - "userData", "locked", "type", "href" + "userData", "locked", "type", "href", + "collisionless" ]); var selectionTargetObject = new TargetObject(targetEntityID, selectionTargetProps); From 7d3eca57bb4a721351162fbdc51ce67a5b875c9a Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 13 Oct 2018 10:22:41 +1300 Subject: [PATCH 013/151] Fix mouse pointer not recognizing or highlighting collisionless entities --- scripts/system/controllers/controllerDispatcher.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 417a2a49bb..f5bf0f8e4c 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -467,7 +467,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }); this.mouseRayPick = Pointers.createPointer(PickType.Ray, { joint: "Mouse", - filter: Picks.PICK_ENTITIES | Picks.PICK_OVERLAYS, + filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, enabled: true }); this.handleHandMessage = function(channel, data, sender) { From e86d1691ce00bc99cdfa556a15bbda51c59bfef9 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 12 Oct 2018 17:26:46 -0700 Subject: [PATCH 014/151] Cache the local Entities file to avoid reading 2 or 3 times --- libraries/octree/src/OctreeDataUtils.cpp | 42 ++++++++++---------- libraries/octree/src/OctreeDataUtils.h | 1 + libraries/octree/src/OctreePersistThread.cpp | 39 +++++++++++++----- libraries/octree/src/OctreePersistThread.h | 1 + 4 files changed, 54 insertions(+), 29 deletions(-) diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index 4b1700d593..a2b0d78209 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -18,30 +18,32 @@ #include #include -// Reads octree file and parses it into a QJsonDocument. Handles both gzipped and non-gzipped files. -// Returns true if the file was successfully opened and parsed, otherwise false. -// Example failures: file does not exist, gzipped file cannot be unzipped, invalid JSON. -bool readOctreeFile(QString path, QJsonDocument* doc) { - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - qCritical() << "Cannot open json file for reading: " << path; - return false; - } +namespace { + // Reads octree file and parses it into a QJsonDocument. Handles both gzipped and non-gzipped files. + // Returns true if the file was successfully opened and parsed, otherwise false. + // Example failures: file does not exist, gzipped file cannot be unzipped, invalid JSON. + bool readOctreeFile(QString path, QJsonDocument* doc) { + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + qCritical() << "Cannot open json file for reading: " << path; + return false; + } - QByteArray data = file.readAll(); - QByteArray jsonData; + QByteArray data = file.readAll(); + QByteArray jsonData; - if (!gunzip(data, jsonData)) { - jsonData = data; - } + if (!gunzip(data, jsonData)) { + jsonData = data; + } - QJsonParseError parserError; - *doc = QJsonDocument::fromJson(jsonData, &parserError); - if (parserError.error != QJsonParseError::NoError) { - qWarning() << "Error reading JSON file" << path << "-" << parserError.errorString(); + QJsonParseError parserError; + *doc = QJsonDocument::fromJson(jsonData, &parserError); + if (parserError.error != QJsonParseError::NoError) { + qWarning() << "Error reading JSON file" << path << "-" << parserError.errorString(); + } + return !doc->isNull(); } - return !doc->isNull(); -} +} // Anon namespace. bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromJSON(QJsonObject root) { if (root.contains("Id") && root.contains("DataVersion") && root.contains("Version")) { diff --git a/libraries/octree/src/OctreeDataUtils.h b/libraries/octree/src/OctreeDataUtils.h index 9060e7b460..ef96415044 100644 --- a/libraries/octree/src/OctreeDataUtils.h +++ b/libraries/octree/src/OctreeDataUtils.h @@ -46,6 +46,7 @@ public: }; class RawEntityData : public RawOctreeData { +public: PacketType dataPacketType() const override; void readSubclassData(const QJsonObject& root) override; void writeSubclassData(QJsonObject& root) const override; diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 8b1d766418..0c78a1a797 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "OctreeLogging.h" #include "OctreeUtils.h" @@ -72,14 +73,27 @@ void OctreePersistThread::start() { OctreeUtils::RawOctreeData data; qCDebug(octree) << "Reading octree data from" << _filename; - if (data.readOctreeDataInfoFromFile(_filename)) { - qCDebug(octree) << "Current octree data: ID(" << data.id << ") DataVersion(" << data.version << ")"; - packet->writePrimitive(true); - auto id = data.id.toRfc4122(); - packet->write(id); - packet->writePrimitive(data.version); + QFile file(_filename); + if (file.open(QIODevice::ReadOnly)) { + QByteArray jsonData(file.readAll()); + file.close(); + if (!gunzip(jsonData, _cachedJSONData)) { + _cachedJSONData = jsonData; + } + + if (data.readOctreeDataInfoFromData(_cachedJSONData)) { + qCDebug(octree) << "Current octree data: ID(" << data.id << ") DataVersion(" << data.version << ")"; + packet->writePrimitive(true); + auto id = data.id.toRfc4122(); + packet->write(id); + packet->writePrimitive(data.version); + } else { + _cachedJSONData.clear(); + qCWarning(octree) << "No octree data found"; + packet->writePrimitive(false); + } } else { - qCWarning(octree) << "No octree data found"; + qCWarning(octree) << "Couldn't access file" << _filename << file.errorString(); packet->writePrimitive(false); } @@ -99,6 +113,7 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointerreadAll(); replaceData(replacementData); hasValidOctreeData = data.readOctreeDataInfoFromFile(_filename); @@ -108,7 +123,7 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointerwithWriteLock([&] { PerformanceWarning warn(true, "Loading Octree File", true); - persistentFileRead = _tree->readFromFile(_filename.toLocal8Bit().constData()); + if (_cachedJSONData.isEmpty()) { + persistentFileRead = _tree->readFromFile(_filename.toLocal8Bit().constData()); + } else { + QDataStream jsonStream(_cachedJSONData); + persistentFileRead = _tree->readFromStream(-1, QDataStream(_cachedJSONData)); + } _tree->pruneTree(); }); + _cachedJSONData.clear(); quint64 loadDone = usecTimestampNow(); _loadTimeUSecs = loadDone - loadStarted; diff --git a/libraries/octree/src/OctreePersistThread.h b/libraries/octree/src/OctreePersistThread.h index 0044a8fa5a..6a38a21a84 100644 --- a/libraries/octree/src/OctreePersistThread.h +++ b/libraries/octree/src/OctreePersistThread.h @@ -78,6 +78,7 @@ private: quint64 _lastTimeDebug; QString _persistAsFileType; + QByteArray _cachedJSONData; }; #endif // hifi_OctreePersistThread_h From f5f34e8e7de96dc49766c98af48abd92a1435429 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 15 Oct 2018 18:14:40 -0700 Subject: [PATCH 015/151] Pasre Entities JSON with our own top-level parser Use QJsonDocument for each individual entity. --- libraries/octree/src/Octree.cpp | 8 + .../octree/src/OctreeEntitiesFileParser.cpp | 218 ++++++++++++++++++ .../octree/src/OctreeEntitiesFileParser.h | 38 +++ 3 files changed, 264 insertions(+) create mode 100644 libraries/octree/src/OctreeEntitiesFileParser.cpp create mode 100644 libraries/octree/src/OctreeEntitiesFileParser.h diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index d583901b41..30108d973e 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -50,6 +50,7 @@ #include "OctreeLogging.h" #include "OctreeQueryNode.h" #include "OctreeUtils.h" +#include "OctreeEntitiesFileParser.h" QVector PERSIST_EXTENSIONS = {"json", "json.gz"}; @@ -827,12 +828,19 @@ bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, jsonBuffer += QByteArray(rawData, got); } + OctreeEntitiesFileParser octreeParser; + octreeParser.setEntitiesString(jsonBuffer); + QVariantMap asMap; + bool parseSuccess = octreeParser.parseEntities(asMap); + + /* QJsonDocument asDocument = QJsonDocument::fromJson(jsonBuffer); if (!marketplaceID.isEmpty()) { asDocument = addMarketplaceIDToDocumentEntities(asDocument, marketplaceID); } QVariant asVariant = asDocument.toVariant(); QVariantMap asMap = asVariant.toMap(); + */ bool success = readFromMap(asMap); delete[] rawData; return success; diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp new file mode 100644 index 0000000000..14380de835 --- /dev/null +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -0,0 +1,218 @@ +// +// OctreeEntititesFileParser.cpp +// libraries/octree/src +// +// Created by Simon Walton on Oct 15, 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 +#include +#include +#include +#include + +#include "OctreeEntitiesFileParser.h" + +using std::string; + +OctreeEntitiesFileParser::OctreeEntitiesFileParser() { +} + +std::string OctreeEntitiesFileParser::getErrorString() const { + std::ostringstream err; + if (_errorString.size() != 0) { + err << "Error: Line " << _line << ", byte position " << _position << ": " << _errorString; + }; + + return err.str(); +} + +void OctreeEntitiesFileParser::setEntitiesString(const QByteArray& entitiesContents) { + _entitiesContents = entitiesContents; + _entitiesLength = _entitiesContents.length(); + _position = 0; + _line = 1; +} + +bool OctreeEntitiesFileParser::parseEntities(QVariantMap& parsedEntities) { + if (nextToken() != '{') { + _errorString = "Text before start of object"; + return false; + } + + bool gotDataVersion = false; + bool gotEntities = false; + bool gotId = false; + bool gotVersion = false; + + while (!(gotDataVersion && gotEntities && gotId && gotVersion)) { + if (nextToken() != '"') { + _errorString = "Incorrect key string"; + return false; + } + + string key = readString(); + if (key.size() == 0) { + _errorString = "Missing object key"; + return false; + } + + if (nextToken() != ':') { + _errorString = "Ill-formed id/value entry"; + return false; + } + + if (key == "DataVersion") { + if (gotDataVersion) { + _errorString = "Duplicate DataVersion entries"; + return false; + } + + int dataVersionValue = readInteger(); + parsedEntities["DataVersion"] = dataVersionValue; + gotDataVersion = true; + } else if (key == "Entities") { + if (gotEntities) { + _errorString = "Duplicate Entities entries"; + return false; + } + + QVariantList entitiesValue; + if (!readEntitiesArray(entitiesValue)) { + return false; + } + + parsedEntities["Entities"] = entitiesValue; + gotEntities = true; + } else if (key == "Id") { + if (gotId) { + _errorString = "Duplicate Id entries"; + return false; + } + + if (nextToken() != '"') { + _errorString = "Invalid Id value"; + return false; + }; + string idString = readString(); + if (idString.size() == 0) { + _errorString = "Invalid Id string"; + return false; + } + QUuid idValue = QUuid::fromString(QLatin1String(idString.c_str()) ); + if (idValue.isNull()) { + _errorString = "Id value invalid UUID string: " + idString; + return false; + } + + parsedEntities["Id"] = idValue; + gotId = true; + } else if (key == "Version") { + if (gotVersion) { + _errorString = "Duplicate Version entries"; + return false; + } + + int versionValue = readInteger(); + parsedEntities["Version"] = versionValue; + gotVersion = true; + } else { + _errorString = "Unrecognized key name: " + key; + return false; + } + + if (gotDataVersion && gotEntities && gotId && gotVersion) { + break; + } else if (nextToken() != ',') { + _errorString = "Id/value incorrectly terminated"; + return false; + } + } + + if (nextToken() != '}' || nextToken() != -1) { + _errorString = "Ill-formed end of object"; + return false; + } + + return true; +} + +int OctreeEntitiesFileParser::nextToken() { + while (_position < _entitiesLength) { + char c = _entitiesContents[_position++]; + if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { + return c; + } + if (c == '\n') { + ++_line; + } + } + + return -1; +} + +string OctreeEntitiesFileParser::readString() { + string returnString; + while (_position < _entitiesLength) { + char c = _entitiesContents[_position++]; + if (c == '"') { + break; + } else { + returnString.push_back(c); + } + } + + return returnString; +} + +int OctreeEntitiesFileParser::readInteger() { + const char* currentPosition = _entitiesContents.constData() + _position; + int i = atoi(currentPosition); + + int token; + do { + token = nextToken(); + } while (token == '-' || token == '+' || (token >= '0' && token <= '9')); + + --_position; + return i; +} + +bool OctreeEntitiesFileParser::readEntitiesArray(QVariantList& entitiesArray) { + if (nextToken() != '[') { + _errorString = "Entities entry is not an array"; + return false; + } + + while (true) { + QJsonParseError parseError; + QByteArray entitiesJson(_entitiesContents.right(_entitiesLength - _position)); + QJsonDocument entity = QJsonDocument::fromJson(entitiesJson, &parseError); + if (parseError.error != QJsonParseError::GarbageAtEnd) { + _errorString = "Ill-formed entity array"; + return false; + } + int entityLength = parseError.offset; + entitiesJson.truncate(entityLength); + _position += entityLength; + + entity = QJsonDocument::fromJson(entitiesJson, &parseError); + if (parseError.error != QJsonParseError::NoError) { + _errorString = "Entity item parse error"; + return false; + } + entitiesArray.append(entity.object()); + char c = nextToken(); + if (c == ']') { + return true; + } else if (c != ',') { + _errorString = "Entity array item incorrectly terminated"; + return false; + } + } + return true; +} diff --git a/libraries/octree/src/OctreeEntitiesFileParser.h b/libraries/octree/src/OctreeEntitiesFileParser.h new file mode 100644 index 0000000000..562a18e833 --- /dev/null +++ b/libraries/octree/src/OctreeEntitiesFileParser.h @@ -0,0 +1,38 @@ +// +// OctreeEntititesFileParser.h +// libraries/octree/src +// +// Created by Simon Walton on Oct 15, 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_OctreeEntitiesFileParser_h +#define hifi_OctreeEntitiesFileParser_h + +#include +#include + +class OctreeEntitiesFileParser { +public: + OctreeEntitiesFileParser(); + void setEntitiesString(const QByteArray& entitiesContents); + bool parseEntities(QVariantMap& parsedEntities); + std::string getErrorString() const; + +private: + int nextToken(); + std::string readString(); + int readInteger(); + bool readEntitiesArray(QVariantList& entitiesArray); + + QByteArray _entitiesContents; + int _position { 0 }; + int _line { 1 }; + int _entitiesLength { 0 }; + std::string _errorString; +}; + +#endif // hifi_OctreeEntitiesFileParser_h From c031769c67d0c5391b7fd13cd937a8576e8d20e7 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 16 Oct 2018 18:08:07 -0700 Subject: [PATCH 016/151] Faster JSON entities parser --- libraries/octree/src/OctreeDataUtils.cpp | 20 +++++-- libraries/octree/src/OctreeDataUtils.h | 1 + .../octree/src/OctreeEntitiesFileParser.cpp | 58 +++++++++++++++---- .../octree/src/OctreeEntitiesFileParser.h | 3 + 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/libraries/octree/src/OctreeDataUtils.cpp b/libraries/octree/src/OctreeDataUtils.cpp index a2b0d78209..7bbad652f8 100644 --- a/libraries/octree/src/OctreeDataUtils.cpp +++ b/libraries/octree/src/OctreeDataUtils.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html #include "OctreeDataUtils.h" +#include "OctreeEntitiesFileParser.h" #include #include @@ -55,19 +56,30 @@ bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromJSON(QJsonObject root) { return true; } +bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromMap(QVariantMap map) { + if (map.contains("Id") && map.contains("DataVersion") && map.contains("Version")) { + id = map["Id"].toUuid(); + dataVersion = map["DataVersion"].toInt(); + version = map["Version"].toInt(); + } + return true; +} + bool OctreeUtils::RawOctreeData::readOctreeDataInfoFromData(QByteArray data) { QByteArray jsonData; if (gunzip(data, jsonData)) { data = jsonData; } - auto doc = QJsonDocument::fromJson(data); - if (doc.isNull()) { + OctreeEntitiesFileParser jsonParser; + jsonParser.setEntitiesString(data); + QVariantMap entitiesMap; + if (!jsonParser.parseEntities(entitiesMap)) { + qCritical() << "Can't parse Entities JSON: " << jsonParser.getErrorString().c_str(); return false; } - auto root = doc.object(); - return readOctreeDataInfoFromJSON(root); + return readOctreeDataInfoFromMap(entitiesMap); } // Reads octree file and parses it into a RawOctreeData object. diff --git a/libraries/octree/src/OctreeDataUtils.h b/libraries/octree/src/OctreeDataUtils.h index ef96415044..236d280bbf 100644 --- a/libraries/octree/src/OctreeDataUtils.h +++ b/libraries/octree/src/OctreeDataUtils.h @@ -43,6 +43,7 @@ public: bool readOctreeDataInfoFromData(QByteArray data); bool readOctreeDataInfoFromFile(QString path); bool readOctreeDataInfoFromJSON(QJsonObject root); + bool readOctreeDataInfoFromMap(QVariantMap map); }; class RawEntityData : public RawOctreeData { diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp index 14380de835..a0e43d0810 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.cpp +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -189,23 +189,25 @@ bool OctreeEntitiesFileParser::readEntitiesArray(QVariantList& entitiesArray) { } while (true) { - QJsonParseError parseError; - QByteArray entitiesJson(_entitiesContents.right(_entitiesLength - _position)); - QJsonDocument entity = QJsonDocument::fromJson(entitiesJson, &parseError); - if (parseError.error != QJsonParseError::GarbageAtEnd) { - _errorString = "Ill-formed entity array"; + if (nextToken() != '{') { + _errorString = "Entity array item is not an object"; + return false; + } + int matchingBrace = findMatchingBrace(); + if (matchingBrace < 0) { + _errorString = "Unterminated entity object"; return false; } - int entityLength = parseError.offset; - entitiesJson.truncate(entityLength); - _position += entityLength; - entity = QJsonDocument::fromJson(entitiesJson, &parseError); - if (parseError.error != QJsonParseError::NoError) { - _errorString = "Entity item parse error"; + QByteArray jsonEntity = _entitiesContents.mid(_position - 1, matchingBrace - _position + 1); + QJsonDocument entity = QJsonDocument::fromJson(jsonEntity); + if (entity.isNull()) { + _errorString = "Ill-formed entity"; return false; } + entitiesArray.append(entity.object()); + _position = matchingBrace; char c = nextToken(); if (c == ']') { return true; @@ -216,3 +218,37 @@ bool OctreeEntitiesFileParser::readEntitiesArray(QVariantList& entitiesArray) { } return true; } + +int OctreeEntitiesFileParser::findMatchingBrace() const { + int index = _position; + int nestCount = 1; + while (index < _entitiesLength && nestCount != 0) { + switch (_entitiesContents[index++]) { + case '{': + ++nestCount; + break; + + case '}': + --nestCount; + break; + + case '"': + // Skip string + while (index < _entitiesLength) { + if (_entitiesContents[index] == '"') { + ++index; + break; + } else if (_entitiesContents[index] == '\\' && _entitiesContents[++index] == 'u') { + index += 4; + } + ++index; + } + break; + + default: + break; + } + } + + return nestCount == 0 ? index : -1; +} diff --git a/libraries/octree/src/OctreeEntitiesFileParser.h b/libraries/octree/src/OctreeEntitiesFileParser.h index 562a18e833..2daeb7e561 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.h +++ b/libraries/octree/src/OctreeEntitiesFileParser.h @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// Parse the top-level of the Models object ourselves - use QJsonDocument for each Entity object. + #ifndef hifi_OctreeEntitiesFileParser_h #define hifi_OctreeEntitiesFileParser_h @@ -27,6 +29,7 @@ private: std::string readString(); int readInteger(); bool readEntitiesArray(QVariantList& entitiesArray); + int findMatchingBrace() const; QByteArray _entitiesContents; int _position { 0 }; From be279f08dc3d8819002125bb5e36eae9c121e0e8 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 17 Oct 2018 15:43:38 -0700 Subject: [PATCH 017/151] Restore the addMarketplaceIDToDocumentEntities() hack Fixes for Linux compiles; other improvements --- libraries/entities/src/EntityTree.cpp | 2 +- libraries/octree/src/Octree.cpp | 35 +++++++++---------- .../octree/src/OctreeEntitiesFileParser.cpp | 6 ++-- .../octree/src/OctreeEntitiesFileParser.h | 2 +- libraries/octree/src/OctreePersistThread.cpp | 2 +- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 19182d8b22..4cac2205a4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -35,7 +35,7 @@ #include "QVariantGLM.h" #include "EntitiesLogging.h" #include "RecurseOctreeToMapOperator.h" -#include "RecurseOctreeToJsonOperator.h" +#include "RecurseOctreeToJSONOperator.h" #include "LogHandler.h" #include "EntityEditFilters.h" #include "EntityDynamicFactoryInterface.h" diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 30108d973e..bbf1e44d08 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -785,28 +785,26 @@ bool Octree::readFromStream(uint64_t streamLength, QDataStream& inputStream, con } +namespace { // hack to get the marketplace id into the entities. We will create a way to get this from a hash of // the entity later, but this helps us move things along for now -QJsonDocument addMarketplaceIDToDocumentEntities(QJsonDocument& doc, const QString& marketplaceID) { +QVariantMap addMarketplaceIDToDocumentEntities(QVariantMap& doc, const QString& marketplaceID) { if (!marketplaceID.isEmpty()) { - QJsonDocument newDoc; - QJsonObject rootObj = doc.object(); - QJsonArray newEntitiesArray; + QVariantList newEntitiesArray; // build a new entities array - auto entitiesArray = rootObj["Entities"].toArray(); - for(auto it = entitiesArray.begin(); it != entitiesArray.end(); it++) { - auto entity = (*it).toObject(); + auto entitiesArray = doc["Entities"].toList(); + for (auto it = entitiesArray.begin(); it != entitiesArray.end(); it++) { + auto entity = (*it).toMap(); entity["marketplaceID"] = marketplaceID; newEntitiesArray.append(entity); } - rootObj["Entities"] = newEntitiesArray; - newDoc.setObject(rootObj); - return newDoc; + doc["Entities"] = newEntitiesArray; } return doc; } +} // Unnamed namepsace const int READ_JSON_BUFFER_SIZE = 2048; bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, const QString& marketplaceID /*=""*/) { @@ -831,16 +829,15 @@ bool Octree::readJSONFromStream(uint64_t streamLength, QDataStream& inputStream, OctreeEntitiesFileParser octreeParser; octreeParser.setEntitiesString(jsonBuffer); QVariantMap asMap; - bool parseSuccess = octreeParser.parseEntities(asMap); - - /* - QJsonDocument asDocument = QJsonDocument::fromJson(jsonBuffer); - if (!marketplaceID.isEmpty()) { - asDocument = addMarketplaceIDToDocumentEntities(asDocument, marketplaceID); + if (!octreeParser.parseEntities(asMap)) { + qCritical() << "Couldn't parse Entities JSON:" << octreeParser.getErrorString().c_str(); + return false; } - QVariant asVariant = asDocument.toVariant(); - QVariantMap asMap = asVariant.toMap(); - */ + + if (!marketplaceID.isEmpty()) { + addMarketplaceIDToDocumentEntities(asMap, marketplaceID); + } + bool success = readFromMap(asMap); delete[] rawData; return success; diff --git a/libraries/octree/src/OctreeEntitiesFileParser.cpp b/libraries/octree/src/OctreeEntitiesFileParser.cpp index a0e43d0810..0b75991fd2 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.cpp +++ b/libraries/octree/src/OctreeEntitiesFileParser.cpp @@ -10,10 +10,10 @@ // #include +#include #include #include #include -#include #include "OctreeEntitiesFileParser.h" @@ -171,12 +171,12 @@ string OctreeEntitiesFileParser::readString() { int OctreeEntitiesFileParser::readInteger() { const char* currentPosition = _entitiesContents.constData() + _position; - int i = atoi(currentPosition); + int i = std::atoi(currentPosition); int token; do { token = nextToken(); - } while (token == '-' || token == '+' || (token >= '0' && token <= '9')); + } while (token == '-' || token == '+' || std::isdigit(token)); --_position; return i; diff --git a/libraries/octree/src/OctreeEntitiesFileParser.h b/libraries/octree/src/OctreeEntitiesFileParser.h index 2daeb7e561..e4e82f0e66 100644 --- a/libraries/octree/src/OctreeEntitiesFileParser.h +++ b/libraries/octree/src/OctreeEntitiesFileParser.h @@ -14,8 +14,8 @@ #ifndef hifi_OctreeEntitiesFileParser_h #define hifi_OctreeEntitiesFileParser_h -#include #include +#include class OctreeEntitiesFileParser { public: diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 0c78a1a797..2717be593b 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -158,7 +158,7 @@ void OctreePersistThread::handleOctreeDataFileReply(QSharedPointerreadFromFile(_filename.toLocal8Bit().constData()); } else { QDataStream jsonStream(_cachedJSONData); - persistentFileRead = _tree->readFromStream(-1, QDataStream(_cachedJSONData)); + persistentFileRead = _tree->readFromStream(-1, jsonStream); } _tree->pruneTree(); }); From 83ec346cc55c59444b5ddc4ea33af052121299d9 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 17 Oct 2018 16:42:29 -0700 Subject: [PATCH 018/151] changed the hand azimuth computation to be in spine2 space. this stops you from having a reversed direction when you sit down with hmd lean off and the hands are behind the origin in the z direction. --- interface/src/avatar/MyAvatar.cpp | 91 +++++++++++++++++++++++-------- interface/src/avatar/MyAvatar.h | 2 +- 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b347963cf1..e7d09b79a2 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -475,16 +475,33 @@ void MyAvatar::update(float deltaTime) { // put the average hand azimuth into sensor space. // then mix it with head facing direction to determine rotation recenter if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { - glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); - glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); - glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); - glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, handHipAzimuthWorldSpace); - glm::vec2 normedHandHipAzimuthSensorSpace(0.0f, 1.0f); - if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) { - normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); + int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2"); + if (!(spine2Index < 0)) { + // use the spine for the azimuth origin. + glm::quat spine2Rot = getAbsoluteJointRotationInObjectFrame(spine2Index); + glm::vec3 handHipAzimuthAvatarSpace = spine2Rot * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); + glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), handHipAzimuthAvatarSpace); + glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); + glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); + glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, handHipAzimuthWorldSpace); + glm::vec2 normedHandHipAzimuthSensorSpace(0.0f, 1.0f); + if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) { + normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); + } + glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH); + _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); + } else { + glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); + glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); + glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); + glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, handHipAzimuthWorldSpace); + glm::vec2 normedHandHipAzimuthSensorSpace(0.0f, 1.0f); + if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) { + normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); + } + glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH); + _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); } - glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH); - _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); } else { _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, _headControllerFacing, tau); } @@ -854,6 +871,20 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { glm::vec2 MyAvatar::computeHandAzimuth() const { controller::Pose leftHandPoseAvatarSpace = getLeftHandPose(); controller::Pose rightHandPoseAvatarSpace = getRightHandPose(); + + int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2"); + glm::vec3 spine2Position(0.0f,0.0f,0.0f); + glm::quat spine2Rotation(0.0f, 0.0f, 0.0f, 1.0f); + if (!(spine2Index < 0)) { + // use the spine for the azimuth origin. + spine2Position = getAbsoluteJointTranslationInObjectFrame(spine2Index); + glm::quat spine2Rotation = getAbsoluteJointRotationInObjectFrame(spine2Index); + } + glm::vec3 rightHandOffset = rightHandPoseAvatarSpace.translation - spine2Position; + glm::vec3 leftHandOffset = leftHandPoseAvatarSpace.translation - spine2Position; + glm::vec3 rightHandSpine2Space = glm::inverse(spine2Rotation) * rightHandOffset; + glm::vec3 leftHandSpine2Space = glm::inverse(spine2Rotation) * leftHandOffset; + controller::Pose headPoseAvatarSpace = getControllerPoseInAvatarFrame(controller::Action::HEAD); const float HALFWAY = 0.50f; glm::vec2 latestHipToHandController = _hipToHandController; @@ -862,23 +893,25 @@ glm::vec2 MyAvatar::computeHandAzimuth() const { // we need the old azimuth reading to prevent flipping the facing direction 180 // in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart. glm::vec2 oldAzimuthReading = _hipToHandController; - if ((glm::length(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)) > 0.0f) && (glm::length(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)) > 0.0f)) { - latestHipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); + if ((glm::length(glm::vec2(rightHandSpine2Space.x, rightHandSpine2Space.z)) > 0.0f) && (glm::length(glm::vec2(leftHandSpine2Space.x, leftHandSpine2Space.z)) > 0.0f)) { + latestHipToHandController = lerp(glm::normalize(glm::vec2(rightHandSpine2Space.x, rightHandSpine2Space.z)), glm::normalize(glm::vec2(leftHandSpine2Space.x, leftHandSpine2Space.z)), HALFWAY); } else { - latestHipToHandController = glm::vec2(0.0f, -1.0f); + latestHipToHandController = glm::vec2(0.0f, 1.0f); } glm::vec3 headLookAtAvatarSpace = transformVectorFast(headPoseAvatarSpace.getMatrix(), glm::vec3(0.0f, 0.0f, 1.0f)); - glm::vec2 headAzimuthAvatarSpace = glm::vec2(headLookAtAvatarSpace.x, headLookAtAvatarSpace.z); - if (glm::length(headAzimuthAvatarSpace) > 0.0f) { - headAzimuthAvatarSpace = glm::normalize(headAzimuthAvatarSpace); + glm::vec3 headLookAtSpine2Space = glm::inverse(spine2Rotation) * headLookAtAvatarSpace; + + glm::vec2 headAzimuthSpine2Space = glm::vec2(headLookAtSpine2Space.x, headLookAtSpine2Space.z); + if (glm::length(headAzimuthSpine2Space) > 0.0f) { + headAzimuthSpine2Space = glm::normalize(headAzimuthSpine2Space); } else { - headAzimuthAvatarSpace = -latestHipToHandController; + headAzimuthSpine2Space = -latestHipToHandController; } // check the angular distance from forward and back float cosForwardAngle = glm::dot(latestHipToHandController, oldAzimuthReading); - float cosHeadShoulder = glm::dot(-latestHipToHandController, headAzimuthAvatarSpace); + float cosHeadShoulder = glm::dot(-latestHipToHandController, headAzimuthSpine2Space); // if we are now closer to the 180 flip of the previous chest forward // then we negate our computed latestHipToHandController to keep the chest from flipping. // also check the head to shoulder azimuth difference if we negate. @@ -3392,19 +3425,33 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { } glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { + int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2"); + glm::quat spine2Rot = Quaternions::IDENTITY; + if (!(spine2Index < 0)) { + // use the spine for the azimuth origin. + spine2Rot = getAbsoluteJointRotationInObjectFrame(spine2Index); + } + glm::vec3 spine2UpAvatarSpace = spine2Rot * glm::vec3(0.0f, 1.0f, 0.0f); + glm::vec3 spine2FwdAvatarSpace = spine2Rot * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); // static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; // RIG_CHANGE_OF_BASIS * AVATAR_TO_RIG_ROTATION * inverse(RIG_CHANGE_OF_BASIS) = Quaternions::Y_180; //avatar Space; const glm::quat AVATAR_TO_RIG_ROTATION = Quaternions::Y_180; - glm::vec3 hipToHandRigSpace = AVATAR_TO_RIG_ROTATION * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); + glm::vec3 spine2UpRigSpace = AVATAR_TO_RIG_ROTATION * spine2UpAvatarSpace; + glm::vec3 spine2FwdRigSpace = AVATAR_TO_RIG_ROTATION * spine2FwdAvatarSpace; glm::vec3 u, v, w; - if (glm::length(hipToHandRigSpace) > 0.0f) { - hipToHandRigSpace = glm::normalize(hipToHandRigSpace); + if (glm::length(spine2FwdRigSpace) > 0.0f) { + spine2FwdRigSpace = glm::normalize(spine2FwdRigSpace); } else { - hipToHandRigSpace = glm::vec3(0.0f, 0.0f, 1.0f); + spine2FwdRigSpace = glm::vec3(0.0f, 0.0f, 1.0f); } - generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); + if (glm::length(spine2UpRigSpace) > 0.0f) { + spine2UpRigSpace = glm::normalize(spine2UpRigSpace); + } else { + spine2UpRigSpace = glm::vec3(0.0f, 1.0f, 0.0f); + } + generateBasisVectors(spine2UpRigSpace, spine2FwdRigSpace, u, v, w); glm::mat4 spine2RigSpace(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)); return spine2RigSpace; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 16b765711a..85d6bea018 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1688,7 +1688,7 @@ private: glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; - glm::vec2 _hipToHandController { 0.0f, -1.0f }; // spine2 facing vector in xz plane (avatar space) + glm::vec2 _hipToHandController { 0.0f, 1.0f }; // spine2 facing vector in xz plane (spine2 space) float _currentStandingHeight { 0.0f }; bool _resetMode { true }; From 8f4fafe4d0480a88d0e19ad4044df32ad69b7433 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 18 Oct 2018 08:44:01 -0700 Subject: [PATCH 019/151] put some smoothing on the spine2 rotation in myskeletonmodel.cpp --- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/MySkeletonModel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e7d09b79a2..a5bb08658d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -878,7 +878,7 @@ glm::vec2 MyAvatar::computeHandAzimuth() const { if (!(spine2Index < 0)) { // use the spine for the azimuth origin. spine2Position = getAbsoluteJointTranslationInObjectFrame(spine2Index); - glm::quat spine2Rotation = getAbsoluteJointRotationInObjectFrame(spine2Index); + spine2Rotation = getAbsoluteJointRotationInObjectFrame(spine2Index); } glm::vec3 rightHandOffset = rightHandPoseAvatarSpace.translation - spine2Position; glm::vec3 leftHandOffset = leftHandPoseAvatarSpace.translation - spine2Position; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 3084542472..900d532c88 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -261,7 +261,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { } generateBasisVectors(up, fwd, u, v, w); AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); - currentSpine2Pose.rot() = newSpinePose.rot(); + currentSpine2Pose.rot() = safeLerp(currentSpine2Pose.rot(), newSpinePose.rot(), 0.5f); params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentSpine2Pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } From 12b03b0e07e3d69d727e44b401cfea953ddcde3f Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 18 Oct 2018 10:52:13 -0700 Subject: [PATCH 020/151] tinkering with lerping the spine2 position in avatar space --- interface/src/avatar/MyAvatar.cpp | 3 ++- interface/src/avatar/MyAvatar.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a5bb08658d..e8d87d30af 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -877,7 +877,8 @@ glm::vec2 MyAvatar::computeHandAzimuth() const { glm::quat spine2Rotation(0.0f, 0.0f, 0.0f, 1.0f); if (!(spine2Index < 0)) { // use the spine for the azimuth origin. - spine2Position = getAbsoluteJointTranslationInObjectFrame(spine2Index); + spine2Position = getAbsoluteJointTranslationInObjectFrame(spine2Index); + _spine2PositionAvatarSpace = _spine2PositionAvatarSpace + ((0.05f) * (spine2Position - _spine2PositionAvatarSpace)); spine2Rotation = getAbsoluteJointRotationInObjectFrame(spine2Index); } glm::vec3 rightHandOffset = rightHandPoseAvatarSpace.translation - spine2Position; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 85d6bea018..f40f7e3023 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1689,6 +1689,7 @@ private: glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; glm::vec2 _hipToHandController { 0.0f, 1.0f }; // spine2 facing vector in xz plane (spine2 space) + glm::vec3 _spine2PositionAvatarSpace { 0.0f, 0.0f, 0.0f }; float _currentStandingHeight { 0.0f }; bool _resetMode { true }; From 5d8a253785c9ea2c6b3b57373824c7b0227ce861 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 19 Oct 2018 02:53:15 +0300 Subject: [PATCH 021/151] FB19400 during shutdown -- TypeError: Cannot read property 'buttons' of null note: change also resolve a lot of other shutdown errors from QML (related to null parent) --- interface/resources/qml/hifi/Desktop.qml | 4 ++-- interface/resources/qml/hifi/tablet/TabletHome.qml | 4 ++-- libraries/qml/src/qml/OffscreenSurface.cpp | 12 ++++++++++-- libraries/qml/src/qml/impl/SharedObject.cpp | 4 +++- libraries/qml/src/qml/impl/SharedObject.h | 2 ++ 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 4d342fe775..1d20587643 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -70,8 +70,8 @@ OriginalDesktop.Desktop { anchors.horizontalCenter: settings.constrainToolbarToCenterX ? desktop.horizontalCenter : undefined; // Literal 50 is overwritten by settings from previous session, and sysToolbar.x comes from settings when not constrained. x: sysToolbar.x - buttonModel: tablet.buttons; - shown: tablet.toolbarMode; + buttonModel: tablet ? tablet.buttons : null; + shown: tablet ? tablet.toolbarMode : false; } Settings { diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 1922b02f93..616cdf7fee 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -115,9 +115,9 @@ Item { property int previousIndex: -1 Repeater { id: pageRepeater - model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) + model: tabletProxy != null ? Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) : null onItemAdded: { - item.proxyModel.sourceModel = tabletProxy.buttons; + item.proxyModel.sourceModel = tabletProxy != null ? tabletProxy.buttons : null; item.proxyModel.pageIndex = index; } diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp index cbcafe9c7d..37c3ae7a37 100644 --- a/libraries/qml/src/qml/OffscreenSurface.cpp +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -387,8 +387,16 @@ void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent, if (!parent) { parent = getRootItem(); } - // Allow child windows to be destroyed from JS - QQmlEngine::setObjectOwnership(newObject, QQmlEngine::JavaScriptOwnership); + // manually control children items lifetime + QQmlEngine::setObjectOwnership(newObject, QQmlEngine::CppOwnership); + // some objects we are going to delete might be already deleted (children of parent we already deleted) so use QPointer to track lifetime + QPointer trackedObject(newObject); + connect(_sharedObject, &SharedObject::onBeforeDestroyed, [trackedObject]() { + if (trackedObject) { + delete trackedObject.data(); + } + }); + newObject->setParent(parent); newItem->setParentItem(parent); } else { diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp index 259defdb48..7612a54173 100644 --- a/libraries/qml/src/qml/impl/SharedObject.cpp +++ b/libraries/qml/src/qml/impl/SharedObject.cpp @@ -79,9 +79,11 @@ SharedObject::SharedObject() { SharedObject::~SharedObject() { + + emit onBeforeDestroyed(); + // After destroy returns, the rendering thread should be gone destroy(); - // _renderTimer is created with `this` as the parent, so need no explicit destruction #ifndef DISABLE_QML // Destroy the event hand diff --git a/libraries/qml/src/qml/impl/SharedObject.h b/libraries/qml/src/qml/impl/SharedObject.h index 002679c44d..57f4756e79 100644 --- a/libraries/qml/src/qml/impl/SharedObject.h +++ b/libraries/qml/src/qml/impl/SharedObject.h @@ -67,6 +67,8 @@ public: bool isPaused() const; bool fetchTexture(TextureAndFence& textureAndFence); +signals: + void onBeforeDestroyed(); private: bool event(QEvent* e) override; From 5fef331995512044a7499b51820c9d2feed02e30 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 19 Oct 2018 19:35:12 +0300 Subject: [PATCH 022/151] address CR comment --- interface/resources/qml/hifi/tablet/TabletHome.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 616cdf7fee..4a1648baaf 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -115,7 +115,7 @@ Item { property int previousIndex: -1 Repeater { id: pageRepeater - model: tabletProxy != null ? Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) : null + model: tabletProxy != null ? Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) : 0 onItemAdded: { item.proxyModel.sourceModel = tabletProxy != null ? tabletProxy.buttons : null; item.proxyModel.pageIndex = index; From 657ff7f6f882d8392fadbbe1cfed66034e34f758 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Mon, 22 Oct 2018 23:29:39 +0300 Subject: [PATCH 023/151] eliminate signal, introduce deletion list --- libraries/qml/src/qml/OffscreenSurface.cpp | 10 +++------- libraries/qml/src/qml/impl/SharedObject.cpp | 14 +++++++++++--- libraries/qml/src/qml/impl/SharedObject.h | 6 +++--- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp index 37c3ae7a37..1f1d50ed1f 100644 --- a/libraries/qml/src/qml/OffscreenSurface.cpp +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -389,13 +389,9 @@ void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent, } // manually control children items lifetime QQmlEngine::setObjectOwnership(newObject, QQmlEngine::CppOwnership); - // some objects we are going to delete might be already deleted (children of parent we already deleted) so use QPointer to track lifetime - QPointer trackedObject(newObject); - connect(_sharedObject, &SharedObject::onBeforeDestroyed, [trackedObject]() { - if (trackedObject) { - delete trackedObject.data(); - } - }); + + // add object to the manual deletion list + _sharedObject->addToDeletionList(newObject); newObject->setParent(parent); newItem->setParentItem(parent); diff --git a/libraries/qml/src/qml/impl/SharedObject.cpp b/libraries/qml/src/qml/impl/SharedObject.cpp index 7612a54173..1f41fefb02 100644 --- a/libraries/qml/src/qml/impl/SharedObject.cpp +++ b/libraries/qml/src/qml/impl/SharedObject.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -79,9 +80,6 @@ SharedObject::SharedObject() { SharedObject::~SharedObject() { - - emit onBeforeDestroyed(); - // After destroy returns, the rendering thread should be gone destroy(); // _renderTimer is created with `this` as the parent, so need no explicit destruction @@ -98,6 +96,11 @@ SharedObject::~SharedObject() { } #endif + // already deleted objects will be reset to null by QPointer so it should be safe just iterate here + for (auto qmlObject : _deletionList) { + delete qmlObject; + } + if (_rootItem) { delete _rootItem; _rootItem = nullptr; @@ -414,6 +417,11 @@ bool SharedObject::fetchTexture(TextureAndFence& textureAndFence) { return true; } +void hifi::qml::impl::SharedObject::addToDeletionList(QObject * object) +{ + _deletionList.append(QPointer(object)); +} + void SharedObject::setProxyWindow(QWindow* window) { #ifndef DISABLE_QML _proxyWindow = window; diff --git a/libraries/qml/src/qml/impl/SharedObject.h b/libraries/qml/src/qml/impl/SharedObject.h index 57f4756e79..ce9fcd46d2 100644 --- a/libraries/qml/src/qml/impl/SharedObject.h +++ b/libraries/qml/src/qml/impl/SharedObject.h @@ -66,9 +66,7 @@ public: void resume(); bool isPaused() const; bool fetchTexture(TextureAndFence& textureAndFence); - -signals: - void onBeforeDestroyed(); + void addToDeletionList(QObject* object); private: bool event(QEvent* e) override; @@ -93,6 +91,8 @@ private: void onAboutToQuit(); void updateTextureAndFence(const TextureAndFence& newTextureAndFence); + QList> _deletionList; + // Texture management TextureAndFence _latestTextureAndFence{ 0, 0 }; QQuickItem* _item{ nullptr }; From 09ea32dcc0e11ed7b7ec2fa4c5cfe1bd3c08cf14 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 22 Oct 2018 14:10:50 -0700 Subject: [PATCH 024/151] fix fading and highlighting issues --- interface/src/avatar/AvatarManager.cpp | 14 +-- interface/src/avatar/AvatarManager.h | 2 +- interface/src/avatar/MyAvatar.cpp | 2 + .../src/avatars-renderer/Avatar.cpp | 9 +- .../src/avatars-renderer/Avatar.h | 2 +- libraries/render-utils/src/Fade.slh | 9 +- libraries/render-utils/src/GeometryCache.cpp | 2 +- .../render-utils/src/HighlightEffect.cpp | 81 +++++++++++------- .../render-utils/src/RenderPipelines.cpp | 30 +++---- .../render-utils/src/RenderShadowTask.cpp | 85 ++++++++++--------- 10 files changed, 127 insertions(+), 109 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c9b3449cec..4abd561dc5 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -188,7 +188,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { { // lock the hash for read to check the size QReadLocker lock(&_hashLock); - if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) { + if (_avatarHash.size() < 2 && _avatarsToFadeOut.isEmpty()) { return; } } @@ -346,23 +346,23 @@ void AvatarManager::sendIdentityRequest(const QUuid& avatarID) const { } void AvatarManager::simulateAvatarFades(float deltaTime) { - if (_avatarsToFade.empty()) { + if (_avatarsToFadeOut.empty()) { return; } QReadLocker locker(&_hashLock); - QVector::iterator avatarItr = _avatarsToFade.begin(); + QVector::iterator avatarItr = _avatarsToFadeOut.begin(); const render::ScenePointer& scene = qApp->getMain3DScene(); render::Transaction transaction; - while (avatarItr != _avatarsToFade.end()) { + while (avatarItr != _avatarsToFadeOut.end()) { auto avatar = std::static_pointer_cast(*avatarItr); - avatar->updateFadingStatus(scene); + avatar->updateFadingStatus(); if (!avatar->isFading()) { // fading to zero is such a rare event we push a unique transaction for each if (avatar->isInScene()) { avatar->removeFromScene(*avatarItr, scene, transaction); } - avatarItr = _avatarsToFade.erase(avatarItr); + avatarItr = _avatarsToFadeOut.erase(avatarItr); } else { ++avatarItr; } @@ -457,7 +457,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar DependencyManager::get()->avatarDisconnected(avatar->getSessionUUID()); avatar->fadeOut(qApp->getMain3DScene(), removalReason); } - _avatarsToFade.push_back(removedAvatar); + _avatarsToFadeOut.push_back(removedAvatar); } void AvatarManager::clearOtherAvatars() { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 34aa4f2cc0..9d5b02d748 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -222,7 +222,7 @@ private: void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; - QVector _avatarsToFade; + QVector _avatarsToFadeOut; using SetOfOtherAvatars = std::set; SetOfOtherAvatars _avatarsToChangeInPhysics; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a7c8c3e179..38df7b9b06 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -857,6 +857,8 @@ void MyAvatar::simulate(float deltaTime) { } updateAvatarEntities(); + + updateFadingStatus(); } // As far as I know no HMD system supports a play area of a kilometer in radius. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 6770cd7f96..c6e6a5e8ca 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -612,6 +612,8 @@ void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "entities"); updateAvatarEntities(); } + + updateFadingStatus(); } float Avatar::getSimulationRate(const QString& rateName) const { @@ -775,14 +777,15 @@ void Avatar::fade(render::Transaction& transaction, render::Transition::Type typ _isFading = true; } -void Avatar::updateFadingStatus(render::ScenePointer scene) { +void Avatar::updateFadingStatus() { render::Transaction transaction; transaction.queryTransitionOnItem(_renderItemID, [this](render::ItemID id, const render::Transition* transition) { - if (transition == nullptr || transition->isFinished) { + if (!transition || transition->isFinished) { + AbstractViewStateInterface::instance()->getMain3DScene()->resetItemTransition(id); _isFading = false; } }); - scene->enqueueTransaction(transaction); + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); } void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 3f4cf7b8c2..014be23fa5 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -402,7 +402,7 @@ public: void fadeIn(render::ScenePointer scene); void fadeOut(render::ScenePointer scene, KillAvatarReason reason); bool isFading() const { return _isFading; } - void updateFadingStatus(render::ScenePointer scene); + void updateFadingStatus(); // JSDoc is in AvatarData.h. Q_INVOKABLE virtual float getEyeHeight() const override; diff --git a/libraries/render-utils/src/Fade.slh b/libraries/render-utils/src/Fade.slh index 47347ba135..940aabe2bd 100644 --- a/libraries/render-utils/src/Fade.slh +++ b/libraries/render-utils/src/Fade.slh @@ -85,7 +85,11 @@ float evalFadeGradient(FadeObjectParams params, vec3 position) { } float evalFadeAlpha(FadeObjectParams params, vec3 position) { - return evalFadeGradient(params, position)-params.threshold; + float alpha = evalFadeGradient(params, position)-params.threshold; + if (fadeParameters[params.category]._isInverted != 0) { + alpha = -alpha; + } + return alpha; } void applyFadeClip(FadeObjectParams params, vec3 position) { @@ -96,9 +100,6 @@ void applyFadeClip(FadeObjectParams params, vec3 position) { void applyFade(FadeObjectParams params, vec3 position, out vec3 emissive) { float alpha = evalFadeAlpha(params, position); - if (fadeParameters[params.category]._isInverted!=0) { - alpha = -alpha; - } if (alpha < 0.0) { discard; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 1215c9abea..38c3106af6 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -826,7 +826,7 @@ render::ShapePipelinePointer GeometryCache::getFadingShapePipeline(bool textured bool unlit, bool depthBias) { auto fadeEffect = DependencyManager::get(); auto fadeBatchSetter = fadeEffect->getBatchSetter(); - auto fadeItemSetter = fadeEffect->getItemStoredSetter(); + auto fadeItemSetter = fadeEffect->getItemUniformSetter(); return std::make_shared(getSimplePipeline(textured, transparent, culled, unlit, depthBias, true), nullptr, [fadeBatchSetter, fadeItemSetter](const render::ShapePipeline& shapePipeline, gpu::Batch& batch, render::Args* args) { batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get()->getWhiteTexture()); diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index 2d4aee7880..176480e35c 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -22,6 +22,8 @@ #include "GeometryCache.h" #include "CubeProjectedPolygon.h" +#include "FadeEffect.h" + #include "render-utils/ShaderConstants.h" using namespace render; @@ -37,7 +39,7 @@ namespace gr { #define OUTLINE_STENCIL_MASK 1 -extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state); +extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); HighlightResources::HighlightResources() { } @@ -156,7 +158,6 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c auto& highlight = highlightStage->getHighlight(highlightId); RenderArgs* args = renderContext->args; - ShapeKey::Builder defaultKeyBuilder; // Render full screen outputs = args->_viewport; @@ -177,10 +178,6 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c gpu::doInBatch("DrawHighlightMask::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; - auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); - auto maskDeformedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withDeformed()); - auto maskDeformedDQPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withDeformed().withDualQuatSkinned()); - // Setup camera, projection and viewport for all items glm::mat4 projMat; Transform viewMat; @@ -191,38 +188,54 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c batch.setProjectionJitter(jitter.x, jitter.y); batch.setViewTransform(viewMat); - std::vector deformedShapeKeys; - std::vector deformedDQShapeKeys; + const std::vector keys = { + ShapeKey::Builder(), ShapeKey::Builder().withFade(), + ShapeKey::Builder().withDeformed(), ShapeKey::Builder().withDeformed().withFade(), + ShapeKey::Builder().withDeformed().withDualQuatSkinned(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withFade(), + ShapeKey::Builder().withOwnPipeline(), ShapeKey::Builder().withOwnPipeline().withFade() + }; + std::vector> sortedShapeKeys(keys.size()); - // Iterate through all inShapes and render the unskinned - args->_shapePipeline = maskPipeline; - batch.setPipeline(maskPipeline->pipeline); + const int OWN_PIPELINE_INDEX = 6; for (const auto& items : inShapes) { itemBounds.insert(itemBounds.end(), items.second.begin(), items.second.end()); - if (items.first.isDeformed() && items.first.isDualQuatSkinned()) { - deformedDQShapeKeys.push_back(items.first); - } else if (items.first.isDeformed()) { - deformedShapeKeys.push_back(items.first); - } else { - renderItems(renderContext, items.second); + + int index = items.first.hasOwnPipeline() ? OWN_PIPELINE_INDEX : 0; + if (items.first.isDeformed()) { + index += 2; + if (items.first.isDualQuatSkinned()) { + index += 2; + } + } + + if (items.first.isFaded()) { + index += 1; + } + + sortedShapeKeys[index].push_back(items.first); + } + + // Render non-withOwnPipeline things + for (size_t i = 0; i < OWN_PIPELINE_INDEX; i++) { + auto& shapeKeys = sortedShapeKeys[i]; + if (shapeKeys.size() > 0) { + const auto& shapePipeline = _shapePlumber->pickPipeline(args, keys[i]); + args->_shapePipeline = shapePipeline; + for (const auto& key : shapeKeys) { + renderShapes(renderContext, _shapePlumber, inShapes.at(key)); + } } } - // Reiterate to render the skinned - if (deformedShapeKeys.size() > 0) { - args->_shapePipeline = maskDeformedPipeline; - batch.setPipeline(maskDeformedPipeline->pipeline); - for (const auto& key : deformedShapeKeys) { - renderItems(renderContext, inShapes.at(key)); - } - } - - // Reiterate to render the DQ skinned - if (deformedDQShapeKeys.size() > 0) { - args->_shapePipeline = maskDeformedDQPipeline; - batch.setPipeline(maskDeformedDQPipeline->pipeline); - for (const auto& key : deformedDQShapeKeys) { - renderItems(renderContext, inShapes.at(key)); + // Render withOwnPipeline things + for (size_t i = OWN_PIPELINE_INDEX; i < keys.size(); i++) { + auto& shapeKeys = sortedShapeKeys[i]; + if (shapeKeys.size() > 0) { + args->_shapePipeline = nullptr; + for (const auto& key : shapeKeys) { + args->_itemShapeKey = key._flags.to_ulong(); + renderShapes(renderContext, _shapePlumber, inShapes.at(key)); + } } } @@ -502,7 +515,9 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setColorWriteMask(false, false, false, false); - initZPassPipelines(*shapePlumber, state); + + auto fadeEffect = DependencyManager::get(); + initZPassPipelines(*shapePlumber, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); } auto sharedParameters = std::make_shared(); diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 4626264756..98596a3d88 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -40,7 +40,7 @@ namespace gr { void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void initForwardPipelines(ShapePlumber& plumber); -void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state); +void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void addPlumberPipeline(ShapePlumber& plumber, const ShapeKey& key, int programId, @@ -364,37 +364,29 @@ void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderAr } } -void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state) { +void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& extraBatchSetter, const render::ShapePipeline::ItemSetter& itemSetter) { using namespace shader::render_utils::program; - gpu::ShaderPointer modelProgram = gpu::Shader::createProgram(model_shadow); + shapePlumber.addPipeline( ShapeKey::Filter::Builder().withoutDeformed().withoutFade(), - modelProgram, state); - - gpu::ShaderPointer skinProgram = gpu::Shader::createProgram(deformed_model_shadow); - shapePlumber.addPipeline( - ShapeKey::Filter::Builder().withDeformed().withoutDualQuatSkinned().withoutFade(), - skinProgram, state); - - gpu::ShaderPointer modelFadeProgram = gpu::Shader::createProgram(model_shadow_fade); + gpu::Shader::createProgram(model_shadow), state); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withoutDeformed().withFade(), - modelFadeProgram, state); + gpu::Shader::createProgram(model_shadow_fade), state, extraBatchSetter, itemSetter); - gpu::ShaderPointer skinFadeProgram = gpu::Shader::createProgram(deformed_model_shadow_fade); + shapePlumber.addPipeline( + ShapeKey::Filter::Builder().withDeformed().withoutDualQuatSkinned().withoutFade(), + gpu::Shader::createProgram(deformed_model_shadow), state); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withDeformed().withoutDualQuatSkinned().withFade(), - skinFadeProgram, state); + gpu::Shader::createProgram(deformed_model_shadow_fade), state, extraBatchSetter, itemSetter); - gpu::ShaderPointer skinModelShadowDualQuatProgram = gpu::Shader::createProgram(deformed_model_shadow_dq); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withDeformed().withDualQuatSkinned().withoutFade(), - skinModelShadowDualQuatProgram, state); - - gpu::ShaderPointer skinModelShadowFadeDualQuatProgram = gpu::Shader::createProgram(deformed_model_shadow_fade_dq); + gpu::Shader::createProgram(deformed_model_shadow_dq), state); shapePlumber.addPipeline( ShapeKey::Filter::Builder().withDeformed().withDualQuatSkinned().withFade(), - skinModelShadowFadeDualQuatProgram, state); + gpu::Shader::createProgram(deformed_model_shadow_fade_dq), state, extraBatchSetter, itemSetter); } // FIXME find a better way to setup the default textures diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index c4fa297965..0b6aebadd7 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -26,6 +26,8 @@ #include "RenderCommonTask.h" +#include "FadeEffect.h" + // These values are used for culling the objects rendered in the shadow map // but are readjusted afterwards #define SHADOW_FRUSTUM_NEAR 1.0f @@ -33,7 +35,7 @@ using namespace render; -extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state); +extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void RenderShadowTask::configure(const Config& configuration) { DependencyManager::get()->setShadowMapEnabled(configuration.enabled); @@ -49,7 +51,8 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, true, gpu::LESS_EQUAL); - initZPassPipelines(*shapePlumber, state); + auto fadeEffect = DependencyManager::get(); + initZPassPipelines(*shapePlumber, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); } // FIXME: calling this here before the zones/lights are drawn during the deferred/forward passes means we're actually using the frames from the previous draw @@ -222,7 +225,6 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con auto& fbo = cascade.framebuffer; RenderArgs* args = renderContext->args; - ShapeKey::Builder defaultKeyBuilder; auto adjustedShadowFrustum = args->getViewFrustum(); // Adjust the frustum near and far depths based on the rendered items bounding box to have @@ -253,53 +255,56 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat, false); - auto shadowPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); - auto shadowDeformedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withDeformed()); - auto shadowDeformedDQPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withDeformed().withDualQuatSkinned()); + const std::vector keys = { + ShapeKey::Builder(), ShapeKey::Builder().withFade(), + ShapeKey::Builder().withDeformed(), ShapeKey::Builder().withDeformed().withFade(), + ShapeKey::Builder().withDeformed().withDualQuatSkinned(), ShapeKey::Builder().withDeformed().withDualQuatSkinned().withFade(), + ShapeKey::Builder().withOwnPipeline(), ShapeKey::Builder().withOwnPipeline().withFade() + }; + std::vector> sortedShapeKeys(keys.size()); - std::vector deformedShapeKeys{}; - std::vector deformedDQShapeKeys{}; - std::vector ownPipelineShapeKeys{}; - - // Iterate through all inShapes and render the unskinned - args->_shapePipeline = shadowPipeline; - batch.setPipeline(shadowPipeline->pipeline); - for (auto items : inShapes) { + const int OWN_PIPELINE_INDEX = 6; + for (const auto& items : inShapes) { + int index = items.first.hasOwnPipeline() ? OWN_PIPELINE_INDEX : 0; if (items.first.isDeformed()) { + index += 2; if (items.first.isDualQuatSkinned()) { - deformedDQShapeKeys.push_back(items.first); - } else { - deformedShapeKeys.push_back(items.first); + index += 2; + } + } + + if (items.first.isFaded()) { + index += 1; + } + + sortedShapeKeys[index].push_back(items.first); + } + + // Render non-withOwnPipeline things + for (size_t i = 0; i < OWN_PIPELINE_INDEX; i++) { + auto& shapeKeys = sortedShapeKeys[i]; + if (shapeKeys.size() > 0) { + const auto& shapePipeline = _shapePlumber->pickPipeline(args, keys[i]); + args->_shapePipeline = shapePipeline; + for (const auto& key : shapeKeys) { + renderShapes(renderContext, _shapePlumber, inShapes.at(key)); } - } else if (!items.first.hasOwnPipeline()) { - renderItems(renderContext, items.second); - } else { - ownPipelineShapeKeys.push_back(items.first); } } - // Reiterate to render the skinned - args->_shapePipeline = shadowDeformedPipeline; - batch.setPipeline(shadowDeformedPipeline->pipeline); - for (const auto& key : deformedShapeKeys) { - renderItems(renderContext, inShapes.at(key)); + // Render withOwnPipeline things + for (size_t i = OWN_PIPELINE_INDEX; i < keys.size(); i++) { + auto& shapeKeys = sortedShapeKeys[i]; + if (shapeKeys.size() > 0) { + args->_shapePipeline = nullptr; + for (const auto& key : shapeKeys) { + args->_itemShapeKey = key._flags.to_ulong(); + renderShapes(renderContext, _shapePlumber, inShapes.at(key)); + } + } } - // Reiterate to render the DQ skinned - args->_shapePipeline = shadowDeformedDQPipeline; - batch.setPipeline(shadowDeformedDQPipeline->pipeline); - for (const auto& key : deformedDQShapeKeys) { - renderItems(renderContext, inShapes.at(key)); - } - - // Finally render the items with their own pipeline last to prevent them from breaking the - // render state. This is probably a temporary code as there is probably something better - // to do in the render call of objects that have their own pipeline. args->_shapePipeline = nullptr; - for (const auto& key : ownPipelineShapeKeys) { - args->_itemShapeKey = key._flags.to_ulong(); - renderItems(renderContext, inShapes.at(key)); - } } args->_batch = nullptr; From 5a8a5bbc55612098b07bb182b84aeb5513785ad4 Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Mon, 22 Oct 2018 18:18:16 -0300 Subject: [PATCH 025/151] Set AEC enabled by default --- .../fragment/SettingsFragment.java | 20 +++++++++++-------- android/app/src/main/res/xml/settings.xml | 3 ++- libraries/audio-client/src/AudioClient.cpp | 4 ++-- libraries/audio-client/src/AudioClient.h | 1 + 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java index cc23665e72..58b6638531 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SettingsFragment.java @@ -3,8 +3,8 @@ package io.highfidelity.hifiinterface.fragment; import android.content.SharedPreferences; import android.media.audiofx.AcousticEchoCanceler; import android.os.Bundle; -import android.preference.Preference; import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; import android.support.annotation.Nullable; import io.highfidelity.hifiinterface.R; @@ -18,17 +18,23 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer private final String HIFI_SETTINGS_AEC_KEY = "aec"; private final String PREFERENCE_KEY_AEC = "aec"; + private final boolean DEFAULT_AEC_ENABLED = true; + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.settings); + boolean aecAvailable = AcousticEchoCanceler.isAvailable(); + PreferenceManager.setDefaultValues(getContext(), R.xml.settings, false); - if (!AcousticEchoCanceler.isAvailable()) { - getPreferenceScreen().getPreferenceManager().findPreference("aec").setEnabled(false); + if (!aecAvailable) { + findPreference(PREFERENCE_KEY_AEC).setEnabled(false); + updateHifiSetting(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, false); } getPreferenceScreen().getSharedPreferences().edit().putBoolean(PREFERENCE_KEY_AEC, - getHifiSettingBoolean(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, false)); + aecAvailable && getHifiSettingBoolean(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, DEFAULT_AEC_ENABLED)).commit(); + } public static SettingsFragment newInstance() { @@ -46,15 +52,13 @@ public class SettingsFragment extends PreferenceFragment implements SharedPrefer public void onPause() { super.onPause(); getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); - } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - Preference pref = findPreference(key); switch (key) { - case "aec": - updateHifiSetting(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, sharedPreferences.getBoolean(key, false)); + case PREFERENCE_KEY_AEC: + updateHifiSetting(HIFI_SETTINGS_ANDROID_GROUP, HIFI_SETTINGS_AEC_KEY, sharedPreferences.getBoolean(key, DEFAULT_AEC_ENABLED)); break; default: break; diff --git a/android/app/src/main/res/xml/settings.xml b/android/app/src/main/res/xml/settings.xml index 5ec47b1aff..934d34ba73 100644 --- a/android/app/src/main/res/xml/settings.xml +++ b/android/app/src/main/res/xml/settings.xml @@ -6,6 +6,7 @@ + android:summary="@string/acoustic_echo_cancellation" + android:defaultValue="true" /> \ No newline at end of file diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index d00bc29054..abe8f628f7 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -465,7 +465,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { #if defined (Q_OS_ANDROID) if (mode == QAudio::AudioInput) { - Setting::Handle enableAEC(SETTING_AEC_KEY, false); + Setting::Handle enableAEC(SETTING_AEC_KEY, DEFAULT_AEC_ENABLED); bool aecEnabled = enableAEC.get(); auto audioClient = DependencyManager::get(); bool headsetOn = audioClient? audioClient->isHeadsetPluggedIn() : false; @@ -1673,7 +1673,7 @@ void AudioClient::setHeadsetPluggedIn(bool pluggedIn) { QThread::msleep(200); } - Setting::Handle enableAEC(SETTING_AEC_KEY, false); + Setting::Handle enableAEC(SETTING_AEC_KEY, DEFAULT_AEC_ENABLED); bool aecEnabled = enableAEC.get(); if ((pluggedIn || !aecEnabled) && _inputDeviceInfo.deviceName() != VOICE_RECOGNITION) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 5e7f1fb8a0..057f477777 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -69,6 +69,7 @@ #define VOICE_COMMUNICATION "voicecommunication" #define SETTING_AEC_KEY "Android/aec" +#define DEFAULT_AEC_ENABLED true #endif class QAudioInput; From 6572400d068f67552f7bc83debcc2faaed3b53e7 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 22 Oct 2018 15:03:09 -0700 Subject: [PATCH 026/151] fix highlight debug script --- .../utilities/render/debugHighlight.js | 55 +++++++++++++------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/scripts/developer/utilities/render/debugHighlight.js b/scripts/developer/utilities/render/debugHighlight.js index 664af836a9..3b171aaaa7 100644 --- a/scripts/developer/utilities/render/debugHighlight.js +++ b/scripts/developer/utilities/render/debugHighlight.js @@ -86,22 +86,23 @@ type: "sphere", dimensions: END_DIMENSIONS, color: COLOR1, - ignoreRayIntersection: true + ignorePickIntersection: true } var end2 = { type: "sphere", dimensions: END_DIMENSIONS, color: COLOR2, - ignoreRayIntersection: true + ignorePickIntersection: true } var laser = Pointers.createPointer(PickType.Ray, { joint: "Mouse", - filter: Picks.PICK_ENTITIES, + filter: Picks.PICK_ENTITIES | Picks.PICK_OVERLAYS | Picks.PICK_AVATARS, renderStates: [{name: "one", end: end1}], defaultRenderStates: [{name: "one", end: end2, distance: 2.0}], enabled: true }); Pointers.setRenderState(laser, "one"); + var hoveredObject = undefined; var HoveringList = "Hovering" var hoveringStyle = { @@ -140,21 +141,41 @@ time = 0 } } - - Entities.hoverEnterEntity.connect(function (id, event) { - // print("hoverEnterEntity"); - if (isSelectionEnabled) Selection.addToSelectedItemsList(HoveringList, "entity", id) - }) - - Entities.hoverOverEntity.connect(function (id, event) { - // print("hoverOverEntity"); - }) - - Entities.hoverLeaveEntity.connect(function (id, event) { - if (isSelectionEnabled) Selection.removeFromSelectedItemsList(HoveringList, "entity", id) - // print("hoverLeaveEntity"); - }) + function getIntersectionTypeString(type) { + if (type === Picks.INTERSECTED_ENTITY) { + return "entity"; + } else if (type === Picks.INTERSECTED_OVERLAY) { + return "overlay"; + } else if (type === Picks.INTERSECTED_AVATAR) { + return "avatar"; + } + } + + function update() { + var result = Pointers.getPrevPickResult(laser); + if (result.intersects) { + // Hovering on something different + if (hoveredObject !== undefined && result.objectID !== hoveredObject.objectID) { + if (isSelectionEnabled) { + Selection.removeFromSelectedItemsList(HoveringList, getIntersectionTypeString(hoveredObject.type), hoveredObject.objectID) + } + } + + // Hovering over something new + if (isSelectionEnabled) { + Selection.addToSelectedItemsList(HoveringList, getIntersectionTypeString(result.type), result.objectID); + hoveredObject = result; + } + } else if (hoveredObject !== undefined) { + // Stopped hovering + if (isSelectionEnabled) { + Selection.removeFromSelectedItemsList(HoveringList, getIntersectionTypeString(hoveredObject.type), hoveredObject.objectID) + hoveredObject = undefined; + } + } + } + Script.update.connect(update); function cleanup() { Pointers.removePointer(laser); From d254db02dcac0bf70817bc31a40574217ae3f07d Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 23 Oct 2018 11:17:50 -0700 Subject: [PATCH 027/151] small cleanup --- libraries/graphics/src/graphics/Material.slh | 2 +- .../graphics/src/graphics/ShaderConstants.h | 2 -- .../render-utils/src/DeferredBufferWrite.slh | 1 - .../render-utils/src/RenderPipelines.cpp | 25 ------------------- .../render-utils/src/deformed_model_dq.slv | 1 - libraries/render/src/render/ShapePipeline.cpp | 1 - libraries/render/src/render/ShapePipeline.h | 8 ------ 7 files changed, 1 insertion(+), 39 deletions(-) diff --git a/libraries/graphics/src/graphics/Material.slh b/libraries/graphics/src/graphics/Material.slh index 2e71e97e06..412e967180 100644 --- a/libraries/graphics/src/graphics/Material.slh +++ b/libraries/graphics/src/graphics/Material.slh @@ -48,7 +48,7 @@ struct Material { vec4 _keySpare3; }; -layout(binding=GRAPHICS_BUFFER_MATERIAL) uniform materialBuffer { +layout(std140, binding=GRAPHICS_BUFFER_MATERIAL) uniform materialBuffer { Material _mat; TexMapArray _texMapArray; }; diff --git a/libraries/graphics/src/graphics/ShaderConstants.h b/libraries/graphics/src/graphics/ShaderConstants.h index c902185d4f..0aff0a7077 100644 --- a/libraries/graphics/src/graphics/ShaderConstants.h +++ b/libraries/graphics/src/graphics/ShaderConstants.h @@ -16,7 +16,6 @@ #define GRAPHICS_BUFFER_SKINNING 0 #define GRAPHICS_BUFFER_MATERIAL 1 -#define GRAPHICS_BUFFER_TEXMAPARRAY 2 #define GRAPHICS_BUFFER_KEY_LIGHT 4 #define GRAPHICS_BUFFER_LIGHT 5 #define GRAPHICS_BUFFER_AMBIENT_LIGHT 6 @@ -41,7 +40,6 @@ namespace buffer { enum Buffer { Skinning = GRAPHICS_BUFFER_SKINNING, Material = GRAPHICS_BUFFER_MATERIAL, - TexMapArray = GRAPHICS_BUFFER_TEXMAPARRAY, Light = GRAPHICS_BUFFER_LIGHT, KeyLight = GRAPHICS_BUFFER_KEY_LIGHT, AmbientLight = GRAPHICS_BUFFER_AMBIENT_LIGHT, diff --git a/libraries/render-utils/src/DeferredBufferWrite.slh b/libraries/render-utils/src/DeferredBufferWrite.slh index 04a65477b2..9602bc4bf4 100644 --- a/libraries/render-utils/src/DeferredBufferWrite.slh +++ b/libraries/render-utils/src/DeferredBufferWrite.slh @@ -21,7 +21,6 @@ layout(location=3) out vec4 _fragColor3; // emissive // the alpha threshold const float alphaThreshold = 0.5; -// FIXME: is this correct? float evalOpaqueFinalAlpha(float alpha, float mapAlpha) { return mix(alpha, 1.0 - alpha, step(mapAlpha, alphaThreshold)); } diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 98596a3d88..2880c06d1c 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -201,31 +201,6 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip addPipeline( Key::Builder().withMaterial().withDeformed().withDualQuatSkinned().withTranslucent().withTangents().withFade(), deformed_model_normal_map_translucent_fade_dq, batchSetter, itemSetter); - - // Depth-only - addPipeline( - Key::Builder().withDepthOnly(), - model_shadow, nullptr, nullptr); - addPipeline( - Key::Builder().withDeformed().withDepthOnly(), - deformed_model_shadow, nullptr, nullptr); - // Same thing but with Fade on - addPipeline( - Key::Builder().withDepthOnly().withFade(), - model_shadow_fade, batchSetter, itemSetter); - addPipeline( - Key::Builder().withDeformed().withDepthOnly().withFade(), - deformed_model_shadow_fade, batchSetter, itemSetter); - - // Now repeat for dual quaternion - // Depth-only - addPipeline( - Key::Builder().withDeformed().withDualQuatSkinned().withDepthOnly(), - deformed_model_shadow_dq, nullptr, nullptr); - // Same thing but with Fade on - addPipeline( - Key::Builder().withDeformed().withDualQuatSkinned().withDepthOnly().withFade(), - deformed_model_shadow_fade_dq, batchSetter, itemSetter); } void initForwardPipelines(ShapePlumber& plumber) { diff --git a/libraries/render-utils/src/deformed_model_dq.slv b/libraries/render-utils/src/deformed_model_dq.slv index c6540c90b1..2165f3b9a4 100644 --- a/libraries/render-utils/src/deformed_model_dq.slv +++ b/libraries/render-utils/src/deformed_model_dq.slv @@ -31,7 +31,6 @@ layout(location=RENDER_UTILS_ATTR_NORMAL_WS) out vec3 _normalWS; layout(location=RENDER_UTILS_ATTR_COLOR) out vec4 _color; void main(void) { - vec4 deformedPosition = vec4(0.0, 0.0, 0.0, 0.0); vec3 deformedNormal = vec3(0.0, 0.0, 0.0); evalMeshDeformer(inPosition, deformedPosition, inNormal.xyz, deformedNormal, diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 2b2accde37..e7c88b3923 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -96,7 +96,6 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p locations->lightingModelBufferUnit = program->getUniformBuffers().isValid(render_utils::slot::buffer::LightModel); locations->skinClusterBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::Skinning); locations->materialBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::Material); - locations->texMapArrayBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::TexMapArray); locations->keyLightBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::KeyLight); locations->lightBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::Light); locations->lightAmbientBufferUnit = program->getUniformBuffers().isValid(graphics::slot::buffer::AmbientLight); diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index bd6ac6521a..715b467e49 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -32,7 +32,6 @@ public: UNLIT, DEFORMED, DUAL_QUAT_SKINNED, - DEPTH_ONLY, DEPTH_BIAS, WIREFRAME, NO_CULL_FACE, @@ -80,7 +79,6 @@ public: Builder& withUnlit() { _flags.set(UNLIT); return (*this); } Builder& withDeformed() { _flags.set(DEFORMED); return (*this); } Builder& withDualQuatSkinned() { _flags.set(DUAL_QUAT_SKINNED); return (*this); } - Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); return (*this); } Builder& withDepthBias() { _flags.set(DEPTH_BIAS); return (*this); } Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); } Builder& withoutCullFace() { _flags.set(NO_CULL_FACE); return (*this); } @@ -133,9 +131,6 @@ public: Builder& withDualQuatSkinned() { _flags.set(DUAL_QUAT_SKINNED); _mask.set(DUAL_QUAT_SKINNED); return (*this); } Builder& withoutDualQuatSkinned() { _flags.reset(DUAL_QUAT_SKINNED); _mask.set(DUAL_QUAT_SKINNED); return (*this); } - Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); } - Builder& withoutDepthOnly() { _flags.reset(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); } - Builder& withDepthBias() { _flags.set(DEPTH_BIAS); _mask.set(DEPTH_BIAS); return (*this); } Builder& withoutDepthBias() { _flags.reset(DEPTH_BIAS); _mask.set(DEPTH_BIAS); return (*this); } @@ -171,7 +166,6 @@ public: bool isTranslucent() const { return _flags[TRANSLUCENT]; } bool isDeformed() const { return _flags[DEFORMED]; } bool isDualQuatSkinned() const { return _flags[DUAL_QUAT_SKINNED]; } - bool isDepthOnly() const { return _flags[DEPTH_ONLY]; } bool isDepthBiased() const { return _flags[DEPTH_BIAS]; } bool isWireframe() const { return _flags[WIREFRAME]; } bool isCullFace() const { return !_flags[NO_CULL_FACE]; } @@ -211,7 +205,6 @@ inline QDebug operator<<(QDebug debug, const ShapeKey& key) { << "isTranslucent:" << key.isTranslucent() << "isDeformed:" << key.isDeformed() << "isDualQuatSkinned:" << key.isDualQuatSkinned() - << "isDepthOnly:" << key.isDepthOnly() << "isDepthBiased:" << key.isDepthBiased() << "isWireframe:" << key.isWireframe() << "isCullFace:" << key.isCullFace() @@ -239,7 +232,6 @@ public: bool lightingModelBufferUnit{ false }; bool skinClusterBufferUnit{ false }; bool materialBufferUnit{ false }; - bool texMapArrayBufferUnit{ false }; bool keyLightBufferUnit{ false }; bool lightBufferUnit{ false }; bool lightAmbientBufferUnit{ false }; From e9dae8ebeb5f25b4b11479c5f920dd89edad068c Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 23 Oct 2018 16:07:18 -0700 Subject: [PATCH 028/151] added force stand option for avatar lean behavior, you can now squat to your hearts content --- interface/src/avatar/MyAvatar.cpp | 6 +++++- interface/src/ui/PreferencesDialog.cpp | 11 ++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 71c3ba4fc6..a6a7689cdd 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -575,7 +575,11 @@ void MyAvatar::update(float deltaTime) { upSpine2 = glm::normalize(upSpine2); } float angleSpine2 = glm::dot(upSpine2, glm::vec3(0.0f, 1.0f, 0.0f)); - if (getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y < (headDefaultPositionAvatarSpace.y - SQUAT_THRESHOLD) && (angleSpine2 > COSINE_THIRTY_DEGREES)) { + + if (getControllerPoseInAvatarFrame(controller::Action::HEAD).getTranslation().y < (headDefaultPositionAvatarSpace.y - SQUAT_THRESHOLD) && + (angleSpine2 > COSINE_THIRTY_DEGREES) && + (getUserRecenterModel() != MyAvatar::SitStandModelType::ForceStand)) { + _squatTimer += deltaTime; if (_squatTimer > SQUATTY_TIMEOUT) { _squatTimer = 0.0f; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 34d80f50cf..6cfe928896 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -267,8 +267,10 @@ void setupPreferences() { return 0; case MyAvatar::SitStandModelType::ForceSit: return 1; - case MyAvatar::SitStandModelType::DisableHMDLean: + case MyAvatar::SitStandModelType::ForceStand: return 2; + case MyAvatar::SitStandModelType::DisableHMDLean: + return 3; } }; auto setter = [myAvatar](int value) { @@ -281,13 +283,16 @@ void setupPreferences() { myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::ForceSit); break; case 2: + myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::ForceStand); + break; + case 3: myAvatar->setUserRecenterModel(MyAvatar::SitStandModelType::DisableHMDLean); break; } }; - auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Auto / Force Sit / Disable Recenter", getter, setter); + auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Auto / Force Sit / Force Stand / Disable Recenter", getter, setter); QStringList items; - items << "Auto - turns on avatar leaning when standing in real world" << "Seated - disables all avatar leaning while sitting in real world" << "Disabled - allows avatar sitting on the floor [Experimental]"; + items << "Auto - turns on avatar leaning when standing in real world" << "Seated - disables all avatar leaning while sitting in real world" << "Standing - enables avatar leaning while sitting in real world" << "Disabled - allows avatar sitting on the floor [Experimental]"; preference->setHeading("Avatar leaning behavior"); preference->setItems(items); preferences->addPreference(preference); From 5063d0b527fc6f7b4d0a0721860ec5be9a78cbb2 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 23 Oct 2018 16:51:56 -0700 Subject: [PATCH 029/151] changed handAzimuth to non const function --- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/MyAvatar.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f75f3fbdfc..ac919d4420 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -997,7 +997,7 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { // Find the vector halfway between the hip to hand azimuth vectors // This midpoint hand azimuth is in Avatar space -glm::vec2 MyAvatar::computeHandAzimuth() const { +glm::vec2 MyAvatar::computeHandAzimuth() { controller::Pose leftHandPoseAvatarSpace = getLeftHandPose(); controller::Pose rightHandPoseAvatarSpace = getRightHandPose(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 6783cb8995..1be07a244e 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -337,7 +337,7 @@ public: void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix); // compute the hip to hand average azimuth. - glm::vec2 computeHandAzimuth() const; + glm::vec2 computeHandAzimuth(); // read the location of a hand controller and save the transform void updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache); From d8217e9a7917cdf9ea5bab3978365ff332c9ee3b Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 23 Oct 2018 22:42:06 -0400 Subject: [PATCH 030/151] synthesize key release events on Desktop focus change --- interface/src/Application.cpp | 5 +++++ interface/src/Application.h | 1 + 2 files changed, 6 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e515a22403..c3196e69b1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4069,6 +4069,10 @@ void Application::focusOutEvent(QFocusEvent* event) { SpacemouseManager::getInstance().ManagerFocusOutEvent(); #endif + synthesizeKeyReleasEvents(); +} + +void Application::synthesizeKeyReleasEvents() { // synthesize events for keys currently pressed, since we may not get their release events // Because our key event handlers may manipulate _keysPressed, lets swap the keys pressed into a local copy, // clearing the existing list. @@ -4694,6 +4698,7 @@ void Application::idle() { if (_keyboardDeviceHasFocus && activeFocusItem != offscreenUi->getRootItem()) { _keyboardMouseDevice->pluginFocusOutEvent(); _keyboardDeviceHasFocus = false; + synthesizeKeyReleasEvents(); } else if (activeFocusItem == offscreenUi->getRootItem()) { _keyboardDeviceHasFocus = true; } diff --git a/interface/src/Application.h b/interface/src/Application.h index c3896a64e4..6a46032a73 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -541,6 +541,7 @@ private: void keyReleaseEvent(QKeyEvent* event); void focusOutEvent(QFocusEvent* event); + void synthesizeKeyReleasEvents(); void focusInEvent(QFocusEvent* event); void mouseMoveEvent(QMouseEvent* event); From f86518df081460f2e06fcde2197b89d455c90f63 Mon Sep 17 00:00:00 2001 From: Flame Soulis Date: Wed, 24 Oct 2018 07:59:43 -0400 Subject: [PATCH 031/151] Changed QString to char* for SpacemouseManager --- plugins/hifiSpacemouse/src/SpacemouseManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/hifiSpacemouse/src/SpacemouseManager.cpp b/plugins/hifiSpacemouse/src/SpacemouseManager.cpp index 4641799b79..220524f303 100644 --- a/plugins/hifiSpacemouse/src/SpacemouseManager.cpp +++ b/plugins/hifiSpacemouse/src/SpacemouseManager.cpp @@ -20,7 +20,7 @@ #include -const QString SpacemouseManager::NAME { "Spacemouse" }; +const char* SpacemouseManager::NAME { "Spacemouse" }; const float MAX_AXIS = 75.0f; // max forward = 2x speed #define LOGITECH_VENDOR_ID 0x46d From f25820c81a1bedf5ecc88b667b9ea25ae3e2dea0 Mon Sep 17 00:00:00 2001 From: Flame Soulis Date: Wed, 24 Oct 2018 08:24:42 -0400 Subject: [PATCH 032/151] Add destroyInputPlugins() --- plugins/hifiSpacemouse/src/SpacemouseProvider.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/hifiSpacemouse/src/SpacemouseProvider.cpp b/plugins/hifiSpacemouse/src/SpacemouseProvider.cpp index c623f77d73..354b4711a3 100644 --- a/plugins/hifiSpacemouse/src/SpacemouseProvider.cpp +++ b/plugins/hifiSpacemouse/src/SpacemouseProvider.cpp @@ -38,6 +38,10 @@ public: return _inputPlugins; } + virtual void destroyInputPlugins() override { + _inputPlugins.clear(); + } + private: InputPluginList _inputPlugins; }; From f1f5f576506db2983509ae63e559d426730d3218 Mon Sep 17 00:00:00 2001 From: Flame Soulis Date: Wed, 24 Oct 2018 08:52:34 -0400 Subject: [PATCH 033/151] Re-enabled all Axis (by popular request) --- plugins/hifiSpacemouse/src/SpacemouseManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/hifiSpacemouse/src/SpacemouseManager.cpp b/plugins/hifiSpacemouse/src/SpacemouseManager.cpp index 220524f303..e5fd2a3501 100644 --- a/plugins/hifiSpacemouse/src/SpacemouseManager.cpp +++ b/plugins/hifiSpacemouse/src/SpacemouseManager.cpp @@ -116,8 +116,8 @@ controller::Input::NamedVector SpacemouseDevice::getAvailableInputs() const { makePair(TRANSLATE_X, "TranslateX"), makePair(TRANSLATE_Y, "TranslateY"), makePair(TRANSLATE_Z, "TranslateZ"), - //makePair(ROTATE_X, "RotateX"), - //makePair(ROTATE_Y, "RotateY"), + makePair(ROTATE_X, "RotateX"), + makePair(ROTATE_Y, "RotateY"), makePair(ROTATE_Z, "RotateZ"), }; From 370801bd85a2f753453082c7cffb78b21b5756d0 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 24 Oct 2018 16:23:05 -0700 Subject: [PATCH 034/151] removed _spine2position variable and added handling for when there is no spine2 joint. In that case the head will do the recentering rotation and the shoulders will not move with the hand azimuth --- interface/src/avatar/MyAvatar.cpp | 72 ++++++++++-------------- interface/src/avatar/MyAvatar.h | 3 +- interface/src/avatar/MySkeletonModel.cpp | 3 +- 3 files changed, 33 insertions(+), 45 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ac919d4420..e4ff88974a 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -538,33 +538,24 @@ void MyAvatar::update(float deltaTime) { // put the average hand azimuth into sensor space. // then mix it with head facing direction to determine rotation recenter - if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { - int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2"); - if (!(spine2Index < 0)) { - // use the spine for the azimuth origin. - glm::quat spine2Rot = getAbsoluteJointRotationInObjectFrame(spine2Index); - glm::vec3 handHipAzimuthAvatarSpace = spine2Rot * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); - glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), handHipAzimuthAvatarSpace); - glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); - glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); - glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, handHipAzimuthWorldSpace); - glm::vec2 normedHandHipAzimuthSensorSpace(0.0f, 1.0f); - if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) { - normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); - } + int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2"); + if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && !(spine2Index < 0)) { + + // use the spine for the azimuth origin. + glm::quat spine2Rot = getAbsoluteJointRotationInObjectFrame(spine2Index); + glm::vec3 handHipAzimuthAvatarSpace = spine2Rot * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); + glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), handHipAzimuthAvatarSpace); + glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); + glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); + glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, handHipAzimuthWorldSpace); + glm::vec2 normedHandHipAzimuthSensorSpace(0.0f, 1.0f); + if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) { + normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH); _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); } else { - glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); - glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); - glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); - glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, handHipAzimuthWorldSpace); - glm::vec2 normedHandHipAzimuthSensorSpace(0.0f, 1.0f); - if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) { - normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); - } - glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH); - _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); + // use head facing if the chest arms vector is up or down. + _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, _headControllerFacing, tau); } } else { _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, _headControllerFacing, tau); @@ -996,30 +987,27 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } // Find the vector halfway between the hip to hand azimuth vectors -// This midpoint hand azimuth is in Avatar space -glm::vec2 MyAvatar::computeHandAzimuth() { +// This midpoint hand azimuth is in Spine2 space +glm::vec2 MyAvatar::computeHandAzimuth() const { controller::Pose leftHandPoseAvatarSpace = getLeftHandPose(); controller::Pose rightHandPoseAvatarSpace = getRightHandPose(); - - int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2"); - glm::vec3 spine2Position(0.0f,0.0f,0.0f); - glm::quat spine2Rotation(0.0f, 0.0f, 0.0f, 1.0f); - if (!(spine2Index < 0)) { - // use the spine for the azimuth origin. - spine2Position = getAbsoluteJointTranslationInObjectFrame(spine2Index); - _spine2PositionAvatarSpace = _spine2PositionAvatarSpace + ((0.05f) * (spine2Position - _spine2PositionAvatarSpace)); - spine2Rotation = getAbsoluteJointRotationInObjectFrame(spine2Index); - } - glm::vec3 rightHandOffset = rightHandPoseAvatarSpace.translation - spine2Position; - glm::vec3 leftHandOffset = leftHandPoseAvatarSpace.translation - spine2Position; - glm::vec3 rightHandSpine2Space = glm::inverse(spine2Rotation) * rightHandOffset; - glm::vec3 leftHandSpine2Space = glm::inverse(spine2Rotation) * leftHandOffset; - controller::Pose headPoseAvatarSpace = getControllerPoseInAvatarFrame(controller::Action::HEAD); const float HALFWAY = 0.50f; + const float SPINE2_POSITION_FILTER = 0.05f; + glm::vec2 latestHipToHandController = _hipToHandController; - if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid() && headPoseAvatarSpace.isValid()) { + int spine2Index = _skeletonModel->getRig().indexOfJoint("Spine2"); + if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid() && headPoseAvatarSpace.isValid() && !(spine2Index < 0)) { + + glm::vec3 spine2Position = getAbsoluteJointTranslationInObjectFrame(spine2Index); + glm::quat spine2Rotation = getAbsoluteJointRotationInObjectFrame(spine2Index); + + glm::vec3 rightHandOffset = rightHandPoseAvatarSpace.translation - spine2Position; + glm::vec3 leftHandOffset = leftHandPoseAvatarSpace.translation - spine2Position; + glm::vec3 rightHandSpine2Space = glm::inverse(spine2Rotation) * rightHandOffset; + glm::vec3 leftHandSpine2Space = glm::inverse(spine2Rotation) * leftHandOffset; + // we need the old azimuth reading to prevent flipping the facing direction 180 // in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart. glm::vec2 oldAzimuthReading = _hipToHandController; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1be07a244e..85ad6fe7a4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -337,7 +337,7 @@ public: void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix); // compute the hip to hand average azimuth. - glm::vec2 computeHandAzimuth(); + glm::vec2 computeHandAzimuth() const; // read the location of a hand controller and save the transform void updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache); @@ -1732,7 +1732,6 @@ private: glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; glm::vec2 _hipToHandController { 0.0f, 1.0f }; // spine2 facing vector in xz plane (spine2 space) - glm::vec3 _spine2PositionAvatarSpace { 0.0f, 0.0f, 0.0f }; float _currentStandingHeight { 0.0f }; bool _resetMode { true }; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 0cda71ca77..b5a495d296 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -243,6 +243,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { + const float SPINE2_ROTATION_FILTER = 0.5f; AnimPose currentSpine2Pose; AnimPose currentHeadPose; AnimPose currentHipsPose; @@ -262,7 +263,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { } generateBasisVectors(up, fwd, u, v, w); AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); - currentSpine2Pose.rot() = safeLerp(currentSpine2Pose.rot(), newSpinePose.rot(), 0.5f); + currentSpine2Pose.rot() = safeLerp(currentSpine2Pose.rot(), newSpinePose.rot(), SPINE2_ROTATION_FILTER); params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentSpine2Pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } From 7fe53d4399f8a24043b6154c7d7785bc321bf7af Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 24 Oct 2018 16:32:18 -0700 Subject: [PATCH 035/151] removed extra const float value --- interface/src/avatar/MyAvatar.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e4ff88974a..15ddb2fe87 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -993,7 +993,6 @@ glm::vec2 MyAvatar::computeHandAzimuth() const { controller::Pose rightHandPoseAvatarSpace = getRightHandPose(); controller::Pose headPoseAvatarSpace = getControllerPoseInAvatarFrame(controller::Action::HEAD); const float HALFWAY = 0.50f; - const float SPINE2_POSITION_FILTER = 0.05f; glm::vec2 latestHipToHandController = _hipToHandController; From 11d82e0ad0f12ee25bffcd04febb8c9c6ac6e87b Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Thu, 25 Oct 2018 19:12:51 -0300 Subject: [PATCH 036/151] New login + signup --- android/app/src/main/AndroidManifest.xml | 4 + android/app/src/main/cpp/native.cpp | 66 ++-- .../highfidelity/hifiinterface/HifiUtils.java | 3 + .../hifiinterface/InterfaceActivity.java | 7 +- .../hifiinterface/LoginMenuActivity.java | 177 +++++++++ .../hifiinterface/MainActivity.java | 85 +--- .../hifiinterface/SplashActivity.java | 9 +- .../fragment/FriendsFragment.java | 2 - .../hifiinterface/fragment/LoginFragment.java | 121 +++--- .../fragment/SignedInFragment.java | 73 ---- .../fragment/SignupFragment.java | 121 +++--- .../fragment/StartMenuFragment.java | 85 ++++ .../drawable/encourage_login_background.jpg | Bin 0 -> 49513 bytes .../app/src/main/res/drawable/hifi_header.xml | 2 +- .../src/main/res/drawable/ic_right_arrow.xml | 11 + ...d_button.xml => rounded_button_color1.xml} | 0 .../res/drawable/rounded_button_color3.xml | 24 ++ .../src/main/res/drawable/rounded_edit.xml | 7 - .../res/layout/activity_encourage_login.xml | 14 + .../src/main/res/layout/fragment_login.xml | 369 ++++++++++++------ .../main/res/layout/fragment_login_menu.xml | 104 +++++ .../src/main/res/layout/fragment_signedin.xml | 63 --- .../src/main/res/layout/fragment_signup.xml | 253 ++++++++---- android/app/src/main/res/values/colors.xml | 1 + android/app/src/main/res/values/dimens.xml | 6 +- android/app/src/main/res/values/strings.xml | 13 +- 26 files changed, 1057 insertions(+), 563 deletions(-) create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java delete mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SignedInFragment.java create mode 100644 android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java create mode 100644 android/app/src/main/res/drawable/encourage_login_background.jpg create mode 100644 android/app/src/main/res/drawable/ic_right_arrow.xml rename android/app/src/main/res/drawable/{rounded_button.xml => rounded_button_color1.xml} (100%) create mode 100644 android/app/src/main/res/drawable/rounded_button_color3.xml delete mode 100644 android/app/src/main/res/drawable/rounded_edit.xml create mode 100644 android/app/src/main/res/layout/activity_encourage_login.xml create mode 100644 android/app/src/main/res/layout/fragment_login_menu.xml delete mode 100644 android/app/src/main/res/layout/fragment_signedin.xml diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b216819ed0..57e708068f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -71,6 +71,10 @@ android:screenOrientation="portrait" android:theme="@style/Theme.AppCompat.Translucent.NoActionBar" /> + + (); @@ -269,17 +269,15 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeCancelLogin(JNIE } JNIEXPORT void JNICALL -Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeCancelLogin(JNIEnv *env, +Java_io_highfidelity_hifiinterface_fragment_SignupFragment_cancelLogin(JNIEnv *env, jobject instance) { - Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeCancelLogin(env, instance); + Java_io_highfidelity_hifiinterface_fragment_LoginFragment_cancelLogin(env, instance); } - JNIEXPORT void JNICALL -Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *env, jobject instance, - jstring username_, jstring password_, - jobject usernameChangedListener) { +Java_io_highfidelity_hifiinterface_fragment_LoginFragment_login(JNIEnv *env, jobject instance, + jstring username_, jstring password_) { const char *c_username = env->GetStringUTFChars(username_, 0); const char *c_password = env->GetStringUTFChars(password_, 0); QString username = QString(c_username); @@ -290,7 +288,6 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en auto accountManager = DependencyManager::get(); __loginCompletedListener = QAndroidJniObject(instance); - __usernameChangedListener = QAndroidJniObject(usernameChangedListener); QObject::connect(accountManager.data(), &AccountManager::loginComplete, [](const QUrl& authURL) { jboolean jSuccess = (jboolean) true; @@ -306,24 +303,16 @@ Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *en } }); - QObject::connect(accountManager.data(), &AccountManager::usernameChanged, [](const QString& username) { - QAndroidJniObject string = QAndroidJniObject::fromString(username); - if (__usernameChangedListener.isValid()) { - __usernameChangedListener.callMethod("handleUsernameChanged", "(Ljava/lang/String;)V", string.object()); - } - }); - QMetaObject::invokeMethod(accountManager.data(), "requestAccessToken", Q_ARG(const QString&, username), Q_ARG(const QString&, password)); } JNIEXPORT void JNICALL -Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeLogin(JNIEnv *env, +Java_io_highfidelity_hifiinterface_fragment_SignupFragment_login(JNIEnv *env, jobject instance, jstring username_, - jstring password_, - jobject usernameChangedListener) { - Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(env, instance, username_, password_, usernameChangedListener); + jstring password_) { + Java_io_highfidelity_hifiinterface_fragment_LoginFragment_login(env, instance, username_, password_); } JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeInitAfterAppLoaded(JNIEnv* env, jobject obj) { @@ -331,7 +320,7 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeInitAf } JNIEXPORT void JNICALL -Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeSignup(JNIEnv *env, jobject instance, +Java_io_highfidelity_hifiinterface_fragment_SignupFragment_signup(JNIEnv *env, jobject instance, jstring email_, jstring username_, jstring password_) { @@ -359,8 +348,6 @@ Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeSignup(JNIEnv * }); QObject::connect(&AndroidHelper::instance(), &AndroidHelper::handleSignupFailed, [](QString errorString) { - jboolean jSuccess = (jboolean) false; - jstring jError = QAndroidJniObject::fromString(errorString).object(); if (__signupCompletedListener.isValid()) { QAndroidJniObject string = QAndroidJniObject::fromString(errorString); __signupCompletedListener.callMethod("handleSignupFailed", "(Ljava/lang/String;)V", string.object()); @@ -371,19 +358,13 @@ Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeSignup(JNIEnv * } JNIEXPORT void JNICALL -Java_io_highfidelity_hifiinterface_fragment_SignupFragment_nativeCancelSignup(JNIEnv *env, jobject instance) { +Java_io_highfidelity_hifiinterface_fragment_SignupFragment_cancelSignup(JNIEnv *env, jobject instance) { QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::handleSignupCompleted, nullptr, nullptr); QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::handleSignupFailed, nullptr, nullptr); __signupCompletedListener = nullptr; } -JNIEXPORT jboolean JNICALL -Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeIsLoggedIn(JNIEnv *env, jobject instance) { - auto accountManager = DependencyManager::get(); - return accountManager->isLoggedIn(); -} - JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_fragment_FriendsFragment_nativeGetAccessToken(JNIEnv *env, jobject instance) { auto accountManager = DependencyManager::get(); @@ -407,22 +388,43 @@ Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(J } JNIEXPORT jboolean JNICALL -Java_io_highfidelity_hifiinterface_MainActivity_nativeIsLoggedIn(JNIEnv *env, jobject instance) { +Java_io_highfidelity_hifiinterface_HifiUtils_isUserLoggedIn(JNIEnv *env, jobject instance) { return DependencyManager::get()->isLoggedIn(); } JNIEXPORT void JNICALL -Java_io_highfidelity_hifiinterface_MainActivity_nativeLogout(JNIEnv *env, jobject instance) { +Java_io_highfidelity_hifiinterface_MainActivity_logout(JNIEnv *env, jobject instance) { DependencyManager::get()->logout(); } JNIEXPORT jstring JNICALL -Java_io_highfidelity_hifiinterface_MainActivity_nativeGetDisplayName(JNIEnv *env, +Java_io_highfidelity_hifiinterface_MainActivity_getUsername(JNIEnv *env, jobject instance) { QString username = DependencyManager::get()->getAccountInfo().getUsername(); return env->NewStringUTF(username.toLatin1().data()); } +JNIEXPORT void JNICALL +Java_io_highfidelity_hifiinterface_MainActivity_setUsernameChangedListener(JNIEnv *env, + jobject instance, + jobject usernameChangedListener) { + __usernameChangedListener = QAndroidJniObject(usernameChangedListener); + + if (!__usernameChangedListener.isValid()) { + return; + } + + auto accountManager = DependencyManager::get(); + + QObject::connect(accountManager.data(), &AccountManager::usernameChanged, [](const QString& username) { + QAndroidJniObject string = QAndroidJniObject::fromString(username); + if (__usernameChangedListener.isValid()) { + __usernameChangedListener.callMethod("handleUsernameChanged", "(Ljava/lang/String;)V", string.object()); + } + }); + +} + JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeBeforeEnterBackground(JNIEnv *env, jobject obj) { AndroidHelper::instance().notifyBeforeEnterBackground(); diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java b/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java index f92cd0a385..a35734b9d7 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/HifiUtils.java @@ -64,4 +64,7 @@ public class HifiUtils { public native String protocolVersionSignature(); + public native boolean isUserLoggedIn(); + + } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java index 90e411173b..5250d2582b 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -313,11 +313,10 @@ public class InterfaceActivity extends QtActivity implements WebViewFragment.OnW break; case "Login": nativeBeforeEnterBackground(); - Intent loginIntent = new Intent(this, MainActivity.class); - loginIntent.putExtra(MainActivity.EXTRA_FRAGMENT, activityName); - loginIntent.putExtra(MainActivity.EXTRA_BACK_TO_SCENE, backToScene); + Intent loginIntent = new Intent(this, LoginMenuActivity.class); + loginIntent.putExtra(LoginMenuActivity.EXTRA_BACK_TO_SCENE, backToScene); if (args != null && args.containsKey(DOMAIN_URL)) { - loginIntent.putExtra(DOMAIN_URL, (String) args.get(DOMAIN_URL)); + loginIntent.putExtra(LoginMenuActivity.EXTRA_DOMAIN_URL, (String) args.get(DOMAIN_URL)); } startActivity(loginIntent); break; diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java new file mode 100644 index 0000000000..ac9fd2eed1 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/LoginMenuActivity.java @@ -0,0 +1,177 @@ +package io.highfidelity.hifiinterface; + + +import android.app.Fragment; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import io.highfidelity.hifiinterface.fragment.LoginFragment; +import io.highfidelity.hifiinterface.fragment.SignupFragment; +import io.highfidelity.hifiinterface.fragment.StartMenuFragment; + +public class LoginMenuActivity extends AppCompatActivity + implements StartMenuFragment.StartMenuInteractionListener, + LoginFragment.OnLoginInteractionListener, + SignupFragment.OnSignupInteractionListener { + + public static final String EXTRA_FINISH_ON_BACK = "finishOnBack"; + public static final String EXTRA_BACK_TO_SCENE = "backToScene"; + public static final String EXTRA_DOMAIN_URL = "url"; + + private boolean finishOnBack; + private boolean backToScene; + private String domainUrlToBack; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_encourage_login); + + finishOnBack = getIntent().getBooleanExtra(EXTRA_FINISH_ON_BACK, false); + backToScene = getIntent().getBooleanExtra(EXTRA_BACK_TO_SCENE, false); + domainUrlToBack = getIntent().getStringExtra(EXTRA_DOMAIN_URL); + + if (savedInstanceState != null) { + finishOnBack = savedInstanceState.getBoolean(EXTRA_FINISH_ON_BACK, false); + backToScene = savedInstanceState.getBoolean(EXTRA_BACK_TO_SCENE, false); + domainUrlToBack = savedInstanceState.getString(EXTRA_DOMAIN_URL); + } + + loadMenuFragment(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(EXTRA_FINISH_ON_BACK, finishOnBack); + outState.putBoolean(EXTRA_BACK_TO_SCENE, backToScene); + outState.putString(EXTRA_DOMAIN_URL, domainUrlToBack); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + finishOnBack = savedInstanceState.getBoolean(EXTRA_FINISH_ON_BACK, false); + backToScene = savedInstanceState.getBoolean(EXTRA_BACK_TO_SCENE, false); + domainUrlToBack = savedInstanceState.getString(EXTRA_DOMAIN_URL); + } + + private void loadMenuFragment() { + FragmentManager fragmentManager = getFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + Fragment fragment = StartMenuFragment.newInstance(); + fragmentTransaction.replace(R.id.content_frame, fragment); + fragmentTransaction.addToBackStack(fragment.toString()); + fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + fragmentTransaction.commit(); + hideStatusBar(); + } + + @Override + protected void onResume() { + super.onResume(); + hideStatusBar(); + } + + private void hideStatusBar() { + View decorView = getWindow().getDecorView(); + // Hide the status bar. + int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + decorView.setSystemUiVisibility(uiOptions); + } + + @Override + public void onSignupButtonClicked() { + loadSignupFragment(); + } + + @Override + public void onLoginButtonClicked() { + loadLoginFragment(); + } + + @Override + public void onSkipLoginClicked() { + loadMainActivity(); + } + + + private void loadSignupFragment() { + FragmentManager fragmentManager = getFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + Fragment fragment = SignupFragment.newInstance(); + fragmentTransaction.replace(R.id.content_frame, fragment); + fragmentTransaction.addToBackStack(fragment.toString()); + fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + fragmentTransaction.commit(); + hideStatusBar(); + } + + private void loadLoginFragment() { + FragmentManager fragmentManager = getFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + Fragment fragment = LoginFragment.newInstance(); + fragmentTransaction.replace(R.id.content_frame, fragment); + fragmentTransaction.addToBackStack(fragment.toString()); + fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + fragmentTransaction.commit(); + hideStatusBar(); + } + + @Override + public void onLoginCompleted() { + loadMainActivity(); + } + + @Override + public void onCancelLogin() { + getFragmentManager().popBackStack(); + } + + @Override + public void onCancelSignup() { + getFragmentManager().popBackStack(); + } + + private void loadMainActivity() { + finish(); + if (backToScene) { + backToScene = false; + goToDomain(domainUrlToBack != null? domainUrlToBack : ""); + } else { + startActivity(new Intent(this, MainActivity.class)); + } + } + + private void goToDomain(String domainUrl) { + Intent intent = new Intent(this, InterfaceActivity.class); + intent.putExtra(InterfaceActivity.DOMAIN_URL, domainUrl); + finish(); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + startActivity(intent); + } + + + @Override + public void onSignupCompleted() { + loadMainActivity(); + } + + @Override + public void onBackPressed() { + FragmentManager fm = getFragmentManager(); + int index = fm.getBackStackEntryCount() - 1; + if (index > 0) { + super.onBackPressed(); + } else if (finishOnBack){ + finishAffinity(); + } else { + finish(); + } + } +} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java index 7df04100b0..76cda847cb 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/MainActivity.java @@ -1,5 +1,6 @@ package io.highfidelity.hifiinterface; +import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; @@ -29,23 +30,16 @@ import android.widget.TextView; import com.squareup.picasso.Callback; import com.squareup.picasso.Picasso; -import java.util.HashMap; -import java.util.Map; - import io.highfidelity.hifiinterface.fragment.FriendsFragment; import io.highfidelity.hifiinterface.fragment.HomeFragment; -import io.highfidelity.hifiinterface.fragment.LoginFragment; import io.highfidelity.hifiinterface.fragment.PolicyFragment; import io.highfidelity.hifiinterface.fragment.SettingsFragment; -import io.highfidelity.hifiinterface.fragment.SignedInFragment; -import io.highfidelity.hifiinterface.fragment.SignupFragment;import io.highfidelity.hifiinterface.task.DownloadProfileImageTask; +import io.highfidelity.hifiinterface.fragment.SignupFragment; +import io.highfidelity.hifiinterface.task.DownloadProfileImageTask; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, - LoginFragment.OnLoginInteractionListener, HomeFragment.OnHomeInteractionListener, - FriendsFragment.OnHomeInteractionListener, - SignupFragment.OnSignupInteractionListener, - SignedInFragment.OnSignedInInteractionListener { + FriendsFragment.OnHomeInteractionListener { private static final int PROFILE_PICTURE_PLACEHOLDER = R.drawable.default_profile_avatar; public static final String DEFAULT_FRAGMENT = "Home"; @@ -55,9 +49,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On private String TAG = "HighFidelity"; - public native boolean nativeIsLoggedIn(); - public native void nativeLogout(); - public native String nativeGetDisplayName(); + public native void logout(); + public native void setUsernameChangedListener(Activity usernameChangedListener); + public native String getUsername(); private DrawerLayout mDrawerLayout; private NavigationView mNavigationView; @@ -130,9 +124,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On private void loadFragment(String fragment) { switch (fragment) { - case "Login": - loadLoginFragment(); - break; case "Home": loadHomeFragment(true); break; @@ -153,14 +144,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On loadFragment(fragment, getString(R.string.home), getString(R.string.tagFragmentHome), addToBackStack, true); } - private void loadLoginFragment() { - Fragment fragment = LoginFragment.newInstance(); - loadFragment(fragment, getString(R.string.login), getString(R.string.tagFragmentLogin), true, true); - } - - private void loadSignedInFragment() { - Fragment fragment = SignedInFragment.newInstance(); - loadFragment(fragment, getString(R.string.welcome), getString(R.string.tagFragmentSignedIn), true, true); + private void startLoginMenuActivity() { + startActivity(new Intent(this, LoginMenuActivity.class)); } private void loadSignupFragment() { @@ -223,7 +208,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On private void updateLoginMenu() { - if (nativeIsLoggedIn()) { + if (HifiUtils.getInstance().isUserLoggedIn()) { mLoginPanel.setVisibility(View.GONE); mProfilePanel.setVisibility(View.VISIBLE); mLogoutOption.setVisibility(View.VISIBLE); @@ -239,7 +224,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } private void updateProfileHeader() { - updateProfileHeader(nativeGetDisplayName()); + updateProfileHeader(getUsername()); } private void updateProfileHeader(String username) { if (!username.isEmpty()) { @@ -289,15 +274,22 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On @Override protected void onStart() { super.onStart(); + setUsernameChangedListener(this); updateLoginMenu(); } + @Override + protected void onStop() { + super.onStop(); + setUsernameChangedListener(null); + } + public void onLoginClicked(View view) { - loadLoginFragment(); + startLoginMenuActivity(); } public void onLogoutClicked(View view) { - nativeLogout(); + logout(); updateLoginMenu(); exitLoggedInFragment(); @@ -338,42 +330,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On startActivity(intent); } - @Override - public void onLoginCompleted() { - loadHomeFragment(false); - updateLoginMenu(); - if (backToScene) { - backToScene = false; - goToLastLocation(); - } - } - - @Override - public void onGettingStarted() { - loadHomeFragment(false); - if (backToScene) { - backToScene = false; - goToLastLocation(); - } - } - - @Override - public void onLoginRequested() { - // go back from signup to login - onBackPressed(); - } - - @Override - public void onSignupRequested() { - loadSignupFragment(); - } - - @Override - public void onSignupCompleted() { - loadSignedInFragment(); - updateLoginMenu(); - } - public void handleUsernameChanged(String username) { runOnUiThread(() -> updateProfileHeader(username)); } @@ -418,7 +374,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On public void onBackPressed() { // if a fragment needs to internally manage back presses.. FragmentManager fm = getFragmentManager(); - Log.d("[BACK]", "getBackStackEntryCount " + fm.getBackStackEntryCount()); Fragment friendsFragment = fm.findFragmentByTag(getString(R.string.tagFragmentPeople)); if (friendsFragment != null && friendsFragment instanceof FriendsFragment) { if (((FriendsFragment) friendsFragment).onBackPressed()) { diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java index e0aa967aaa..bb42467ace 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/SplashActivity.java @@ -3,7 +3,6 @@ package io.highfidelity.hifiinterface; import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.os.Handler; import android.view.View; public class SplashActivity extends Activity { @@ -37,7 +36,13 @@ public class SplashActivity extends Activity { } public void onAppLoadedComplete() { - startActivity(new Intent(this, MainActivity.class)); + if (HifiUtils.getInstance().isUserLoggedIn()) { + startActivity(new Intent(this, MainActivity.class)); + } else { + Intent menuIntent = new Intent(this, LoginMenuActivity.class); + menuIntent.putExtra(LoginMenuActivity.EXTRA_FINISH_ON_BACK, true); + startActivity(menuIntent); + } SplashActivity.this.finish(); } } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index 2475c4d887..e19a9c5a7a 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -23,8 +23,6 @@ import io.highfidelity.hifiinterface.view.UserListAdapter; public class FriendsFragment extends Fragment { - public native boolean nativeIsLoggedIn(); - public native String nativeGetAccessToken(); private RecyclerView mUsersView; diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java index f22e5cd6bb..81493b1f65 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/LoginFragment.java @@ -1,13 +1,11 @@ package io.highfidelity.hifiinterface.fragment; -import android.app.Activity; import android.app.Fragment; -import android.app.ProgressDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -29,14 +27,13 @@ public class LoginFragment extends Fragment { private EditText mUsername; private EditText mPassword; private TextView mError; - private TextView mForgotPassword; - private TextView mSignup; private Button mLoginButton; + private ViewGroup mLoginForm; + private ViewGroup mLoggingInFrame; + private ViewGroup mLoggedInFrame; - private ProgressDialog mDialog; - - public native void nativeLogin(String username, String password, Activity usernameChangedListener); - public native void nativeCancelLogin(); + public native void login(String username, String password, Fragment usernameChangedListener); + public native void cancelLogin(); private LoginFragment.OnLoginInteractionListener mListener; @@ -58,22 +55,21 @@ public class LoginFragment extends Fragment { mPassword = rootView.findViewById(R.id.password); mError = rootView.findViewById(R.id.error); mLoginButton = rootView.findViewById(R.id.loginButton); - mForgotPassword = rootView.findViewById(R.id.forgotPassword); - mSignup = rootView.findViewById(R.id.signupButton); + mLoginForm = rootView.findViewById(R.id.loginForm); + mLoggingInFrame = rootView.findViewById(R.id.loggingInFrame); + mLoggedInFrame = rootView.findViewById(R.id.loggedInFrame); - mLoginButton.setOnClickListener(view -> login()); + rootView.findViewById(R.id.forgotPassword).setOnClickListener(view -> onForgotPasswordClicked()); - mForgotPassword.setOnClickListener(view -> forgotPassword()); - mSignup.setOnClickListener(view -> signup()); + rootView.findViewById(R.id.cancel).setOnClickListener(view -> onCancelLogin()); + + rootView.findViewById(R.id.getStarted).setOnClickListener(view -> onGetStartedClicked()); + + mLoginButton.setOnClickListener(view -> onLoginButtonClicked()); + + rootView.findViewById(R.id.takeMeInWorld).setOnClickListener(view -> skipLogin()); + mPassword.setOnEditorActionListener((textView, actionId, keyEvent) -> onPasswordEditorAction(textView, actionId, keyEvent)); - mPassword.setOnEditorActionListener( - (textView, actionId, keyEvent) -> { - if (actionId == EditorInfo.IME_ACTION_DONE) { - mLoginButton.performClick(); - return true; - } - return false; - }); return rootView; } @@ -104,13 +100,39 @@ public class LoginFragment extends Fragment { @Override public void onStop() { super.onStop(); - cancelActivityIndicator(); + showLoginForm(); // Leave the Qt app paused QtNative.setApplicationState(ApplicationInactive); hideKeyboard(); } - public void login() { + private void onCancelLogin() { + if (mListener != null) { + mListener.onCancelLogin(); + } + } + + private boolean onPasswordEditorAction(TextView textView, int actionId, KeyEvent keyEvent) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + mLoginButton.performClick(); + return true; + } + return false; + } + + private void skipLogin() { + if (mListener != null) { + mListener.onSkipLoginClicked(); + } + } + + private void onGetStartedClicked() { + if (mListener != null) { + mListener.onLoginCompleted(); + } + } + + public void onLoginButtonClicked() { String username = mUsername.getText().toString().trim(); String password = mPassword.getText().toString(); hideKeyboard(); @@ -120,13 +142,7 @@ public class LoginFragment extends Fragment { mLoginButton.setEnabled(false); hideError(); showActivityIndicator(); - nativeLogin(username, password, getActivity()); - } - } - - public void signup() { - if (mListener != null) { - mListener.onSignupRequested(); + login(username, password, this); } } @@ -138,33 +154,29 @@ public class LoginFragment extends Fragment { } } - private void forgotPassword() { + private void onForgotPasswordClicked() { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://highfidelity.com/users/password/new")); startActivity(intent); } private void showActivityIndicator() { - if (mDialog == null) { - mDialog = new ProgressDialog(getContext()); - } - mDialog.setMessage(getString(R.string.logging_in)); - mDialog.setCancelable(true); - mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialogInterface) { - nativeCancelLogin(); - cancelActivityIndicator(); - mLoginButton.setEnabled(true); - } - }); - mDialog.show(); + mLoginForm.setVisibility(View.GONE); + mLoggedInFrame.setVisibility(View.GONE); + mLoggingInFrame.setVisibility(View.VISIBLE); } - private void cancelActivityIndicator() { - if (mDialog != null) { - mDialog.cancel(); - } + private void showLoginForm() { + mLoggingInFrame.setVisibility(View.GONE); + mLoggedInFrame.setVisibility(View.GONE); + mLoginForm.setVisibility(View.VISIBLE); } + + private void showLoggedInMessage() { + mLoginForm.setVisibility(View.GONE); + mLoggingInFrame.setVisibility(View.GONE); + mLoggedInFrame.setVisibility(View.VISIBLE); + } + private void showError(String error) { mError.setText(error); mError.setVisibility(View.VISIBLE); @@ -178,12 +190,10 @@ public class LoginFragment extends Fragment { public void handleLoginCompleted(boolean success) { getActivity().runOnUiThread(() -> { mLoginButton.setEnabled(true); - cancelActivityIndicator(); if (success) { - if (mListener != null) { - mListener.onLoginCompleted(); - } + showLoggedInMessage(); } else { + showLoginForm(); showError(getString(R.string.login_username_or_password_incorrect)); } }); @@ -191,7 +201,8 @@ public class LoginFragment extends Fragment { public interface OnLoginInteractionListener { void onLoginCompleted(); - void onSignupRequested(); + void onCancelLogin(); + void onSkipLoginClicked(); } } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SignedInFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SignedInFragment.java deleted file mode 100644 index 9ed2f1c7f5..0000000000 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SignedInFragment.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.highfidelity.hifiinterface.fragment; - -import android.app.Fragment; -import android.content.Context; -import android.os.Bundle; -import android.text.Html; -import android.text.Spanned; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - -import java.io.IOException; -import java.io.InputStream; - -import io.highfidelity.hifiinterface.R; - -public class SignedInFragment extends Fragment { - - private Button mGetStartedButton; - private OnSignedInInteractionListener mListener; - - public SignedInFragment() { - // Required empty public constructor - } - - public static SignedInFragment newInstance() { - SignedInFragment fragment = new SignedInFragment(); - return fragment; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.fragment_signedin, container, false); - mGetStartedButton = rootView.findViewById(R.id.getStarted); - - mGetStartedButton.setOnClickListener(view -> { - getStarted(); - }); - - return rootView; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof SignedInFragment.OnSignedInInteractionListener) { - mListener = (SignedInFragment.OnSignedInInteractionListener) context; - } else { - throw new RuntimeException(context.toString() - + " must implement OnSignedInInteractionListener"); - } - } - - @Override - public void onDetach() { - super.onDetach(); - mListener = null; - } - - public void getStarted() { - if (mListener != null) { - mListener.onGettingStarted(); - } - } - - public interface OnSignedInInteractionListener { - void onGettingStarted(); - } - -} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SignupFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SignupFragment.java index 33644e5bda..e0297bb9ab 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SignupFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/SignupFragment.java @@ -1,10 +1,9 @@ package io.highfidelity.hifiinterface.fragment; -import android.app.Activity; import android.app.Fragment; -import android.app.ProgressDialog; import android.content.Context; import android.os.Bundle; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -27,16 +26,17 @@ public class SignupFragment extends Fragment { private EditText mUsername; private EditText mPassword; private TextView mError; - private TextView mCancelButton; - + private TextView mActivityText; private Button mSignupButton; - private ProgressDialog mDialog; + private ViewGroup mSignupForm; + private ViewGroup mLoggingInFrame; + private ViewGroup mLoggedInFrame; - public native void nativeSignup(String email, String username, String password); // move to SignupFragment - public native void nativeCancelSignup(); - public native void nativeLogin(String username, String password, Activity usernameChangedListener); - public native void nativeCancelLogin(); + public native void signup(String email, String username, String password); // move to SignupFragment + public native void cancelSignup(); + public native void login(String username, String password); + public native void cancelLogin(); private SignupFragment.OnSignupInteractionListener mListener; @@ -59,18 +59,20 @@ public class SignupFragment extends Fragment { mPassword = rootView.findViewById(R.id.password); mError = rootView.findViewById(R.id.error); mSignupButton = rootView.findViewById(R.id.signupButton); - mCancelButton = rootView.findViewById(R.id.cancelButton); + mActivityText = rootView.findViewById(R.id.activityText); + + mSignupForm = rootView.findViewById(R.id.signupForm); + mLoggedInFrame = rootView.findViewById(R.id.loggedInFrame); + mLoggingInFrame = rootView.findViewById(R.id.loggingInFrame); + + rootView.findViewById(R.id.cancel).setOnClickListener(view -> onCancelSignup()); mSignupButton.setOnClickListener(view -> signup()); - mCancelButton.setOnClickListener(view -> login()); - mPassword.setOnEditorActionListener( - (textView, actionId, keyEvent) -> { - if (actionId == EditorInfo.IME_ACTION_DONE) { - mSignupButton.performClick(); - return true; - } - return false; - }); + + rootView.findViewById(R.id.getStarted).setOnClickListener(view -> onGetStartedPressed()); + + mPassword.setOnEditorActionListener((textView, actionId, keyEvent) -> onPasswordEditorAction(textView, actionId, keyEvent)); + return rootView; } @@ -101,15 +103,23 @@ public class SignupFragment extends Fragment { @Override public void onStop() { super.onStop(); - cancelActivityIndicator(); + showSignupForm(); // Leave the Qt app paused QtNative.setApplicationState(ApplicationInactive); hideKeyboard(); } - private void login() { + private boolean onPasswordEditorAction(TextView textView, int actionId, KeyEvent keyEvent) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + mSignupButton.performClick(); + return true; + } + return false; + } + + private void onCancelSignup() { if (mListener != null) { - mListener.onLoginRequested(); + mListener.onCancelSignup(); } } @@ -123,8 +133,9 @@ public class SignupFragment extends Fragment { } else { mSignupButton.setEnabled(false); hideError(); + mActivityText.setText(R.string.creating_account); showActivityIndicator(); - nativeSignup(email, username, password); + signup(email, username, password); } } @@ -137,23 +148,21 @@ public class SignupFragment extends Fragment { } private void showActivityIndicator() { - if (mDialog == null) { - mDialog = new ProgressDialog(getContext()); - } - mDialog.setMessage(getString(R.string.creating_account)); - mDialog.setCancelable(true); - mDialog.setOnCancelListener(dialogInterface -> { - nativeCancelSignup(); - cancelActivityIndicator(); - mSignupButton.setEnabled(true); - }); - mDialog.show(); + mSignupForm.setVisibility(View.GONE); + mLoggedInFrame.setVisibility(View.GONE); + mLoggingInFrame.setVisibility(View.VISIBLE); } - private void cancelActivityIndicator() { - if (mDialog != null) { - mDialog.cancel(); - } + private void showLoggedInMessage() { + mSignupForm.setVisibility(View.GONE); + mLoggingInFrame.setVisibility(View.GONE); + mLoggedInFrame.setVisibility(View.VISIBLE); + } + + private void showSignupForm() { + mLoggingInFrame.setVisibility(View.GONE); + mLoggedInFrame.setVisibility(View.GONE); + mSignupForm.setVisibility(View.VISIBLE); } private void showError(String error) { mError.setText(error); @@ -167,29 +176,30 @@ public class SignupFragment extends Fragment { public interface OnSignupInteractionListener { void onSignupCompleted(); - void onLoginRequested(); + void onCancelSignup(); } + private void onGetStartedPressed() { + if (mListener != null) { + mListener.onSignupCompleted(); + } + } + + public void handleSignupCompleted() { String username = mUsername.getText().toString().trim(); String password = mPassword.getText().toString(); - mDialog.setMessage(getString(R.string.logging_in)); - mDialog.setCancelable(true); - mDialog.setOnCancelListener(dialogInterface -> { - nativeCancelLogin(); - cancelActivityIndicator(); - if (mListener != null) { - mListener.onLoginRequested(); - } + getActivity().runOnUiThread(() -> { + mActivityText.setText(R.string.logging_in); }); - mDialog.show(); - nativeLogin(username, password, getActivity()); + + login(username, password); } public void handleSignupFailed(String error) { getActivity().runOnUiThread(() -> { mSignupButton.setEnabled(true); - cancelActivityIndicator(); + showSignupForm(); mError.setText(error); mError.setVisibility(View.VISIBLE); }); @@ -198,20 +208,15 @@ public class SignupFragment extends Fragment { public void handleLoginCompleted(boolean success) { getActivity().runOnUiThread(() -> { mSignupButton.setEnabled(true); - cancelActivityIndicator(); - if (success) { - if (mListener != null) { - mListener.onSignupCompleted(); - } + showLoggedInMessage(); } else { // Registration was successful but login failed. // Let the user to login manually - mListener.onLoginRequested(); + mListener.onCancelSignup(); + showSignupForm(); } }); } - - } diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java new file mode 100644 index 0000000000..22614fa300 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/StartMenuFragment.java @@ -0,0 +1,85 @@ +package io.highfidelity.hifiinterface.fragment; + +import android.content.Context; +import android.os.Bundle; +import android.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import io.highfidelity.hifiinterface.R; + +public class StartMenuFragment extends Fragment { + + private StartMenuInteractionListener mListener; + + public StartMenuFragment() { + // Required empty public constructor + } + + public static StartMenuFragment newInstance() { + StartMenuFragment fragment = new StartMenuFragment(); + return fragment; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + View rootView = inflater.inflate(R.layout.fragment_login_menu, container, false); + rootView.findViewById(R.id.signupButton).setOnClickListener(view -> { + if (mListener != null) { + mListener.onSignupButtonClicked(); + } + }); + + rootView.findViewById(R.id.loginButton).setOnClickListener(view -> { + if (mListener != null) { + mListener.onLoginButtonClicked(); + } + }); + + rootView.findViewById(R.id.takeMeInWorld).setOnClickListener(view -> { + if (mListener != null) { + mListener.onSkipLoginClicked(); + } + }); + + + + return rootView; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof StartMenuInteractionListener) { + mListener = (StartMenuInteractionListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement StartMenuInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + /** + * This interface must be implemented by activities that contain this + * fragment to allow an interaction in this fragment to be communicated + * to the activity and potentially other fragments contained in that + * activity. + *

+ * See the Android Training lesson Communicating with Other Fragments for more information. + */ + public interface StartMenuInteractionListener { + void onSignupButtonClicked(); + void onLoginButtonClicked(); + void onSkipLoginClicked(); + } +} diff --git a/android/app/src/main/res/drawable/encourage_login_background.jpg b/android/app/src/main/res/drawable/encourage_login_background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5382880c3ee52920bddcce22dfd72d271dbf5c2d GIT binary patch literal 49513 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%0A*U_dU(PQr$WN`; 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 diff --git a/android/app/src/main/res/drawable/ic_right_arrow.xml b/android/app/src/main/res/drawable/ic_right_arrow.xml new file mode 100644 index 0000000000..e35d1a2733 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_right_arrow.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/rounded_button.xml b/android/app/src/main/res/drawable/rounded_button_color1.xml similarity index 100% rename from android/app/src/main/res/drawable/rounded_button.xml rename to android/app/src/main/res/drawable/rounded_button_color1.xml diff --git a/android/app/src/main/res/drawable/rounded_button_color3.xml b/android/app/src/main/res/drawable/rounded_button_color3.xml new file mode 100644 index 0000000000..6230885b30 --- /dev/null +++ b/android/app/src/main/res/drawable/rounded_button_color3.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/rounded_edit.xml b/android/app/src/main/res/drawable/rounded_edit.xml deleted file mode 100644 index 3c1cac4d1d..0000000000 --- a/android/app/src/main/res/drawable/rounded_edit.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android/app/src/main/res/layout/activity_encourage_login.xml b/android/app/src/main/res/layout/activity_encourage_login.xml new file mode 100644 index 0000000000..d7c9ff6b4d --- /dev/null +++ b/android/app/src/main/res/layout/activity_encourage_login.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/fragment_login.xml b/android/app/src/main/res/layout/fragment_login.xml index 6933ad1eb5..2f48424ae8 100644 --- a/android/app/src/main/res/layout/fragment_login.xml +++ b/android/app/src/main/res/layout/fragment_login.xml @@ -6,6 +6,17 @@ android:layout_height="match_parent" android:background="@color/backgroundLight"> + + + + - - - - - - - - - -