From 611779b58b0f92461fc97a1ed78c9228238a0ecb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 8 Jul 2016 16:25:50 -0700 Subject: [PATCH 001/279] Improve gamma correction implementation in cube map processing --- libraries/gpu/src/gpu/Texture.cpp | 9 +++------ libraries/shared/src/ColorUtils.h | 7 +++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index f9a8f3b26b..bd0ad0ce7b 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -788,12 +788,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< uint pixOffsetIndex = (x + y * width) * numComponents; // get color from texture and map to range [0, 1] - glm::vec3 clr(float(data[pixOffsetIndex]) * UCHAR_TO_FLOAT, - float(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT, - float(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT); - - // Gamma correct - clr = ColorUtils::sRGBToLinearVec3(clr); + glm::vec3 clr(ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex]) * UCHAR_TO_FLOAT, + ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT, + ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT); // scale color and add to previously accumulated coefficients sphericalHarmonicsScale(shBuffB.data(), order, diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index 5ee9254bc9..42d36ebd4b 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -17,6 +17,9 @@ #include "DependencyManager.h" +static const float srgbToLinearLookupTable[256] = + { 0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f, 0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.00251584218443f, 0.00279619194822f, 0.00309396642819f, 0.00340946205345f, 0.00374296799396f, 0.00409476661624f, 0.00446513389425f, 0.00485433978143f, 0.00526264854875f, 0.00569031909303f, 0.00613760521883f, 0.00660475589722f, 0.00709201550367f, 0.00759962403765f, 0.00812781732551f, 0.00867682720861f, 0.00924688171802f, 0.00983820523704f, 0.0104510186528f, 0.0110855394981f, 0.0117419820834f, 0.0124205576216f, 0.0131214743443f, 0.0138449376117f, 0.0145911500156f, 0.0153603114768f, 0.0161526193372f, 0.0169682684465f, 0.0178074512441f, 0.0186703578377f, 0.0195571760767f, 0.0204680916222f, 0.0214032880141f, 0.0223629467344f, 0.0233472472675f, 0.0243563671578f, 0.0253904820647f, 0.026449765815f, 0.0275343904531f, 0.0286445262888f, 0.0297803419432f, 0.0309420043928f, 0.0321296790111f, 0.0333435296099f, 0.0345837184768f, 0.0358504064137f, 0.0371437527716f, 0.0384639154854f, 0.0398110511069f, 0.0411853148367f, 0.0425868605546f, 0.0440158408496f, 0.045472407048f, 0.046956709241f, 0.0484688963113f, 0.0500091159586f, 0.0515775147244f, 0.0531742380159f, 0.0547994301291f, 0.0564532342711f, 0.058135792582f, 0.0598472461555f, 0.0615877350593f, 0.063357398355f, 0.0651563741167f, 0.0669847994499f, 0.0688428105093f, 0.0707305425158f, 0.0726481297741f, 0.0745957056885f, 0.0765734027789f, 0.0785813526965f, 0.0806196862387f, 0.0826885333636f, 0.0847880232044f, 0.086918284083f, 0.0890794435236f, 0.0912716282659f, 0.0934949642776f, 0.0957495767668f, 0.0980355901944f, 0.100353128286f, 0.102702314041f, 0.10508326975f, 0.107496116997f, 0.109940976678f, 0.112417969007f, 0.114927213525f, 0.117468829116f, 0.120042934009f, 0.122649645793f, 0.125289081424f, 0.127961357236f, 0.130666588944f, 0.13340489166f, 0.136176379898f, 0.138981167581f, 0.141819368051f, 0.144691094076f, 0.147596457859f, 0.150535571041f, 0.153508544716f, 0.156515489432f, 0.1595565152f, 0.1626317315f, 0.16574124729f, 0.168885171012f, 0.172063610595f, 0.175276673468f, 0.178524466557f, 0.181807096302f, 0.185124668654f, 0.188477289086f, 0.191865062595f, 0.195288093712f, 0.198746486503f, 0.202240344578f, 0.205769771096f, 0.209334868766f, 0.212935739858f, 0.216572486205f, 0.220245209207f, 0.223954009837f, 0.227698988648f, 0.231480245773f, 0.235297880934f, 0.239151993444f, 0.243042682212f, 0.246970045747f, 0.250934182163f, 0.254935189183f, 0.258973164144f, 0.263048203998f, 0.267160405319f, 0.271309864307f, 0.27549667679f, 0.279720938228f, 0.283982743718f, 0.288282187998f, 0.292619365448f, 0.296994370096f, 0.30140729562f, 0.305858235354f, 0.310347282289f, 0.314874529074f, 0.319440068025f, 0.324043991126f, 0.32868639003f, 0.333367356062f, 0.338086980228f, 0.34284535321f, 0.347642565374f, 0.352478706774f, 0.357353867148f, 0.36226813593f, 0.367221602246f, 0.372214354918f, 0.37724648247f, 0.382318073128f, 0.387429214822f, 0.392579995191f, 0.397770501584f, 0.403000821062f, 0.408271040402f, 0.413581246099f, 0.418931524369f, 0.424321961148f, 0.4297526421f, 0.435223652615f, 0.440735077813f, 0.446287002544f, 0.451879511396f, 0.45751268869f, 0.463186618488f, 0.46890138459f, 0.474657070542f, 0.480453759632f, 0.486291534897f, 0.492170479122f, 0.498090674843f, 0.50405220435f, 0.510055149687f, 0.516099592656f, 0.522185614816f, 0.528313297489f, 0.534482721758f, 0.54069396847f, 0.546947118241f, 0.553242251452f, 0.559579448254f, 0.565958788573f, 0.572380352104f, 0.578844218319f, 0.585350466467f, 0.591899175574f, 0.598490424448f, 0.605124291677f, 0.611800855632f, 0.61852019447f, 0.625282386134f, 0.632087508355f, 0.638935638652f, 0.645826854338f, 0.652761232515f, 0.659738850081f, 0.66675978373f, 0.673824109951f, 0.680931905032f, 0.688083245062f, 0.695278205929f, 0.702516863324f, 0.709799292744f, 0.717125569488f, 0.724495768663f, 0.731909965185f, 0.739368233777f, 0.746870648974f, 0.754417285121f, 0.762008216379f, 0.76964351672f, 0.777323259932f, 0.785047519623f, 0.792816369214f, 0.800629881949f, 0.80848813089f, 0.816391188922f, 0.824339128751f, 0.832332022907f, 0.840369943747f, 0.848452963452f, 0.856581154031f, 0.864754587319f, 0.872973334984f, 0.881237468522f, 0.889547059261f, 0.897902178361f, 0.906302896816f, 0.914749285456f, 0.923241414944f, 0.931779355781f, 0.940363178305f, 0.948992952695f, 0.957668748966f, 0.966390636975f, 0.975158686423f }; + class ColorUtils { public: inline static glm::vec3 toVec3(const xColor& color); @@ -33,6 +36,7 @@ public: inline static glm::vec4 tosRGBVec4(const glm::vec4& srgb); inline static float sRGBToLinearFloat(const float& srgb); + inline static float sRGB8ToLinearFloat(const uint8_t srgb); inline static float tosRGBFloat(const float& linear); }; @@ -82,6 +86,9 @@ inline float ColorUtils::sRGBToLinearFloat(const float &srgb) { return linearValue; } +inline float ColorUtils::sRGB8ToLinearFloat(const uint8_t srgb) { + return srgbToLinearLookupTable[srgb]; +} // This is based upon the conversions found in section 17.3.9 of the OpenGL 4.4 specification. // glm::pow(color, 1.0f/2.2f) is approximate, and will cause subtle differences when used with sRGB framebuffers. From 676f4bdfcfdbbe0e2b1d11559d458dc98d11e86a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 29 Jul 2016 15:36:32 -0700 Subject: [PATCH 002/279] misc fixes from particles branch --- cmake/macros/AutoScribeShader.cmake | 16 +++++++++------- cmake/macros/SetupHifiLibrary.cmake | 3 ++- interface/src/ui/overlays/Overlays.h | 2 +- .../src/RenderableModelEntityItem.cpp | 19 ------------------- .../src/RenderableModelEntityItem.h | 1 - .../src/textured_particle.slv | 2 +- libraries/entities/src/EntityTreeElement.cpp | 4 ++-- libraries/gpu-gl/src/gpu/gl/GLShared.cpp | 2 +- libraries/gpu/src/gpu/Shader.h | 15 +++++++++------ libraries/render-utils/CMakeLists.txt | 4 ++-- libraries/render-utils/src/AnimDebugDraw.cpp | 2 ++ libraries/render-utils/src/GeometryCache.cpp | 2 +- libraries/render/CMakeLists.txt | 2 +- 13 files changed, 31 insertions(+), 43 deletions(-) diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index dfa59943d6..e586304503 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -23,13 +23,13 @@ function(AUTOSCRIBE_SHADER SHADER_FILE) #Extract the unique include shader paths set(INCLUDES ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}) - #message(Hifi for includes ${INCLUDES}) - foreach(EXTRA_SHADER_INCLUDE ${INCLUDES}) + #message(${TARGET_NAME} Hifi for includes ${INCLUDES}) + foreach(EXTRA_SHADER_INCLUDE ${INCLUDES}) list(APPEND SHADER_INCLUDES_PATHS ${EXTRA_SHADER_INCLUDE}) endforeach() list(REMOVE_DUPLICATES SHADER_INCLUDES_PATHS) - #message(ready for includes ${SHADER_INCLUDES_PATHS}) + #message(ready for includes ${SHADER_INCLUDES_PATHS}) # make the scribe include arguments set(SCRIBE_INCLUDES) @@ -77,6 +77,7 @@ endfunction() macro(AUTOSCRIBE_SHADER_LIB) + set(HIFI_LIBRARIES_SHADER_INCLUDE_FILES "") file(RELATIVE_PATH RELATIVE_LIBRARY_DIR_PATH ${CMAKE_CURRENT_SOURCE_DIR} "${HIFI_LIBRARY_DIR}") foreach(HIFI_LIBRARY ${ARGN}) #if (NOT TARGET ${HIFI_LIBRARY}) @@ -86,7 +87,7 @@ macro(AUTOSCRIBE_SHADER_LIB) #file(GLOB_RECURSE HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src/*.slh) list(APPEND HIFI_LIBRARIES_SHADER_INCLUDE_FILES ${HIFI_LIBRARY_DIR}/${HIFI_LIBRARY}/src) endforeach() - #message(${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}) + #message("${TARGET_NAME} ${HIFI_LIBRARIES_SHADER_INCLUDE_FILES}") file(GLOB_RECURSE SHADER_INCLUDE_FILES src/*.slh) file(GLOB_RECURSE SHADER_SOURCE_FILES src/*.slv src/*.slf src/*.slg) @@ -95,13 +96,14 @@ macro(AUTOSCRIBE_SHADER_LIB) set(SHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/shaders/${TARGET_NAME}") file(MAKE_DIRECTORY ${SHADERS_DIR}) - #message(${SHADER_INCLUDE_FILES}) + #message("${TARGET_NAME} ${SHADER_INCLUDE_FILES}") + set(AUTOSCRIBE_SHADER_SRC "") foreach(SHADER_FILE ${SHADER_SOURCE_FILES}) AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES}) file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE) list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE}) endforeach() - #message(${AUTOSCRIBE_SHADER_SRC}) + #message(${TARGET_NAME} ${AUTOSCRIBE_SHADER_SRC}) if (WIN32) source_group("Shaders" FILES ${SHADER_INCLUDE_FILES}) @@ -116,4 +118,4 @@ macro(AUTOSCRIBE_SHADER_LIB) # Link library shaders, if they exist include_directories("${SHADERS_DIR}") -endmacro() +endmacro() \ No newline at end of file diff --git a/cmake/macros/SetupHifiLibrary.cmake b/cmake/macros/SetupHifiLibrary.cmake index 26c769c6e6..a10c7c11e6 100644 --- a/cmake/macros/SetupHifiLibrary.cmake +++ b/cmake/macros/SetupHifiLibrary.cmake @@ -54,8 +54,9 @@ macro(SETUP_HIFI_LIBRARY) target_link_libraries(${TARGET_NAME} Qt5::${QT_MODULE}) endforeach() - # Don't make scribed shaders cumulative + # Don't make scribed shaders or QT resource files cumulative set(AUTOSCRIBE_SHADER_LIB_SRC "") + set(QT_RESOURCES_FILE "") target_glm() diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 99f74fa0f9..e19a6b36a9 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -93,7 +93,7 @@ public slots: /// successful edit, if the input id is for an unknown overlay this function will have no effect bool editOverlays(const QVariant& propertiesById); - /// deletes a particle + /// deletes an overlay void deleteOverlay(unsigned int id); /// get the string type of the overlay used in addOverlay diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 4e8ecf3054..b0207358d2 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -176,25 +176,6 @@ void RenderableModelEntityItem::doInitialModelSimulation() { _needsInitialSimulation = false; } - -// TODO: we need a solution for changes to the postion/rotation/etc of a model... -// this current code path only addresses that in this setup case... not the changing/moving case -bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) { - if (!_model && renderArgs) { - // TODO: this getModel() appears to be about 3% of model render time. We should optimize - PerformanceTimer perfTimer("getModel"); - EntityTreeRenderer* renderer = static_cast(renderArgs->_renderer); - getModel(renderer); - } - if (renderArgs && _model && _needsInitialSimulation && _model->isActive() && _model->isLoaded()) { - // make sure to simulate so everything gets set up correctly for rendering - doInitialModelSimulation(); - _model->renderSetup(renderArgs); - } - bool ready = !_needsInitialSimulation && _model && _model->readyToAddToScene(renderArgs); - return ready; -} - class RenderableModelEntityItemMeta { public: RenderableModelEntityItemMeta(EntityItemPointer entity) : entity(entity){ } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 339c907532..cced8df6ab 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -40,7 +40,6 @@ public: void doInitialModelSimulation(); - virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr); virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override; virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override; diff --git a/libraries/entities-renderer/src/textured_particle.slv b/libraries/entities-renderer/src/textured_particle.slv index 79f75187c5..cab76227c4 100644 --- a/libraries/entities-renderer/src/textured_particle.slv +++ b/libraries/entities-renderer/src/textured_particle.slv @@ -44,7 +44,7 @@ out vec4 varColor; out vec2 varTexcoord; const int NUM_VERTICES_PER_PARTICLE = 4; -// This ordering ensures that un-rotated particles render upright in the wiewer. +// This ordering ensures that un-rotated particles render upright in the viewer. const vec4 UNIT_QUAD[NUM_VERTICES_PER_PARTICLE] = vec4[NUM_VERTICES_PER_PARTICLE]( vec4(-1.0, 1.0, 0.0, 0.0), vec4(-1.0, -1.0, 0.0, 0.0), diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 0523933ee6..657e0b286b 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -634,8 +634,8 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con } } else { // if the entity type doesn't support a detailed intersection, then just return the non-AABox results - // Never intersect with particle effect entities - if (localDistance < distance && EntityTypes::getEntityTypeName(entity->getType()) != "ParticleEffect") { + // Never intersect with particle entities + if (localDistance < distance && entity->getType() != EntityTypes::ParticleEffect) { distance = localDistance; face = localFace; surfaceNormal = localSurfaceNormal; diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp index 8f234ca6b4..35cf9b83ba 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.cpp @@ -596,7 +596,7 @@ int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindin } Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); - buffers.insert(Shader::Slot(name, binding, element, Resource::BUFFER)); + buffers.insert(Shader::Slot(name, binding, element, Resource::BUFFER, size)); } return buffersCount; } diff --git a/libraries/gpu/src/gpu/Shader.h b/libraries/gpu/src/gpu/Shader.h index 9072bf23a9..a741eafd40 100755 --- a/libraries/gpu/src/gpu/Shader.h +++ b/libraries/gpu/src/gpu/Shader.h @@ -53,19 +53,22 @@ public: int32 _location{INVALID_LOCATION}; Element _element; uint16 _resourceType{Resource::BUFFER}; + uint32 _size { 0 }; - Slot(const Slot& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType) {} - Slot(Slot&& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType) {} - Slot(const std::string& name, int32 location, const Element& element, uint16 resourceType = Resource::BUFFER) : - _name(name), _location(location), _element(element), _resourceType(resourceType) {} + Slot(const Slot& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {} + Slot(Slot&& s) : _name(s._name), _location(s._location), _element(s._element), _resourceType(s._resourceType), _size(s._size) {} + Slot(const std::string& name, int32 location, const Element& element, uint16 resourceType = Resource::BUFFER, uint32 size = 0) : + _name(name), _location(location), _element(element), _resourceType(resourceType), _size(size) {} Slot(const std::string& name) : _name(name) {} - + Slot& operator= (const Slot& s) { _name = s._name; _location = s._location; _element = s._element; _resourceType = s._resourceType; - return (*this); } + _size = s._size; + return (*this); + } }; class Binding { diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 2a7d33e33a..7b272f7b7d 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -1,9 +1,9 @@ set(TARGET_NAME render-utils) -AUTOSCRIBE_SHADER_LIB(gpu model render procedural) +AUTOSCRIBE_SHADER_LIB(gpu model render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Widgets OpenGL Network Qml Quick Script) -link_hifi_libraries(shared gpu procedural model model-networking render animation fbx) +link_hifi_libraries(shared gpu model model-networking render animation fbx) target_nsight() target_oglplus() diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 11c43eaee4..f1443f7e4d 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -15,6 +15,8 @@ #include "GLMHelpers.h" #include "DebugDraw.h" +#include + #include "AnimDebugDraw.h" struct Vertex { diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index cebd8ad37f..bead7549db 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -11,7 +11,7 @@ #include "GeometryCache.h" - +#include #include #include diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index c5cfdf3668..76fc8303ce 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME render) -AUTOSCRIBE_SHADER_LIB(gpu model procedural) +AUTOSCRIBE_SHADER_LIB(gpu model) setup_hifi_library() link_hifi_libraries(shared gpu model) From e4e86c245e8291640d6261e3df821478f19d375c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 31 Jul 2016 14:11:56 -0700 Subject: [PATCH 003/279] 3d overlays can be children of entities or avatars --- interface/src/ui/overlays/Base3DOverlay.cpp | 117 ++++++++++++++---- interface/src/ui/overlays/Base3DOverlay.h | 16 +-- interface/src/ui/overlays/Circle3DOverlay.cpp | 6 +- interface/src/ui/overlays/Image3DOverlay.cpp | 15 ++- interface/src/ui/overlays/Line3DOverlay.cpp | 6 +- interface/src/ui/overlays/Overlay.cpp | 4 +- interface/src/ui/overlays/Planar3DOverlay.cpp | 6 +- interface/src/ui/overlays/Sphere3DOverlay.cpp | 2 +- interface/src/ui/overlays/Text3DOverlay.cpp | 31 +++-- interface/src/ui/overlays/Volume3DOverlay.cpp | 2 +- interface/src/ui/overlays/Web3DOverlay.cpp | 16 ++- libraries/entities/src/EntityItem.h | 4 - libraries/shared/src/SpatiallyNestable.cpp | 30 ++++- libraries/shared/src/SpatiallyNestable.h | 18 ++- 14 files changed, 193 insertions(+), 80 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 94533137ad..dc97af5300 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -21,6 +21,7 @@ const bool DEFAULT_IS_SOLID = false; const bool DEFAULT_IS_DASHED_LINE = false; Base3DOverlay::Base3DOverlay() : + SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()), _lineWidth(DEFAULT_LINE_WIDTH), _isSolid(DEFAULT_IS_SOLID), _isDashedLine(DEFAULT_IS_DASHED_LINE), @@ -31,15 +32,68 @@ Base3DOverlay::Base3DOverlay() : Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : Overlay(base3DOverlay), - _transform(base3DOverlay->_transform), + SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()), _lineWidth(base3DOverlay->_lineWidth), _isSolid(base3DOverlay->_isSolid), _isDashedLine(base3DOverlay->_isDashedLine), _ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection), _drawInFront(base3DOverlay->_drawInFront) { + setTransform(base3DOverlay->getTransform()); } -void Base3DOverlay::setProperties(const QVariantMap& properties) { + +QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties, + const QUuid& currentParentID, + int currentParentJointIndex) { + // the position and rotation in _transform are relative to the parent (aka local). The versions coming from + // scripts are in world-frame, unless localPosition or localRotation are used. Patch up the properties + // so that "position" and "rotation" are relative-to-parent values. + QVariantMap result = properties; + QUuid parentID = result["parentID"].isValid() ? QUuid(result["parentID"].toString()) : currentParentID; + int parentJointIndex = + result["parentJointIndex"].isValid() ? result["parentJointIndex"].toInt() : currentParentJointIndex; + bool success; + + // carry over some legacy keys + if (!result["position"].isValid() && !result["localPosition"].isValid()) { + if (result["p1"].isValid()) { + result["position"] = result["p1"]; + } else if (result["point"].isValid()) { + result["position"] = result["point"]; + } else if (result["start"].isValid()) { + result["position"] = result["start"]; + } + } + if (!result["orientation"].isValid() && result["rotation"].isValid()) { + result["orientation"] = result["rotation"]; + } + if (!result["localOrientation"].isValid() && result["localRotation"].isValid()) { + result["localOrientation"] = result["localRotation"]; + } + + // make "position" and "orientation" be relative-to-parent + if (result["localPosition"].isValid()) { + result["position"] = result["localPosition"]; + } else if (result["position"].isValid()) { + glm::vec3 localPosition = SpatiallyNestable::worldToLocal(vec3FromVariant(result["position"]), + parentID, parentJointIndex, success); + result["position"] = vec3toVariant(localPosition); + } + + if (result["localOrientation"].isValid()) { + result["orientation"] = result["localOrientation"]; + } else if (result["orientation"].isValid()) { + glm::quat localOrientation = SpatiallyNestable::worldToLocal(quatFromVariant(result["orientation"]), + parentID, parentJointIndex, success); + result["orientation"] = quatToVariant(localOrientation); + } + + return result; +} + +void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { + QVariantMap properties = + convertOverlayLocationFromScriptSemantics(originalProperties, getParentID(), getParentJointIndex()); Overlay::setProperties(properties); bool needRenderItemUpdate = false; @@ -52,17 +106,12 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) { needRenderItemUpdate = true; } - auto position = properties["position"]; - - // if "position" property was not there, check to see if they included aliases: point, p1 - if (!position.isValid()) { - position = properties["p1"]; - if (!position.isValid()) { - position = properties["point"]; - } + if (properties["position"].isValid()) { + setPosition(vec3FromVariant(properties["position"])); + needRenderItemUpdate = true; } - if (position.isValid()) { - setPosition(vec3FromVariant(position)); + if (properties["orientation"].isValid()) { + setOrientation(quatFromVariant(properties["orientation"])); needRenderItemUpdate = true; } @@ -71,13 +120,6 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) { needRenderItemUpdate = true; } - auto rotation = properties["rotation"]; - - if (rotation.isValid()) { - setRotation(quatFromVariant(rotation)); - needRenderItemUpdate = true; - } - if (properties["isSolid"].isValid()) { setIsSolid(properties["isSolid"].toBool()); } @@ -107,6 +149,13 @@ void Base3DOverlay::setProperties(const QVariantMap& properties) { setIgnoreRayIntersection(properties["ignoreRayIntersection"].toBool()); } + if (properties["parentID"].isValid()) { + setParentID(QUuid(properties["parentID"].toString())); + } + if (properties["parentJointIndex"].isValid()) { + setParentJointIndex(properties["parentJointIndex"].toInt()); + } + // Communicate changes to the renderItem if needed if (needRenderItemUpdate) { auto itemID = getRenderItemID(); @@ -123,12 +172,18 @@ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "position" || property == "start" || property == "p1" || property == "point") { return vec3toVariant(getPosition()); } + if (property == "localPosition") { + return vec3toVariant(getLocalPosition()); + } + if (property == "rotation" || property == "orientation") { + return quatToVariant(getOrientation()); + } + if (property == "localRotation" || property == "localOrientation") { + return quatToVariant(getLocalOrientation()); + } if (property == "lineWidth") { return _lineWidth; } - if (property == "rotation") { - return quatToVariant(getRotation()); - } if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filed") { return _isSolid; } @@ -144,6 +199,12 @@ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "drawInFront") { return _drawInFront; } + if (property == "parentID") { + return getParentID(); + } + if (property == "parentJointIndex") { + return getParentJointIndex(); + } return Overlay::getProperty(property); } @@ -152,3 +213,15 @@ bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3 float& distance, BoxFace& face, glm::vec3& surfaceNormal) { return false; } + +void Base3DOverlay::locationChanged(bool tellPhysics) { + auto itemID = getRenderItemID(); + if (render::Item::isValidID(itemID)) { + render::ScenePointer scene = qApp->getMain3DScene(); + render::PendingChanges pendingChanges; + pendingChanges.updateItem(itemID); + scene->enqueuePendingChanges(pendingChanges); + } + // Overlays can't currently have children. + // SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also +} diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index e602dec48c..551c1c3384 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -12,10 +12,11 @@ #define hifi_Base3DOverlay_h #include +#include #include "Overlay.h" -class Base3DOverlay : public Overlay { +class Base3DOverlay : public Overlay, public SpatiallyNestable { Q_OBJECT public: @@ -24,12 +25,9 @@ public: // getters virtual bool is3D() const override { return true; } - const glm::vec3& getPosition() const { return _transform.getTranslation(); } - const glm::quat& getRotation() const { return _transform.getRotation(); } - const glm::vec3& getScale() const { return _transform.getScale(); } // TODO: consider implementing registration points in this class - const glm::vec3& getCenter() const { return getPosition(); } + glm::vec3 getCenter() const { return getPosition(); } float getLineWidth() const { return _lineWidth; } bool getIsSolid() const { return _isSolid; } @@ -38,12 +36,6 @@ public: bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; } bool getDrawInFront() const { return _drawInFront; } - // setters - void setPosition(const glm::vec3& value) { _transform.setTranslation(value); } - void setRotation(const glm::quat& value) { _transform.setRotation(value); } - void setScale(float value) { _transform.setScale(value); } - void setScale(const glm::vec3& value) { _transform.setScale(value); } - void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } void setIsSolid(bool isSolid) { _isSolid = isSolid; } void setIsDashedLine(bool isDashedLine) { _isDashedLine = isDashedLine; } @@ -64,7 +56,7 @@ public: } protected: - Transform _transform; + virtual void locationChanged(bool tellPhysics = true) override; float _lineWidth; bool _isSolid; diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 6ebfd5c71c..e9ee997aac 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -69,17 +69,17 @@ void Circle3DOverlay::render(RenderArgs* args) { // FIXME: THe line width of _lineWidth is not supported anymore, we ll need a workaround - auto transform = _transform; + auto transform = getTransform(); transform.postScale(glm::vec3(getDimensions(), 1.0f)); batch.setModelTransform(transform); - + // for our overlay, is solid means we draw a ring between the inner and outer radius of the circle, otherwise // we just draw a line... if (getIsSolid()) { if (!_quadVerticesID) { _quadVerticesID = geometryCache->allocateID(); } - + if (geometryChanged) { QVector points; QVector colors; diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index d59e552779..d05eaac07c 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -37,7 +37,9 @@ Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) : } void Image3DOverlay::update(float deltatime) { - applyTransformTo(_transform); + Transform transform = getTransform(); + applyTransformTo(transform); + setTransform(transform); } void Image3DOverlay::render(RenderArgs* args) { @@ -86,13 +88,14 @@ void Image3DOverlay::render(RenderArgs* args) { xColor color = getColor(); float alpha = getAlpha(); - applyTransformTo(_transform, true); - Transform transform = _transform; + Transform transform = getTransform(); + applyTransformTo(transform, true); + setTransform(transform); transform.postScale(glm::vec3(getDimensions(), 1.0f)); batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); - + DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) @@ -187,7 +190,9 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec float& distance, BoxFace& face, glm::vec3& surfaceNormal) { if (_texture && _texture->isLoaded()) { // Make sure position and rotation is updated. - applyTransformTo(_transform, true); + Transform transform; + applyTransformTo(transform, true); + setTransform(transform); // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index c9a8b19f6a..85962d38b1 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -35,8 +35,8 @@ AABox Line3DOverlay::getBounds() const { auto extents = Extents{}; extents.addPoint(_start); extents.addPoint(_end); - extents.transform(_transform); - + extents.transform(getTransform()); + return AABox(extents); } @@ -52,7 +52,7 @@ void Line3DOverlay::render(RenderArgs* args) { auto batch = args->_batch; if (batch) { - batch->setModelTransform(_transform); + batch->setModelTransform(getTransform()); auto geometryCache = DependencyManager::get(); if (getIsDashedLine()) { diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 5d2c315a92..82b90d228c 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -77,7 +77,7 @@ void Overlay::setProperties(const QVariantMap& properties) { if (properties["pulsePeriod"].isValid()) { setPulsePeriod(properties["pulsePeriod"].toFloat()); } - + if (properties["alphaPulse"].isValid()) { setAlphaPulse(properties["alphaPulse"].toFloat()); } @@ -90,7 +90,7 @@ void Overlay::setProperties(const QVariantMap& properties) { bool visible = properties["visible"].toBool(); setVisible(visible); } - + if (properties["anchor"].isValid()) { QString property = properties["anchor"].toString(); if (property == "MyAvatar") { diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index c580464e16..58d72b100b 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -28,10 +28,10 @@ Planar3DOverlay::Planar3DOverlay(const Planar3DOverlay* planar3DOverlay) : AABox Planar3DOverlay::getBounds() const { auto halfDimensions = glm::vec3{_dimensions / 2.0f, 0.01f}; - + auto extents = Extents{-halfDimensions, halfDimensions}; - extents.transform(_transform); - + extents.transform(getTransform()); + return AABox(extents); } diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index bbdd886d11..b925265cde 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -39,7 +39,7 @@ void Sphere3DOverlay::render(RenderArgs* args) { auto batch = args->_batch; if (batch) { - Transform transform = _transform; + Transform transform = getTransform(); transform.postScale(getDimensions() * SPHERE_OVERLAY_SCALE); batch->setModelTransform(transform); diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 0ae1c306ba..1fbf6a9bbd 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -65,44 +65,47 @@ xColor Text3DOverlay::getBackgroundColor() { } void Text3DOverlay::update(float deltatime) { - applyTransformTo(_transform); + Transform transform = getTransform(); + applyTransformTo(transform); + setTransform(transform); } void Text3DOverlay::render(RenderArgs* args) { if (!_visible || !getParentVisible()) { return; // do nothing if we're not visible } - + Q_ASSERT(args->_batch); auto& batch = *args->_batch; - - applyTransformTo(_transform, true); - batch.setModelTransform(_transform); + + Transform transform = getTransform(); + applyTransformTo(transform, true); + setTransform(transform); + batch.setModelTransform(transform); const float MAX_COLOR = 255.0f; xColor backgroundColor = getBackgroundColor(); glm::vec4 quadColor(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, backgroundColor.blue / MAX_COLOR, getBackgroundAlpha()); - + glm::vec2 dimensions = getDimensions(); glm::vec2 halfDimensions = dimensions * 0.5f; - + const float SLIGHTLY_BEHIND = -0.001f; - + glm::vec3 topLeft(-halfDimensions.x, -halfDimensions.y, SLIGHTLY_BEHIND); glm::vec3 bottomRight(halfDimensions.x, halfDimensions.y, SLIGHTLY_BEHIND); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, quadColor); - + // Same font properties as textSize() float maxHeight = (float)_textRenderer->computeExtent("Xy").y * LINE_SCALE_RATIO; - + float scaleFactor = (maxHeight / FIXED_FONT_SCALING_RATIO) * _lineHeight; - + glm::vec2 clipMinimum(0.0f, 0.0f); glm::vec2 clipDimensions((dimensions.x - (_leftMargin + _rightMargin)) / scaleFactor, (dimensions.y - (_topMargin + _bottomMargin)) / scaleFactor); - Transform transform = _transform; transform.postTranslate(glm::vec3(-(halfDimensions.x - _leftMargin), halfDimensions.y - _topMargin, 0.001f)); transform.setScale(scaleFactor); @@ -222,6 +225,8 @@ QSizeF Text3DOverlay::textSize(const QString& text) const { bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance, BoxFace &face, glm::vec3& surfaceNormal) { - applyTransformTo(_transform, true); + Transform transform = getTransform(); + applyTransformTo(transform, true); + setTransform(transform); return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face, surfaceNormal); } diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index 563198c976..ad61e28bc7 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -56,7 +56,7 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve float& distance, BoxFace& face, glm::vec3& surfaceNormal) { // extents is the entity relative, scaled, centered extents of the entity glm::mat4 worldToEntityMatrix; - _transform.getInverseMatrix(worldToEntityMatrix); + getTransform().getInverseMatrix(worldToEntityMatrix); glm::vec3 overlayFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); glm::vec3 overlayFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)); diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 1c84e71fa7..1b9adbfa95 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -47,7 +47,7 @@ Web3DOverlay::~Web3DOverlay() { _webSurface->disconnect(_connection); // The lifetime of the QML surface MUST be managed by the main thread // Additionally, we MUST use local variables copied by value, rather than - // member variables, since they would implicitly refer to a this that + // member variables, since they would implicitly refer to a this that // is no longer valid auto webSurface = _webSurface; AbstractViewStateInterface::instance()->postLambdaEvent([webSurface] { @@ -57,7 +57,9 @@ Web3DOverlay::~Web3DOverlay() { } void Web3DOverlay::update(float deltatime) { - applyTransformTo(_transform); + Transform transform = getTransform(); + applyTransformTo(transform); + setTransform(transform); } void Web3DOverlay::render(RenderArgs* args) { @@ -85,8 +87,9 @@ void Web3DOverlay::render(RenderArgs* args) { vec2 halfSize = size / 2.0f; vec4 color(toGlm(getColor()), getAlpha()); - applyTransformTo(_transform, true); - Transform transform = _transform; + Transform transform = getTransform(); + applyTransformTo(transform, true); + setTransform(transform); if (glm::length2(getDimensions()) != 1.0f) { transform.postScale(vec3(getDimensions(), 1.0f)); } @@ -165,7 +168,10 @@ bool Web3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& // FIXME - face and surfaceNormal not being returned // Make sure position and rotation is updated. - applyTransformTo(_transform, true); + Transform transform; + applyTransformTo(transform, true); + setTransform(transform); + vec2 size = _resolution / _dpi * INCHES_TO_METERS * vec2(getDimensions()); // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), size, distance); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 9fa13690f1..7ee80e73af 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -321,10 +321,6 @@ public: /// return preferred shape type (actual physical shape may differ) virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; } - // these are only needed because the names don't match - virtual const glm::quat getRotation() const { return getOrientation(); } - virtual void setRotation(glm::quat orientation) { setOrientation(orientation); } - // updateFoo() methods to be used when changes need to be accumulated in the _dirtyFlags virtual void updateRegistrationPoint(const glm::vec3& value); void updatePosition(const glm::vec3& value); diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 2a3cb4af47..41b1460619 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -513,6 +513,15 @@ const Transform SpatiallyNestable::getTransform(bool& success, int depth) const return result; } +const Transform SpatiallyNestable::getTransform() const { + bool success; + Transform result = getTransform(success); + if (!success) { + qDebug() << "getTransform failed for" << getID(); + } + return result; +} + const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success, int depth) const { // this returns the world-space transform for this object. It finds its parent's transform (which may // cause this object's parent to query its parent, etc) and multiplies this object's local transform onto it. @@ -558,6 +567,12 @@ void SpatiallyNestable::setTransform(const Transform& transform, bool& success) } } +bool SpatiallyNestable::setTransform(const Transform& transform) { + bool success; + setTransform(transform, success); + return success; +} + glm::vec3 SpatiallyNestable::getScale() const { // TODO: scale glm::vec3 result; @@ -575,7 +590,7 @@ glm::vec3 SpatiallyNestable::getScale(int jointIndex) const { void SpatiallyNestable::setScale(const glm::vec3& scale) { // guard against introducing NaN into the transform if (isNaN(scale)) { - qDebug() << "SpatiallyNestable::setLocalScale -- scale contains NaN"; + qDebug() << "SpatiallyNestable::setScale -- scale contains NaN"; return; } // TODO: scale @@ -585,6 +600,19 @@ void SpatiallyNestable::setScale(const glm::vec3& scale) { dimensionsChanged(); } +void SpatiallyNestable::setScale(float value) { + // guard against introducing NaN into the transform + if (value <= 0.0f) { + qDebug() << "SpatiallyNestable::setScale -- scale is zero or negative value"; + return; + } + // TODO: scale + _transformLock.withWriteLock([&] { + _transform.setScale(value); + }); + dimensionsChanged(); +} + const Transform SpatiallyNestable::getLocalTransform() const { Transform result; _transformLock.withReadLock([&] { diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index c2563a1188..a579c0dfe4 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -28,7 +28,8 @@ using SpatiallyNestableConstPointer = std::shared_ptr; enum class NestableType { Entity, - Avatar + Avatar, + Overlay }; class SpatiallyNestable : public std::enable_shared_from_this { @@ -53,7 +54,9 @@ public: // world frame virtual const Transform getTransform(bool& success, int depth = 0) const; + virtual const Transform getTransform() const; virtual void setTransform(const Transform& transform, bool& success); + virtual bool setTransform(const Transform& transform); virtual Transform getParentTransform(bool& success, int depth = 0) const; @@ -68,6 +71,10 @@ public: virtual void setOrientation(const glm::quat& orientation, bool& success, bool tellPhysics = true); virtual void setOrientation(const glm::quat& orientation); + // these are here because some older code uses rotation rather than orientation + virtual const glm::quat getRotation() const { return getOrientation(); } + virtual void setRotation(glm::quat orientation) { setOrientation(orientation); } + virtual glm::vec3 getVelocity(bool& success) const; virtual glm::vec3 getVelocity() const; virtual void setVelocity(const glm::vec3& velocity, bool& success); @@ -91,6 +98,7 @@ public: virtual glm::vec3 getScale() const; virtual void setScale(const glm::vec3& scale); + virtual void setScale(float value); // get world-frame values for a specific joint virtual const Transform getTransform(int jointIndex, bool& success, int depth = 0) const; @@ -123,10 +131,10 @@ public: // this object's frame virtual const Transform getAbsoluteJointTransformInObjectFrame(int jointIndex) const; - virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const = 0; - virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const = 0; - virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) = 0; - virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) = 0; + virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const { return glm::quat(); } + virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const { return glm::vec3(); } + virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) { return false; } + virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) {return false; } SpatiallyNestablePointer getThisPointer() const; From 627048db845f7b957e1ff18df73f4b535f37cb67 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 31 Jul 2016 16:36:14 -0700 Subject: [PATCH 004/279] pull missing location properties from overlay --- interface/src/ui/overlays/Base3DOverlay.cpp | 67 +++++++++++++-------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index dc97af5300..b6f1c1eadc 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -42,35 +42,15 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : setTransform(base3DOverlay->getTransform()); } -QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties, - const QUuid& currentParentID, - int currentParentJointIndex) { +QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties) { // the position and rotation in _transform are relative to the parent (aka local). The versions coming from // scripts are in world-frame, unless localPosition or localRotation are used. Patch up the properties // so that "position" and "rotation" are relative-to-parent values. QVariantMap result = properties; - QUuid parentID = result["parentID"].isValid() ? QUuid(result["parentID"].toString()) : currentParentID; - int parentJointIndex = - result["parentJointIndex"].isValid() ? result["parentJointIndex"].toInt() : currentParentJointIndex; + QUuid parentID = result["parentID"].isValid() ? QUuid(result["parentID"].toString()) : QUuid(); + int parentJointIndex = result["parentJointIndex"].isValid() ? result["parentJointIndex"].toInt() : -1; bool success; - // carry over some legacy keys - if (!result["position"].isValid() && !result["localPosition"].isValid()) { - if (result["p1"].isValid()) { - result["position"] = result["p1"]; - } else if (result["point"].isValid()) { - result["position"] = result["point"]; - } else if (result["start"].isValid()) { - result["position"] = result["start"]; - } - } - if (!result["orientation"].isValid() && result["rotation"].isValid()) { - result["orientation"] = result["rotation"]; - } - if (!result["localOrientation"].isValid() && result["localRotation"].isValid()) { - result["localOrientation"] = result["localRotation"]; - } - // make "position" and "orientation" be relative-to-parent if (result["localPosition"].isValid()) { result["position"] = result["localPosition"]; @@ -92,8 +72,45 @@ QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& propert } void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { - QVariantMap properties = - convertOverlayLocationFromScriptSemantics(originalProperties, getParentID(), getParentJointIndex()); + QVariantMap properties = originalProperties; + + // carry over some legacy keys + if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { + if (properties["p1"].isValid()) { + properties["position"] = properties["p1"]; + } else if (properties["point"].isValid()) { + properties["position"] = properties["point"]; + } else if (properties["start"].isValid()) { + properties["position"] = properties["start"]; + } + } + if (!properties["orientation"].isValid() && properties["rotation"].isValid()) { + properties["orientation"] = properties["rotation"]; + } + if (!properties["localOrientation"].isValid() && properties["localRotation"].isValid()) { + properties["localOrientation"] = properties["localRotation"]; + } + + // All of parentID, parentJointIndex, position, orientation are needed to make sense of any of them. + // If any of these changed, pull any missing properties from the overlay. + if (properties["parentID"].isValid() || properties["parentJointIndex"].isValid() || + properties["position"].isValid() || properties["localPosition"].isValid() || + properties["orientation"].isValid() || properties["localOrientation"].isValid()) { + if (!properties["parentID"].isValid()) { + properties["parentID"] = getParentID(); + } + if (!properties["parentJointIndex"].isValid()) { + properties["parentJointIndex"] = getParentJointIndex(); + } + if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { + properties["position"] = vec3toVariant(getPosition()); + } + if (!properties["orientation"].isValid() || !properties["localOrientation"].isValid()) { + properties["orientation"] = quatToVariant(getOrientation()); + } + } + + properties = convertOverlayLocationFromScriptSemantics(properties); Overlay::setProperties(properties); bool needRenderItemUpdate = false; From 8102ea4b8ee3f2eddbd21bb889c277fb1db9a62f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Aug 2016 07:48:44 -0700 Subject: [PATCH 005/279] don't call locationChanged or dimensionsChanged when they didn't --- libraries/shared/src/SpatiallyNestable.cpp | 111 ++++++++++++++++----- 1 file changed, 87 insertions(+), 24 deletions(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 41b1460619..6a9cdd0935 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -313,17 +313,20 @@ void SpatiallyNestable::setPosition(const glm::vec3& position, bool& success, bo success = false; return; } + + bool changed = false; Transform parentTransform = getParentTransform(success); Transform myWorldTransform; _transformLock.withWriteLock([&] { Transform::mult(myWorldTransform, parentTransform, _transform); - myWorldTransform.setTranslation(position); - Transform::inverseMult(_transform, parentTransform, myWorldTransform); + if (myWorldTransform.getTranslation() != position) { + changed = true; + myWorldTransform.setTranslation(position); + Transform::inverseMult(_transform, parentTransform, myWorldTransform); + } }); - if (success) { + if (success && changed) { locationChanged(tellPhysics); - } else { - qDebug() << "setPosition failed for" << getID(); } } @@ -363,14 +366,18 @@ void SpatiallyNestable::setOrientation(const glm::quat& orientation, bool& succe return; } + bool changed = false; Transform parentTransform = getParentTransform(success); Transform myWorldTransform; _transformLock.withWriteLock([&] { Transform::mult(myWorldTransform, parentTransform, _transform); - myWorldTransform.setRotation(orientation); - Transform::inverseMult(_transform, parentTransform, myWorldTransform); + if (myWorldTransform.getRotation() != orientation) { + changed = true; + myWorldTransform.setRotation(orientation); + Transform::inverseMult(_transform, parentTransform, myWorldTransform); + } }); - if (success) { + if (success && changed) { locationChanged(tellPhysics); } } @@ -558,11 +565,17 @@ void SpatiallyNestable::setTransform(const Transform& transform, bool& success) success = false; return; } + + bool changed = false; Transform parentTransform = getParentTransform(success); _transformLock.withWriteLock([&] { + Transform beforeTransform = _transform; Transform::inverseMult(_transform, parentTransform, transform); + if (_transform != beforeTransform) { + changed = true; + } }); - if (success) { + if (success && changed) { locationChanged(); } } @@ -593,11 +606,18 @@ void SpatiallyNestable::setScale(const glm::vec3& scale) { qDebug() << "SpatiallyNestable::setScale -- scale contains NaN"; return; } + + bool changed = false; // TODO: scale _transformLock.withWriteLock([&] { - _transform.setScale(scale); + if (_transform.getScale() != scale) { + _transform.setScale(scale); + changed = true; + } }); - dimensionsChanged(); + if (changed) { + dimensionsChanged(); + } } void SpatiallyNestable::setScale(float value) { @@ -606,11 +626,20 @@ void SpatiallyNestable::setScale(float value) { qDebug() << "SpatiallyNestable::setScale -- scale is zero or negative value"; return; } + + bool changed = false; // TODO: scale _transformLock.withWriteLock([&] { + glm::vec3 beforeScale = _transform.getScale(); _transform.setScale(value); + if (_transform.getScale() != beforeScale) { + changed = true; + } }); - dimensionsChanged(); + + if (changed) { + dimensionsChanged(); + } } const Transform SpatiallyNestable::getLocalTransform() const { @@ -627,10 +656,18 @@ void SpatiallyNestable::setLocalTransform(const Transform& transform) { qDebug() << "SpatiallyNestable::setLocalTransform -- transform contains NaN"; return; } + + bool changed = false; _transformLock.withWriteLock([&] { - _transform = transform; + if (_transform != transform) { + _transform = transform; + changed = true; + } }); - locationChanged(); + + if (changed) { + locationChanged(); + } } glm::vec3 SpatiallyNestable::getLocalPosition() const { @@ -647,10 +684,16 @@ void SpatiallyNestable::setLocalPosition(const glm::vec3& position, bool tellPhy qDebug() << "SpatiallyNestable::setLocalPosition -- position contains NaN"; return; } + bool changed = false; _transformLock.withWriteLock([&] { - _transform.setTranslation(position); + if (_transform.getTranslation() != position) { + _transform.setTranslation(position); + changed = true; + } }); - locationChanged(tellPhysics); + if (changed) { + locationChanged(tellPhysics); + } } glm::quat SpatiallyNestable::getLocalOrientation() const { @@ -667,10 +710,16 @@ void SpatiallyNestable::setLocalOrientation(const glm::quat& orientation) { qDebug() << "SpatiallyNestable::setLocalOrientation -- orientation contains NaN"; return; } + bool changed = false; _transformLock.withWriteLock([&] { - _transform.setRotation(orientation); + if (_transform.getRotation() != orientation) { + _transform.setRotation(orientation); + changed = true; + } }); - locationChanged(); + if (changed) { + locationChanged(); + } } glm::vec3 SpatiallyNestable::getLocalVelocity() const { @@ -716,9 +765,14 @@ void SpatiallyNestable::setLocalScale(const glm::vec3& scale) { qDebug() << "SpatiallyNestable::setLocalScale -- scale contains NaN"; return; } + + bool changed = false; // TODO: scale _transformLock.withWriteLock([&] { - _transform.setScale(scale); + if (_transform.getScale() != scale) { + _transform.setScale(scale); + changed = true; + } }); dimensionsChanged(); } @@ -914,12 +968,18 @@ void SpatiallyNestable::getLocalTransformAndVelocities( } void SpatiallyNestable::setLocalTransformAndVelocities( - const Transform& localTransform, - const glm::vec3& localVelocity, - const glm::vec3& localAngularVelocity) { + const Transform& localTransform, + const glm::vec3& localVelocity, + const glm::vec3& localAngularVelocity) { + + bool changed = false; + // transform _transformLock.withWriteLock([&] { - _transform = localTransform; + if (_transform != localTransform) { + _transform = localTransform; + changed = true; + } }); // linear velocity _velocityLock.withWriteLock([&] { @@ -929,5 +989,8 @@ void SpatiallyNestable::setLocalTransformAndVelocities( _angularVelocityLock.withWriteLock([&] { _angularVelocity = localAngularVelocity; }); - locationChanged(false); + + if (changed) { + locationChanged(false); + } } From bddff3341acae693741462551f7e3a4141c62f7b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Aug 2016 07:49:22 -0700 Subject: [PATCH 006/279] angry De Morgan --- interface/src/ui/overlays/Base3DOverlay.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index b6f1c1eadc..0dbbe16c3f 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -105,7 +105,7 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { properties["position"] = vec3toVariant(getPosition()); } - if (!properties["orientation"].isValid() || !properties["localOrientation"].isValid()) { + if (!properties["orientation"].isValid() && !properties["localOrientation"].isValid()) { properties["orientation"] = quatToVariant(getOrientation()); } } @@ -168,9 +168,11 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { if (properties["parentID"].isValid()) { setParentID(QUuid(properties["parentID"].toString())); + needRenderItemUpdate = true; } if (properties["parentJointIndex"].isValid()) { setParentJointIndex(properties["parentJointIndex"].toInt()); + needRenderItemUpdate = true; } // Communicate changes to the renderItem if needed From 182829e64c01f519bf8f71a11204736359dcf2b2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Aug 2016 07:49:57 -0700 Subject: [PATCH 007/279] get transform once rather than 3 times --- interface/src/ui/overlays/Image3DOverlay.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index d05eaac07c..e22a0826d7 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -190,7 +190,7 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec float& distance, BoxFace& face, glm::vec3& surfaceNormal) { if (_texture && _texture->isLoaded()) { // Make sure position and rotation is updated. - Transform transform; + Transform transform = getTransform(); applyTransformTo(transform, true); setTransform(transform); @@ -202,7 +202,10 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize); // FIXME - face and surfaceNormal not being set - return findRayRectangleIntersection(origin, direction, getRotation(), getPosition(), dimensions, distance); + return findRayRectangleIntersection(origin, direction, + transform.getRotation(), + transform.getTranslation(), + dimensions, distance); } return false; From 2d88e74841838ebb2c7fe04044fcb550d0f09575 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Aug 2016 07:50:12 -0700 Subject: [PATCH 008/279] added operator!= for Transform class --- libraries/shared/src/Transform.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index 1e1d10c54b..38d47695f7 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -89,6 +89,10 @@ public: return _rotation == other._rotation && _scale == other._scale && _translation == other._translation; } + bool operator!=(const Transform& other) const { + return _rotation != other._rotation || _scale != other._scale || _translation != other._translation; + } + Transform& setIdentity(); const Vec3& getTranslation() const; From 6f3146f872b0313fccb5a812050c06f94d6112c9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Aug 2016 08:00:51 -0700 Subject: [PATCH 009/279] oops, position and rotation are local, by this point --- interface/src/ui/overlays/Base3DOverlay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 0dbbe16c3f..a5b5a2dd72 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -124,11 +124,11 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { } if (properties["position"].isValid()) { - setPosition(vec3FromVariant(properties["position"])); + setLocalPosition(vec3FromVariant(properties["position"])); needRenderItemUpdate = true; } if (properties["orientation"].isValid()) { - setOrientation(quatFromVariant(properties["orientation"])); + setLocalOrientation(quatFromVariant(properties["orientation"])); needRenderItemUpdate = true; } From 70becfaff1820b08dc73582e7b46bb2920875429 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Aug 2016 08:20:24 -0700 Subject: [PATCH 010/279] avoid some unneeded SpatiallyNestable transform-locks --- interface/src/ui/overlays/Image3DOverlay.cpp | 13 ++++++++----- interface/src/ui/overlays/Text3DOverlay.cpp | 8 +++++--- interface/src/ui/overlays/Web3DOverlay.cpp | 8 +++++--- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index e22a0826d7..a384c992ad 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -37,9 +37,11 @@ Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) : } void Image3DOverlay::update(float deltatime) { - Transform transform = getTransform(); - applyTransformTo(transform); - setTransform(transform); + if (usecTimestampNow() > _transformExpiry) { + Transform transform = getTransform(); + applyTransformTo(transform); + setTransform(transform); + } } void Image3DOverlay::render(RenderArgs* args) { @@ -191,8 +193,9 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec if (_texture && _texture->isLoaded()) { // Make sure position and rotation is updated. Transform transform = getTransform(); - applyTransformTo(transform, true); - setTransform(transform); + // XXX this code runs too often for this... + // applyTransformTo(transform, true); + // setTransform(transform); // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 1fbf6a9bbd..153f787fc7 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -65,9 +65,11 @@ xColor Text3DOverlay::getBackgroundColor() { } void Text3DOverlay::update(float deltatime) { - Transform transform = getTransform(); - applyTransformTo(transform); - setTransform(transform); + if (usecTimestampNow() > _transformExpiry) { + Transform transform = getTransform(); + applyTransformTo(transform); + setTransform(transform); + } } void Text3DOverlay::render(RenderArgs* args) { diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 1b9adbfa95..6ca3e49569 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -57,9 +57,11 @@ Web3DOverlay::~Web3DOverlay() { } void Web3DOverlay::update(float deltatime) { - Transform transform = getTransform(); - applyTransformTo(transform); - setTransform(transform); + if (usecTimestampNow() > _transformExpiry) { + Transform transform = getTransform(); + applyTransformTo(transform); + setTransform(transform); + } } void Web3DOverlay::render(RenderArgs* args) { From 28e0ca2e49e7c57fca14cdf7a3259d7a27d8d366 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Aug 2016 10:55:34 -0700 Subject: [PATCH 011/279] when a parent of a 3d overlay is deleted, delete the overlay --- interface/src/ui/overlays/Base3DOverlay.cpp | 4 ++++ interface/src/ui/overlays/Base3DOverlay.h | 1 + interface/src/ui/overlays/Overlay.h | 10 ++++++++-- interface/src/ui/overlays/Overlays.cpp | 1 + libraries/shared/src/SpatiallyNestable.cpp | 6 ++++++ libraries/shared/src/SpatiallyNestable.h | 3 ++- 6 files changed, 22 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index a5b5a2dd72..8f2149f02d 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -244,3 +244,7 @@ void Base3DOverlay::locationChanged(bool tellPhysics) { // Overlays can't currently have children. // SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also } + +void Base3DOverlay::parentDeleted() { + qApp->getOverlays().deleteOverlay(getOverlayID()); +} diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 551c1c3384..1860af4e85 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -57,6 +57,7 @@ public: protected: virtual void locationChanged(bool tellPhysics = true) override; + virtual void parentDeleted() override; float _lineWidth; bool _isSolid; diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 466ec0e913..51792b24b3 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -17,7 +17,7 @@ class Overlay : public QObject { Q_OBJECT - + public: enum Anchor { NO_ANCHOR, @@ -31,9 +31,13 @@ public: Overlay(); Overlay(const Overlay* overlay); ~Overlay(); + + unsigned int getOverlayID() { return _overlayID; } + void setOverlayID(unsigned int overlayID) { _overlayID = overlayID; } + virtual void update(float deltatime) {} virtual void render(RenderArgs* args) = 0; - + virtual AABox getBounds() const = 0; virtual bool supportsGetProperty() const { return true; } @@ -85,6 +89,8 @@ protected: render::ItemID _renderItemID{ render::Item::INVALID_ITEM_ID }; + unsigned int _overlayID; // what Overlays.cpp knows this instance as + bool _isLoaded; float _alpha; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 0b4bcc8652..2c1f7552cd 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -192,6 +192,7 @@ unsigned int Overlays::addOverlay(const QString& type, const QVariant& propertie unsigned int Overlays::addOverlay(Overlay::Pointer overlay) { QWriteLocker lock(&_lock); unsigned int thisID = _nextOverlayID; + overlay->setOverlayID(thisID); _nextOverlayID++; if (overlay->is3D()) { _overlaysWorld[thisID] = overlay; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 6a9cdd0935..c912ecfc47 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -26,6 +26,12 @@ SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) : _transform.setRotation(glm::quat()); } +SpatiallyNestable::~SpatiallyNestable() { + forEachChild([&](SpatiallyNestablePointer object) { + object->parentDeleted(); + }); +} + const QUuid SpatiallyNestable::getID() const { QUuid result; _idLock.withReadLock([&] { diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index a579c0dfe4..391f13cc27 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -35,7 +35,7 @@ enum class NestableType { class SpatiallyNestable : public std::enable_shared_from_this { public: SpatiallyNestable(NestableType nestableType, QUuid id); - virtual ~SpatiallyNestable() { } + virtual ~SpatiallyNestable(); virtual const QUuid getID() const; virtual void setID(const QUuid& id); @@ -178,6 +178,7 @@ protected: virtual void locationChanged(bool tellPhysics = true); // called when a this object's location has changed virtual void dimensionsChanged() { } // called when a this object's dimensions have changed + virtual void parentDeleted() { } // called on children of a deleted parent // _queryAACube is used to decide where something lives in the octree mutable AACube _queryAACube; From 217a102926c39e732437559a60a87f1a8938864a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 1 Aug 2016 13:15:55 -0700 Subject: [PATCH 012/279] working on loading fade --- libraries/render-utils/src/MeshPartPayload.cpp | 16 +++++++++++++++- libraries/render-utils/src/MeshPartPayload.h | 7 +++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index cb6c73f414..da9ba50271 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -329,6 +329,8 @@ ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int par updateTransform(transform, offsetTransform); initCache(); + + _fadeStartTime = usecTimestampNow(); } void ModelMeshPartPayload::initCache() { @@ -352,6 +354,11 @@ void ModelMeshPartPayload::initCache() { } +float ModelMeshPartPayload::calcFadeRatio() const { + const float FADE_TIME = 0.5f; + float t = std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); + return -0.5f * (cosf(M_PI*t) - 1.0f); +} void ModelMeshPartPayload::notifyLocationChanged() { @@ -392,6 +399,10 @@ ItemKey ModelMeshPartPayload::getKey() const { } } + if (calcFadeRatio() < 1.0f) { + builder.withTransparent(); + } + return builder.build(); } @@ -429,7 +440,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { drawMaterialKey = _drawMaterial->getKey(); } - bool isTranslucent = drawMaterialKey.isTranslucent(); + bool isTranslucent = drawMaterialKey.isTranslucent() || calcFadeRatio() < 1.0f; bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty(); bool hasSpecular = drawMaterialKey.isMetallicMap(); bool hasLightmap = drawMaterialKey.isLightmapMap(); @@ -541,6 +552,9 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // apply material properties bindMaterial(batch, locations); + // model fading + batch._glColor4f(1.0f, 1.0f, 1.0f, calcFadeRatio()); + if (args) { args->_details._materialSwitches++; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 41869ec7e3..d5c59a7967 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -81,6 +81,9 @@ public: void notifyLocationChanged() override; void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices); + // Entity fade in + float calcFadeRatio() const; + // Render Item interface render::ItemKey getKey() const override; render::ShapeKey getShapeKey() const override; // shape interface @@ -99,6 +102,10 @@ public: bool _isSkinned{ false }; bool _isBlendShaped{ false }; + +private: + quint64 _fadeStartTime { usecTimestampNow() }; + bool _hasFadeStarted { false }; }; namespace render { From 313ba87fce2788ed441c3d3d377f828564e7b91c Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 1 Aug 2016 14:47:20 -0700 Subject: [PATCH 013/279] fade on texture load --- libraries/render-utils/src/MeshPartPayload.cpp | 15 +++++---------- libraries/render-utils/src/MeshPartPayload.h | 7 +++++-- libraries/render-utils/src/Model.cpp | 4 ++++ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index da9ba50271..99fedefc99 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -329,8 +329,6 @@ ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int par updateTransform(transform, offsetTransform); initCache(); - - _fadeStartTime = usecTimestampNow(); } void ModelMeshPartPayload::initCache() { @@ -357,7 +355,7 @@ void ModelMeshPartPayload::initCache() { float ModelMeshPartPayload::calcFadeRatio() const { const float FADE_TIME = 0.5f; float t = std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); - return -0.5f * (cosf(M_PI*t) - 1.0f); + return -(cosf(M_PI_2*t) - 1.0f); } void ModelMeshPartPayload::notifyLocationChanged() { @@ -495,9 +493,9 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } - // TODO: Get rid of that extra call - if (!_hasColorAttrib) { - batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + float fadeRatio = calcFadeRatio(); + if (!_hasColorAttrib || fadeRatio < 1.0f) { + batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); } } @@ -528,7 +526,7 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: void ModelMeshPartPayload::render(RenderArgs* args) const { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); - if (!_model->_readyWhenAdded || !_model->_isVisible) { + if (!_model->_readyWhenAdded || !_model->_isVisible || !_hasStartedFade) { return; // bail asap } @@ -552,9 +550,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // apply material properties bindMaterial(batch, locations); - // model fading - batch._glColor4f(1.0f, 1.0f, 1.0f, calcFadeRatio()); - if (args) { args->_details._materialSwitches++; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index d5c59a7967..4be6132122 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -83,6 +83,9 @@ public: // Entity fade in float calcFadeRatio() const; + void startFade() { _fadeStartTime = usecTimestampNow(); } + bool hasStartedFade() { return _hasStartedFade; } + void setHasStartedFade(bool hasStartedFade) { _hasStartedFade = hasStartedFade; } // Render Item interface render::ItemKey getKey() const override; @@ -104,8 +107,8 @@ public: bool _isBlendShaped{ false }; private: - quint64 _fadeStartTime { usecTimestampNow() }; - bool _hasFadeStarted { false }; + quint64 _fadeStartTime { 0 }; + bool _hasStartedFade { false }; }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b04a1d8023..cc7178587f 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -207,6 +207,10 @@ void Model::updateRenderItems() { render::PendingChanges pendingChanges; foreach (auto itemID, self->_modelMeshRenderItems.keys()) { pendingChanges.updateItem(itemID, [modelTransform, modelMeshOffset, deleteGeometryCounter](ModelMeshPartPayload& data) { + if (!data.hasStartedFade() && data._model && data._model->isLoaded() && data._model->getGeometry()->areTexturesLoaded()) { + data.startFade(); + data.setHasStartedFade(true); + } // Ensure the model geometry was not reset between frames if (data._model && data._model->isLoaded() && deleteGeometryCounter == data._model->_deleteGeometryCounter) { // lazy update of cluster matrices used for rendering. We need to update them here, so we can correctly update the bounding box. From ae0b9ea9a32ea85b4a73df33d37ec8e0404128cc Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 1 Aug 2016 15:26:04 -0700 Subject: [PATCH 014/279] fade wireframes --- libraries/render-utils/src/MeshPartPayload.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 99fedefc99..030b5699aa 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -438,7 +438,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { drawMaterialKey = _drawMaterial->getKey(); } - bool isTranslucent = drawMaterialKey.isTranslucent() || calcFadeRatio() < 1.0f; + bool isTranslucent = drawMaterialKey.isTranslucent(); bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty(); bool hasSpecular = drawMaterialKey.isMetallicMap(); bool hasLightmap = drawMaterialKey.isLightmapMap(); @@ -452,7 +452,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { } ShapeKey::Builder builder; - if (isTranslucent) { + if (isTranslucent || calcFadeRatio() < 0.9f) { builder.withTranslucent(); } if (hasTangents) { From 6154aaddda9c911a202e4a8a2b058fe3005cfa77 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 1 Aug 2016 16:19:03 -0700 Subject: [PATCH 015/279] try to fix linux build --- libraries/render-utils/src/MeshPartPayload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 030b5699aa..48c1c88757 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -355,7 +355,7 @@ void ModelMeshPartPayload::initCache() { float ModelMeshPartPayload::calcFadeRatio() const { const float FADE_TIME = 0.5f; float t = std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); - return -(cosf(M_PI_2*t) - 1.0f); + return -(cosf((float)M_PI_2 * t) - 1.0f); } void ModelMeshPartPayload::notifyLocationChanged() { From 0b5c7909b829f48dc61954b62fef61d1ad8e9a38 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 1 Aug 2016 18:45:25 -0700 Subject: [PATCH 016/279] a calculated change --- libraries/render-utils/src/MeshPartPayload.cpp | 8 ++++---- libraries/render-utils/src/MeshPartPayload.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 48c1c88757..9f107bd3e1 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -352,7 +352,7 @@ void ModelMeshPartPayload::initCache() { } -float ModelMeshPartPayload::calcFadeRatio() const { +float ModelMeshPartPayload::calculateFadeRatio() const { const float FADE_TIME = 0.5f; float t = std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); return -(cosf((float)M_PI_2 * t) - 1.0f); @@ -397,7 +397,7 @@ ItemKey ModelMeshPartPayload::getKey() const { } } - if (calcFadeRatio() < 1.0f) { + if (calculateFadeRatio() < 1.0f) { builder.withTransparent(); } @@ -452,7 +452,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { } ShapeKey::Builder builder; - if (isTranslucent || calcFadeRatio() < 0.9f) { + if (isTranslucent || calculateFadeRatio() < 0.9f) { builder.withTranslucent(); } if (hasTangents) { @@ -493,7 +493,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } - float fadeRatio = calcFadeRatio(); + float fadeRatio = calculateFadeRatio(); if (!_hasColorAttrib || fadeRatio < 1.0f) { batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 4be6132122..e0181a366d 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -82,7 +82,7 @@ public: void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices); // Entity fade in - float calcFadeRatio() const; + float calculateFadeRatio() const; void startFade() { _fadeStartTime = usecTimestampNow(); } bool hasStartedFade() { return _hasStartedFade; } void setHasStartedFade(bool hasStartedFade) { _hasStartedFade = hasStartedFade; } From 2c449320d0a46ee23728a0d222b761d5072fa2ba Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 Aug 2016 08:20:48 -0700 Subject: [PATCH 017/279] when and ID of a SpatiallyNestable subclass is changed, update the parentID of any children --- libraries/shared/src/SpatiallyNestable.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index c912ecfc47..c3dbafa0d9 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -41,6 +41,10 @@ const QUuid SpatiallyNestable::getID() const { } void SpatiallyNestable::setID(const QUuid& id) { + // adjust the parentID of any children + forEachChild([&](SpatiallyNestablePointer object) { + object->setParentID(id); + }); _idLock.withWriteLock([&] { _id = id; }); From 74f11eb70b5692eb1b0bca523cf709ab39250747 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 2 Aug 2016 10:50:31 -0700 Subject: [PATCH 018/279] try a different easing function --- libraries/render-utils/src/MeshPartPayload.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 9f107bd3e1..63068f7796 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -353,9 +353,13 @@ void ModelMeshPartPayload::initCache() { } float ModelMeshPartPayload::calculateFadeRatio() const { - const float FADE_TIME = 0.5f; - float t = std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); - return -(cosf((float)M_PI_2 * t) - 1.0f); + const float FADE_TIME = 1.0f; + float t = 2.0f * std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); + float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-pow(2.0f, -10.0f * (t - 1.0f)) + 2.0f); + + // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly + const float EASING_SCALE = 1.001f; + return std::min(EASING_SCALE * fadeRatio, 1.0f); } void ModelMeshPartPayload::notifyLocationChanged() { From 3d08502080f40477a42d9b885216432a29fe0983 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 2 Aug 2016 10:50:57 -0700 Subject: [PATCH 019/279] space --- libraries/render-utils/src/MeshPartPayload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 63068f7796..0616ae20f7 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -354,7 +354,7 @@ void ModelMeshPartPayload::initCache() { float ModelMeshPartPayload::calculateFadeRatio() const { const float FADE_TIME = 1.0f; - float t = 2.0f * std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); + float t = 2.0f * std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-pow(2.0f, -10.0f * (t - 1.0f)) + 2.0f); // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly From ad5dec829c22695afd40a69406900d4e3e968129 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 2 Aug 2016 10:52:51 -0700 Subject: [PATCH 020/279] why did I change that --- libraries/render-utils/src/MeshPartPayload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 0616ae20f7..83b25a49e3 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -456,7 +456,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { } ShapeKey::Builder builder; - if (isTranslucent || calculateFadeRatio() < 0.9f) { + if (isTranslucent || calculateFadeRatio() < 1.0f) { builder.withTranslucent(); } if (hasTangents) { From 5de21982be4b1e587a6bd6ece6c9c1bc96137ffd Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 2 Aug 2016 11:44:17 -0700 Subject: [PATCH 021/279] fix linux build --- libraries/render-utils/src/MeshPartPayload.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 83b25a49e3..f599e9df67 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -355,7 +355,7 @@ void ModelMeshPartPayload::initCache() { float ModelMeshPartPayload::calculateFadeRatio() const { const float FADE_TIME = 1.0f; float t = 2.0f * std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); - float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-pow(2.0f, -10.0f * (t - 1.0f)) + 2.0f); + float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-powf(2.0f, -10.0f * (t - 1.0f)) + 2.0f); // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly const float EASING_SCALE = 1.001f; From d63a0ef08f36be368dc61448a4c8507b5c9b3942 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 2 Aug 2016 18:09:42 -0700 Subject: [PATCH 022/279] working on making other entities transparent --- .../src/RenderableEntityItem.cpp | 4 ++-- .../src/RenderableShapeEntityItem.cpp | 9 +++++++-- .../src/RenderableShapeEntityItem.h | 6 +++++- libraries/entities/src/EntityItem.h | 1 + libraries/render-utils/src/MeshPartPayload.cpp | 18 +++++------------- libraries/render-utils/src/MeshPartPayload.h | 1 - libraries/shared/src/Interpolate.cpp | 12 ++++++++++++ libraries/shared/src/Interpolate.h | 4 ++++ 8 files changed, 36 insertions(+), 19 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 011675fc82..d49eacdf7b 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -1,4 +1,4 @@ -// + // // RenderableEntityItem.cpp // interface/src // @@ -19,7 +19,7 @@ namespace render { if (payload->entity->getType() == EntityTypes::Light) { return ItemKey::Builder::light(); } - if (payload && payload->entity->getType() == EntityTypes::PolyLine) { + if (payload && (payload->entity->getType() == EntityTypes::PolyLine || payload->entity->isTransparent())) { return ItemKey::Builder::transparentShape(); } } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 48ad05a714..61e152f5ac 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -40,7 +40,6 @@ static std::array MAPPING { { GeometryCache::Cylinder, } }; - RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { Pointer entity = std::make_shared(entityID); entity->setProperties(properties); @@ -106,7 +105,13 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { DependencyManager::get()->renderShape(batch, MAPPING[_shape]); } else { // FIXME, support instanced multi-shape rendering using multidraw indirect - DependencyManager::get()->renderSolidShapeInstance(batch, MAPPING[_shape], color); + float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime); + if (fadeRatio < 1.0f) { + color = glm::vec4(0.0f, 0.0f, 1.0f, 0.5f); + DependencyManager::get()->renderSolidShapeInstance(batch, MAPPING[_shape], color); + } else { + DependencyManager::get()->renderSolidShapeInstance(batch, MAPPING[_shape], color); + } } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index b18370b13c..7bfb411874 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -11,6 +11,7 @@ #include #include +#include #include "RenderableEntityItem.h" @@ -21,15 +22,18 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties); - RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) {} + RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID), _fadeStartTime(usecTimestampNow()) {} void render(RenderArgs* args) override; void setUserData(const QString& value) override; + bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } + SIMPLE_RENDERABLE(); private: QSharedPointer _procedural; + quint64 _fadeStartTime { 0 }; }; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 9fa13690f1..f1715a2525 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -435,6 +435,7 @@ public: QUuid getOwningAvatarID() const { return _owningAvatarID; } void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } + virtual bool isTransparent() { return false; } protected: diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index f599e9df67..fe914f4d1a 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -13,6 +13,8 @@ #include +#include + #include "DeferredLightingEffect.h" #include "Model.h" @@ -352,16 +354,6 @@ void ModelMeshPartPayload::initCache() { } -float ModelMeshPartPayload::calculateFadeRatio() const { - const float FADE_TIME = 1.0f; - float t = 2.0f * std::min(((float)(usecTimestampNow() - _fadeStartTime)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); - float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-powf(2.0f, -10.0f * (t - 1.0f)) + 2.0f); - - // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly - const float EASING_SCALE = 1.001f; - return std::min(EASING_SCALE * fadeRatio, 1.0f); -} - void ModelMeshPartPayload::notifyLocationChanged() { } @@ -401,7 +393,7 @@ ItemKey ModelMeshPartPayload::getKey() const { } } - if (calculateFadeRatio() < 1.0f) { + if (Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f) { builder.withTransparent(); } @@ -456,7 +448,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { } ShapeKey::Builder builder; - if (isTranslucent || calculateFadeRatio() < 1.0f) { + if (isTranslucent || Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f) { builder.withTranslucent(); } if (hasTangents) { @@ -497,7 +489,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } - float fadeRatio = calculateFadeRatio(); + float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime); if (!_hasColorAttrib || fadeRatio < 1.0f) { batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index e0181a366d..54878f3352 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -82,7 +82,6 @@ public: void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices); // Entity fade in - float calculateFadeRatio() const; void startFade() { _fadeStartTime = usecTimestampNow(); } bool hasStartedFade() { return _hasStartedFade; } void setHasStartedFade(bool hasStartedFade) { _hasStartedFade = hasStartedFade; } diff --git a/libraries/shared/src/Interpolate.cpp b/libraries/shared/src/Interpolate.cpp index bef69c9a33..b20c7b8fac 100644 --- a/libraries/shared/src/Interpolate.cpp +++ b/libraries/shared/src/Interpolate.cpp @@ -14,6 +14,8 @@ #include #include +#include "NumericalConstants.h" + float Interpolate::bezierInterpolate(float y1, float y2, float y3, float u) { // https://en.wikipedia.org/wiki/Bezier_curve assert(0.0f <= u && u <= 1.0f); @@ -58,3 +60,13 @@ float Interpolate::interpolate3Points(float y1, float y2, float y3, float u) { } } } + +float Interpolate::calculateFadeRatio(quint64 start) { + const float FADE_TIME = 1.0f; + float t = 2.0f * std::min(((float)(usecTimestampNow() - start)) / ((float)(FADE_TIME * USECS_PER_SECOND)), 1.0f); + float fadeRatio = (t < 1.0f) ? 0.5f * powf(2.0f, 10.0f * (t - 1.0f)) : 0.5f * (-powf(2.0f, -10.0f * (t - 1.0f)) + 2.0f); + + // The easing function isn't exactly 1 at t = 2, so we need to scale the whole function up slightly + const float EASING_SCALE = 1.001f; + return std::min(EASING_SCALE * fadeRatio, 1.0f); +} \ No newline at end of file diff --git a/libraries/shared/src/Interpolate.h b/libraries/shared/src/Interpolate.h index 316bee1339..a9fa5baad2 100644 --- a/libraries/shared/src/Interpolate.h +++ b/libraries/shared/src/Interpolate.h @@ -12,6 +12,8 @@ #ifndef hifi_Interpolate_h #define hifi_Interpolate_h +#include "SharedUtil.h" + class Interpolate { public: @@ -22,6 +24,8 @@ public: // Interpolate at position u [0.0 - 1.0] between y values equally spaced along the x-axis such that the interpolated values // pass through all three y values. Return value lies wholly within the range of y values passed in. static float interpolate3Points(float y1, float y2, float y3, float u); + + static float calculateFadeRatio(quint64 start); }; #endif // hifi_Interpolate_h From 2eb0c7735f7a52bd1c6acb12fbd42ff4398028b9 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 3 Aug 2016 13:30:05 -0700 Subject: [PATCH 023/279] working on fading shape entities --- interface/src/avatar/Avatar.cpp | 2 +- interface/src/ui/overlays/Cube3DOverlay.cpp | 4 +-- interface/src/ui/overlays/Line3DOverlay.cpp | 4 +-- .../src/ui/overlays/Rectangle3DOverlay.cpp | 2 +- interface/src/ui/overlays/Shape3DOverlay.cpp | 2 +- interface/src/ui/overlays/Sphere3DOverlay.cpp | 2 +- interface/src/ui/overlays/Web3DOverlay.cpp | 2 +- .../src/RenderableShapeEntityItem.cpp | 12 +++---- .../src/RenderableShapeEntityItem.h | 4 +-- .../src/RenderableTextEntityItem.cpp | 2 +- .../src/RenderableWebEntityItem.cpp | 5 +-- .../render-utils/src/DeferredBufferWrite.slh | 2 ++ libraries/render-utils/src/GeometryCache.cpp | 34 +++++++++++++------ libraries/render-utils/src/GeometryCache.h | 30 ++++++++-------- .../render-utils/src/simple_textured.slf | 28 ++++++++++----- tests/gpu-test/src/TestFloorTexture.cpp | 2 +- 16 files changed, 81 insertions(+), 56 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4d9481f002..018a9bb2fe 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -776,7 +776,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const { PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect"); - DependencyManager::get()->bindSimpleProgram(batch, false, true, true, true); + DependencyManager::get()->bindSimpleProgram(batch, false, false, true, true, true); DependencyManager::get()->renderBevelCornersRect(batch, left, bottom, width, height, bevelDistance, backgroundColor); } diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index f72fb8d920..a61e442436 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -47,7 +47,7 @@ void Cube3DOverlay::render(RenderArgs* args) { auto geometryCache = DependencyManager::get(); auto pipeline = args->_pipeline; if (!pipeline) { - pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline(); + pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline(); } if (_isSolid) { @@ -55,7 +55,7 @@ void Cube3DOverlay::render(RenderArgs* args) { batch->setModelTransform(transform); geometryCache->renderSolidCubeInstance(*batch, cubeColor, pipeline); } else { - geometryCache->bindSimpleProgram(*batch, false, false, true, true); + geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); if (getIsDashedLine()) { transform.setScale(1.0f); batch->setModelTransform(transform); diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index c9a8b19f6a..a05783fea2 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -57,12 +57,12 @@ void Line3DOverlay::render(RenderArgs* args) { auto geometryCache = DependencyManager::get(); if (getIsDashedLine()) { // TODO: add support for color to renderDashedLine() - geometryCache->bindSimpleProgram(*batch, false, false, true, true); + geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); geometryCache->renderDashedLine(*batch, _start, _end, colorv4, _geometryCacheID); } else if (_glow > 0.0f) { geometryCache->renderGlowLine(*batch, _start, _end, colorv4, _glow, _glowWidth, _geometryCacheID); } else { - geometryCache->bindSimpleProgram(*batch, false, false, true, true); + geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); geometryCache->renderLine(*batch, _start, _end, colorv4, _geometryCacheID); } } diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 75d7ec565c..35c479dce6 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -61,7 +61,7 @@ void Rectangle3DOverlay::render(RenderArgs* args) { geometryCache->bindSimpleProgram(*batch); geometryCache->renderQuad(*batch, topLeft, bottomRight, rectangleColor); } else { - geometryCache->bindSimpleProgram(*batch, false, false, true, true); + geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); if (getIsDashedLine()) { glm::vec3 point1(-halfDimensions.x, -halfDimensions.y, 0.0f); glm::vec3 point2(halfDimensions.x, -halfDimensions.y, 0.0f); diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index cd07385aab..2556bc84aa 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -47,7 +47,7 @@ void Shape3DOverlay::render(RenderArgs* args) { auto geometryCache = DependencyManager::get(); auto pipeline = args->_pipeline; if (!pipeline) { - pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline(); + pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline(); } transform.setScale(dimensions); diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index bbdd886d11..774237d334 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -46,7 +46,7 @@ void Sphere3DOverlay::render(RenderArgs* args) { auto geometryCache = DependencyManager::get(); auto pipeline = args->_pipeline; if (!pipeline) { - pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline(); + pipeline = _isSolid ? geometryCache->getOpaqueShapePipeline() : geometryCache->getWireShapePipeline(); } if (_isSolid) { diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index c9c24d3ab6..769fab1acd 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -101,7 +101,7 @@ void Web3DOverlay::render(RenderArgs* args) { batch.setModelTransform(transform); auto geometryCache = DependencyManager::get(); - geometryCache->bindSimpleProgram(batch, true, false, true, false); + geometryCache->bindSimpleProgram(batch, true, false, false, true, false); geometryCache->renderQuad(batch, halfSize * -1.0f, halfSize, vec2(0), vec2(1), color); batch.setResourceTexture(0, args->_whiteTexture); // restore default white color after me } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 61e152f5ac..65c924080f 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -105,16 +105,12 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { DependencyManager::get()->renderShape(batch, MAPPING[_shape]); } else { // FIXME, support instanced multi-shape rendering using multidraw indirect - float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime); - if (fadeRatio < 1.0f) { - color = glm::vec4(0.0f, 0.0f, 1.0f, 0.5f); - DependencyManager::get()->renderSolidShapeInstance(batch, MAPPING[_shape], color); - } else { - DependencyManager::get()->renderSolidShapeInstance(batch, MAPPING[_shape], color); - } + color.a *= Interpolate::calculateFadeRatio(_fadeStartTime); + auto geometryCache = DependencyManager::get(); + auto pipeline = color.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline(); + geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline); } - static const auto triCount = DependencyManager::get()->getShapeTriangleCount(MAPPING[_shape]); args->_details._trianglesRendered += (int)triCount; } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 7bfb411874..537052cdfd 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -22,7 +22,7 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties); - RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID), _fadeStartTime(usecTimestampNow()) {} + RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) {} void render(RenderArgs* args) override; void setUserData(const QString& value) override; @@ -33,7 +33,7 @@ public: private: QSharedPointer _procedural; - quint64 _fadeStartTime { 0 }; + quint64 _fadeStartTime { usecTimestampNow() }; }; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 6773a906fe..7f2644a68e 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -62,7 +62,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) { batch.setModelTransform(transformToTopLeft); - DependencyManager::get()->bindSimpleProgram(batch, false, false, false, true); + DependencyManager::get()->bindSimpleProgram(batch, false, false, false, false, true); DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, backgroundColor); float scale = _lineHeight / _textRenderer->getFontSize(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 8298dbcec5..d86aa8e2f9 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -208,8 +208,9 @@ void RenderableWebEntityItem::render(RenderArgs* args) { batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture); textured = emissive = true; } - - DependencyManager::get()->bindSimpleProgram(batch, textured, culled, emissive); + bool transparent = false; + + DependencyManager::get()->bindSimpleProgram(batch, textured, transparent, culled, emissive); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, 0.0f)); } diff --git a/libraries/render-utils/src/DeferredBufferWrite.slh b/libraries/render-utils/src/DeferredBufferWrite.slh index 3153a851fb..aa79781c25 100755 --- a/libraries/render-utils/src/DeferredBufferWrite.slh +++ b/libraries/render-utils/src/DeferredBufferWrite.slh @@ -73,6 +73,8 @@ void packDeferredFragmentTranslucent(vec3 normal, float alpha, vec3 albedo, vec3 discard; } _fragColor0 = vec4(albedo.rgb, alpha); + _fragColor1 = vec4(packNormal(normal), clamp(roughness, 0.0, 1.0)); + } <@endif@> diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index cebd8ad37f..670881db80 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -397,14 +397,25 @@ gpu::Stream::FormatPointer& getInstancedSolidStreamFormat() { return INSTANCED_SOLID_STREAM_FORMAT; } -render::ShapePipelinePointer GeometryCache::_simplePipeline; +render::ShapePipelinePointer GeometryCache::_simpleOpaquePipeline; +render::ShapePipelinePointer GeometryCache::_simpleTransparentPipeline; render::ShapePipelinePointer GeometryCache::_simpleWirePipeline; GeometryCache::GeometryCache() : _nextID(0) { buildShapes(); - GeometryCache::_simplePipeline = - std::make_shared(getSimplePipeline(), nullptr, + GeometryCache::_simpleOpaquePipeline = + std::make_shared(getSimplePipeline(false, false, true, false), nullptr, + [](const render::ShapePipeline&, gpu::Batch& batch) { + // Set the defaults needed for a simple program + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, + DependencyManager::get()->getWhiteTexture()); + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, + DependencyManager::get()->getNormalFittingTexture()); + } + ); + GeometryCache::_simpleTransparentPipeline = + std::make_shared(getSimplePipeline(false, true, true, false), nullptr, [](const render::ShapePipeline&, gpu::Batch& batch) { // Set the defaults needed for a simple program batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, @@ -1703,6 +1714,7 @@ class SimpleProgramKey { public: enum FlagBit { IS_TEXTURED_FLAG = 0, + IS_TRANSPARENT_FLAG, IS_CULLED_FLAG, IS_UNLIT_FLAG, HAS_DEPTH_BIAS_FLAG, @@ -1712,6 +1724,7 @@ public: enum Flag { IS_TEXTURED = (1 << IS_TEXTURED_FLAG), + IS_TRANSPARENT = (1 << IS_TRANSPARENT_FLAG), IS_CULLED = (1 << IS_CULLED_FLAG), IS_UNLIT = (1 << IS_UNLIT_FLAG), HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG), @@ -1721,6 +1734,7 @@ public: bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); } bool isTextured() const { return isFlag(IS_TEXTURED); } + bool isTransparent() const { return isFlag(IS_TRANSPARENT); } bool isCulled() const { return isFlag(IS_CULLED); } bool isUnlit() const { return isFlag(IS_UNLIT); } bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); } @@ -1731,9 +1745,9 @@ public: int getRaw() const { return *reinterpret_cast(this); } - SimpleProgramKey(bool textured = false, bool culled = true, + SimpleProgramKey(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false) { - _flags = (textured ? IS_TEXTURED : 0) | (culled ? IS_CULLED : 0) | + _flags = (textured ? IS_TEXTURED : 0) | (transparent ? IS_TRANSPARENT : 0) | (culled ? IS_CULLED : 0) | (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0); } @@ -1748,8 +1762,8 @@ inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) { return a.getRaw() == b.getRaw(); } -void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool culled, bool unlit, bool depthBiased) { - batch.setPipeline(getSimplePipeline(textured, culled, unlit, depthBiased)); +void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) { + batch.setPipeline(getSimplePipeline(textured, transparent, culled, unlit, depthBiased)); // If not textured, set a default albedo map if (!textured) { @@ -1761,8 +1775,8 @@ void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool cul DependencyManager::get()->getNormalFittingTexture()); } -gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled, bool unlit, bool depthBiased) { - SimpleProgramKey config { textured, culled, unlit, depthBiased }; +gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool culled, bool unlit, bool depthBiased) { + SimpleProgramKey config { textured, transparent, culled, unlit, depthBiased }; // Compile the shaders static std::once_flag once; @@ -1798,7 +1812,7 @@ gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool culled state->setDepthBias(1.0f); state->setDepthBiasSlopeScale(1.0f); } - state->setBlendFunction(false, + state->setBlendFunction(config.isTransparent(), gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index bab0942672..33fe8b1ef3 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -152,12 +152,13 @@ public: // Bind the pipeline and get the state to render static geometry - void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool culled = true, + void bindSimpleProgram(gpu::Batch& batch, bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false); // Get the pipeline to render static geometry - gpu::PipelinePointer getSimplePipeline(bool textured = false, bool culled = true, + gpu::PipelinePointer getSimplePipeline(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false); - render::ShapePipelinePointer getShapePipeline() { return GeometryCache::_simplePipeline; } + render::ShapePipelinePointer getOpaqueShapePipeline() { return GeometryCache::_simpleOpaquePipeline; } + render::ShapePipelinePointer getTransparentShapePipeline() { return GeometryCache::_simpleTransparentPipeline; } render::ShapePipelinePointer getWireShapePipeline() { return GeometryCache::_simpleWirePipeline; } // Static (instanced) geometry @@ -165,42 +166,42 @@ public: void renderWireShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer); void renderSolidShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1), - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderSolidShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline) { + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderSolidShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline); } void renderWireShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1), - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderWireShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline) { + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderWireShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline); } void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline) { + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderSolidSphereInstance(batch, glm::vec4(color, 1.0f), pipeline); } void renderWireSphereInstance(gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleWirePipeline); void renderWireSphereInstance(gpu::Batch& batch, const glm::vec3& color, const render::ShapePipelinePointer& pipeline = _simpleWirePipeline) { renderWireSphereInstance(batch, glm::vec4(color, 1.0f), pipeline); } void renderSolidCubeInstance(gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline); void renderSolidCubeInstance(gpu::Batch& batch, const glm::vec3& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline) { + const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) { renderSolidCubeInstance(batch, glm::vec4(color, 1.0f), pipeline); } void renderWireCubeInstance(gpu::Batch& batch, const glm::vec4& color, - const render::ShapePipelinePointer& pipeline = _simplePipeline); + const render::ShapePipelinePointer& pipeline = _simpleWirePipeline); void renderWireCubeInstance(gpu::Batch& batch, const glm::vec3& color, const render::ShapePipelinePointer& pipeline = _simpleWirePipeline) { renderWireCubeInstance(batch, glm::vec4(color, 1.0f), pipeline); @@ -412,7 +413,8 @@ private: gpu::ShaderPointer _simpleShader; gpu::ShaderPointer _unlitShader; - static render::ShapePipelinePointer _simplePipeline; + static render::ShapePipelinePointer _simpleOpaquePipeline; + static render::ShapePipelinePointer _simpleTransparentPipeline; static render::ShapePipelinePointer _simpleWirePipeline; gpu::PipelinePointer _glowLinePipeline; QHash _simplePrograms; diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf index f045af2ce5..815d28310f 100644 --- a/libraries/render-utils/src/simple_textured.slf +++ b/libraries/render-utils/src/simple_textured.slf @@ -29,13 +29,23 @@ void main(void) { if (_color.a <= 0.0) { texel = colorToLinearRGBA(texel); } - packDeferredFragment( - normalize(_normal.xyz), - texel.a, - _color.rgb * texel.rgb, - DEFAULT_ROUGHNESS, - DEFAULT_METALLIC, - DEFAULT_EMISSIVE, - DEFAULT_OCCLUSION, - DEFAULT_SCATTERING); + + if (_color.a * texel.a < 1.0) { + packDeferredFragmentTranslucent( + normalize(_normal), + _color.a * texel.a, + _color.rgb * texel.rgb, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); + } else { + packDeferredFragment( + normalize(_normal), + 1.0, + _color.rgb * texel.rgb, + DEFAULT_ROUGHNESS, + DEFAULT_METALLIC, + DEFAULT_EMISSIVE, + DEFAULT_OCCLUSION, + DEFAULT_SCATTERING); + } } \ No newline at end of file diff --git a/tests/gpu-test/src/TestFloorTexture.cpp b/tests/gpu-test/src/TestFloorTexture.cpp index 884be5ec30..b7853fdbb4 100644 --- a/tests/gpu-test/src/TestFloorTexture.cpp +++ b/tests/gpu-test/src/TestFloorTexture.cpp @@ -78,7 +78,7 @@ void FloorTextureTest::renderTest(size_t testId, RenderArgs* args) { texture->incremementMinMip(); } - geometryCache->bindSimpleProgram(batch, true, true, true); + geometryCache->bindSimpleProgram(batch, true, false, true, true); batch.setInputBuffer(0, vertexBuffer, 0, sizeof(Vertex)); batch.setInputFormat(vertexFormat); batch.setIndexBuffer(gpu::UINT16, indexBuffer, 0); From 40a5e4abc892cb3ef0357cdc0b492a01327e40a4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 Aug 2016 13:54:25 -0700 Subject: [PATCH 024/279] in full-screen mirror camera mode, arrow keys move camera but don't make the avatar walk --- .../resources/controllers/keyboardMouse.json | 20 +++++++++++++--- interface/src/Application.cpp | 24 ++++++++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 0037e3741d..8baf56684a 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -63,6 +63,19 @@ ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] ] }, + "when": "Application.CameraFirstPerson", + "to": "Actions.Yaw" + }, + { "from": { "makeAxis" : [ + ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], + ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] + ] + }, + "when": "Application.CameraThirdPerson", + "to": "Actions.Yaw" + }, + { "from": { "makeAxis" : [ ["Keyboard.A"], ["Keyboard.D"] ] }, + "when": "Application.CameraFSM", "to": "Actions.Yaw" }, @@ -81,9 +94,10 @@ { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, { "from": "Keyboard.Up", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, - - { "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" }, - { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" }, + { "from": "Keyboard.Down", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_BACKWARD" }, + { "from": "Keyboard.Down", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_BACKWARD" }, { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5d50a1c9fe..6fc294b8d7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -391,6 +391,11 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt } static const QString STATE_IN_HMD = "InHMD"; +static const QString STATE_CAMERA_FULL_SCREEN_MIRROR = "CameraFSM"; +static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson"; +static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson"; +static const QString STATE_CAMERA_ENTITY = "CameraEntity"; +static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent"; static const QString STATE_SNAP_TURN = "SnapTurn"; static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_NAV_FOCUSED = "NavigationFocused"; @@ -470,7 +475,9 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } }); + controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, + STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, + STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } }); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -954,6 +961,21 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _applicationStateDevice->setInputVariant(STATE_IN_HMD, []() -> float { return qApp->isHMDMode() ? 1 : 0; }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_FULL_SCREEN_MIRROR, []() -> float { + return qApp->getCamera()->getMode() == CAMERA_MODE_MIRROR ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_FIRST_PERSON, []() -> float { + return qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float { + return qApp->getCamera()->getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_ENTITY, []() -> float { + return qApp->getCamera()->getMode() == CAMERA_MODE_ENTITY ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_CAMERA_INDEPENDENT, []() -> float { + return qApp->getCamera()->getMode() == CAMERA_MODE_INDEPENDENT ? 1 : 0; + }); _applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float { return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0; }); From 27bacc9165152a5a8d161d378ccb806cbb13489a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 3 Aug 2016 14:33:05 -0700 Subject: [PATCH 025/279] try to fade in procedural shapes --- .../src/RenderableShapeEntityItem.cpp | 9 ++++ .../src/RenderableShapeEntityItem.h | 2 +- .../procedural/src/procedural/Procedural.cpp | 1 + .../procedural/src/procedural/Procedural.h | 3 ++ libraries/render-utils/src/simple.slf | 42 ++++++++++++++++--- .../src/simple_textured_unlit.slf | 17 ++++++-- 6 files changed, 64 insertions(+), 10 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 65c924080f..352777e958 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -71,6 +71,14 @@ void RenderableShapeEntityItem::setUserData(const QString& value) { } } +bool RenderableShapeEntityItem::isTransparent() { + if (_procedural && _procedural->ready()) { + return Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f; + } else { + return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; + } +} + void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); //Q_ASSERT(getType() == EntityTypes::Shape); @@ -101,6 +109,7 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { if (_procedural->ready()) { _procedural->prepare(batch, getPosition(), getDimensions(), getOrientation()); auto outColor = _procedural->getColor(color); + outColor.a *= Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()); batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); DependencyManager::get()->renderShape(batch, MAPPING[_shape]); } else { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 537052cdfd..716daf1d03 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -27,7 +27,7 @@ public: void render(RenderArgs* args) override; void setUserData(const QString& value) override; - bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } + bool isTransparent() override; SIMPLE_RENDERABLE(); diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 7dd729384c..1c7fcade18 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -184,6 +184,7 @@ bool Procedural::ready() { // Reset dirty flag after reading _proceduralData, but before releasing lock // to avoid resetting it after more data is set _proceduralDataDirty = false; + _fadeStartTime = usecTimestampNow(); } if (!_enabled) { diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index 6991b47946..f8d0c963f4 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -42,6 +42,7 @@ public: const gpu::ShaderPointer& getShader() const { return _shader; } glm::vec4 getColor(const glm::vec4& entityColor); + quint64 getFadeStartTime() { return _fadeStartTime; } uint8_t _version { 1 }; @@ -106,6 +107,8 @@ private: void setupUniforms(); void setupChannels(bool shouldCreate); + + quint64 _fadeStartTime; }; #endif diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index dd5faf40db..fff91a9261 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -49,11 +49,43 @@ void main(void) { #endif - if (emissiveAmount > 0.0) { - packDeferredFragmentLightmap( - normal, 1.0, diffuse, max(0, 1.0 - shininess / 128.0), DEFAULT_METALLIC, specular, specular); + if (_color.a < 1.0) { + if (emissiveAmount > 0.0) { + // TODO: transparent emissive? + packDeferredFragmentTranslucent( + normal, + _color.a, + diffuse, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); + } else { + packDeferredFragmentTranslucent( + normal, + _color.a, + diffuse, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); + } } else { - packDeferredFragment( - normal, 1.0, diffuse, max(0, 1.0 - shininess / 128.0), length(specular), DEFAULT_EMISSIVE, DEFAULT_OCCLUSION, DEFAULT_SCATTERING); + if (emissiveAmount > 0.0) { + packDeferredFragmentLightmap( + normal, + 1.0, + diffuse, + max(0, 1.0 - shininess / 128.0), + DEFAULT_METALLIC, + specular, + specular); + } else { + packDeferredFragment( + normal, + 1.0, + diffuse, + max(0, 1.0 - shininess / 128.0), + length(specular), + DEFAULT_EMISSIVE, + DEFAULT_OCCLUSION, + DEFAULT_SCATTERING); + } } } diff --git a/libraries/render-utils/src/simple_textured_unlit.slf b/libraries/render-utils/src/simple_textured_unlit.slf index cbfc7d7768..296f805902 100644 --- a/libraries/render-utils/src/simple_textured_unlit.slf +++ b/libraries/render-utils/src/simple_textured_unlit.slf @@ -29,8 +29,17 @@ void main(void) { texel = colorToLinearRGBA(texel); } - packDeferredFragmentUnlit( - normalize(_normal), - texel.a, - _color.rgb * texel.rgb); + if (_color.a * texel.a < 1.0) { + packDeferredFragmentTranslucent( + normalize(_normal), + _color.a * texel.a, + _color.rgb * texel.rgb, + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); + } else { + packDeferredFragmentUnlit( + normalize(_normal), + 1.0, + _color.rgb * texel.rgb); + } } \ No newline at end of file From 7713c1f4bfb664cf9d3e322367e219fe049a24f8 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 3 Aug 2016 15:56:41 -0700 Subject: [PATCH 026/279] try to fade in web entities --- libraries/entities-renderer/src/RenderableEntityItem.cpp | 2 +- .../entities-renderer/src/RenderablePolyLineEntityItem.h | 2 ++ .../entities-renderer/src/RenderableWebEntityItem.cpp | 8 ++++++-- libraries/entities-renderer/src/RenderableWebEntityItem.h | 5 +++++ libraries/render-utils/src/simple.slf | 3 ++- libraries/render-utils/src/simple_textured.slf | 3 ++- libraries/render-utils/src/simple_textured_unlit.slf | 3 ++- 7 files changed, 20 insertions(+), 6 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index d49eacdf7b..bd34506250 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -19,7 +19,7 @@ namespace render { if (payload->entity->getType() == EntityTypes::Light) { return ItemKey::Builder::light(); } - if (payload && (payload->entity->getType() == EntityTypes::PolyLine || payload->entity->isTransparent())) { + if (payload && payload->entity->isTransparent()) { return ItemKey::Builder::transparentShape(); } } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index dfde97c407..46716e5bab 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -32,6 +32,8 @@ public: virtual void update(const quint64& now) override; virtual bool needsToCallUpdate() const override { return true; }; + bool isTransparent() override { return true; } + SIMPLE_RENDERABLE(); NetworkTexturePointer _texture; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index d86aa8e2f9..bb5cb7e2f8 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -181,6 +181,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { if (!buildWebSurface(static_cast(args->_renderer))) { return; } + _fadeStartTime = usecTimestampNow(); #endif } @@ -208,10 +209,13 @@ void RenderableWebEntityItem::render(RenderArgs* args) { batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture); textured = emissive = true; } - bool transparent = false; + + float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime); + bool transparent = fadeRatio < 1.0f; + batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); DependencyManager::get()->bindSimpleProgram(batch, textured, transparent, culled, emissive); - DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, 0.0f)); + DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); } void RenderableWebEntityItem::setSourceUrl(const QString& value) { diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 4125be61dd..049575ec95 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -12,6 +12,7 @@ #include #include +#include #include "RenderableEntityItem.h" @@ -35,6 +36,8 @@ public: void update(const quint64& now) override; bool needsToCallUpdate() const override { return _webSurface != nullptr; } + bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } + SIMPLE_RENDERABLE(); private: @@ -53,6 +56,8 @@ private: QMetaObject::Connection _mouseReleaseConnection; QMetaObject::Connection _mouseMoveConnection; QMetaObject::Connection _hoverLeaveConnection; + + quint64 _fadeStartTime; }; diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index fff91a9261..85d85b3db7 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -49,7 +49,8 @@ void main(void) { #endif - if (_color.a < 1.0) { + const float ALPHA_THRESHOLD = 0.999; + if (_color.a < ALPHA_THRESHOLD) { if (emissiveAmount > 0.0) { // TODO: transparent emissive? packDeferredFragmentTranslucent( diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf index 815d28310f..6067c81a1b 100644 --- a/libraries/render-utils/src/simple_textured.slf +++ b/libraries/render-utils/src/simple_textured.slf @@ -30,7 +30,8 @@ void main(void) { texel = colorToLinearRGBA(texel); } - if (_color.a * texel.a < 1.0) { + const float ALPHA_THRESHOLD = 0.999; + if (_color.a * texel.a < ALPHA_THRESHOLD) { packDeferredFragmentTranslucent( normalize(_normal), _color.a * texel.a, diff --git a/libraries/render-utils/src/simple_textured_unlit.slf b/libraries/render-utils/src/simple_textured_unlit.slf index 296f805902..4f02140825 100644 --- a/libraries/render-utils/src/simple_textured_unlit.slf +++ b/libraries/render-utils/src/simple_textured_unlit.slf @@ -29,7 +29,8 @@ void main(void) { texel = colorToLinearRGBA(texel); } - if (_color.a * texel.a < 1.0) { + const float ALPHA_THRESHOLD = 0.999; + if (_color.a * texel.a < ALPHA_THRESHOLD) { packDeferredFragmentTranslucent( normalize(_normal), _color.a * texel.a, From d7052f6250ec5f825b8656e4e3650f988b49b355 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 3 Aug 2016 16:27:51 -0700 Subject: [PATCH 027/279] try to make text entities fade --- .../entities-renderer/src/RenderableTextEntityItem.cpp | 10 +++++----- .../entities-renderer/src/RenderableTextEntityItem.h | 4 ++++ libraries/render-utils/src/sdf_text3D.slf | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 7f2644a68e..11c5f21a59 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -15,8 +15,6 @@ #include #include - - #include "RenderableTextEntityItem.h" #include "GLMHelpers.h" @@ -31,8 +29,10 @@ void RenderableTextEntityItem::render(RenderArgs* args) { Q_ASSERT(getType() == EntityTypes::Text); static const float SLIGHTLY_BEHIND = -0.005f; - glm::vec4 textColor = glm::vec4(toGlm(getTextColorX()), 1.0f); - glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), 1.0f); + float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime); + bool transparent = fadeRatio < 1.0f; + glm::vec4 textColor = glm::vec4(toGlm(getTextColorX()), fadeRatio); + glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), fadeRatio); glm::vec3 dimensions = getDimensions(); // Render background @@ -62,7 +62,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) { batch.setModelTransform(transformToTopLeft); - DependencyManager::get()->bindSimpleProgram(batch, false, false, false, false, true); + DependencyManager::get()->bindSimpleProgram(batch, false, transparent, false, false, true); DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, backgroundColor); float scale = _lineHeight / _textRenderer->getFontSize(); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index cbe2b11c27..e0ddcad266 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -14,6 +14,7 @@ #include #include +#include #include "RenderableEntityItem.h" @@ -27,10 +28,13 @@ public: virtual void render(RenderArgs* args) override; + bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } + SIMPLE_RENDERABLE(); private: TextRenderer3D* _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f); + quint64 _fadeStartTime { usecTimestampNow() }; }; diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf index f5385c23b7..f578895c85 100644 --- a/libraries/render-utils/src/sdf_text3D.slf +++ b/libraries/render-utils/src/sdf_text3D.slf @@ -48,7 +48,7 @@ void main() { packDeferredFragmentTranslucent( normalize(_normal), - a, + a * Color.a, Color.rgb, DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); From d521315475587075d7cd9b146ccfbd0d499aefb4 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 3 Aug 2016 17:03:52 -0700 Subject: [PATCH 028/279] fade polylines (needs testing) --- .../entities-renderer/src/RenderablePolyLineEntityItem.cpp | 2 ++ .../entities-renderer/src/RenderablePolyLineEntityItem.h | 7 ++++--- libraries/entities-renderer/src/paintStroke.slf | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 600b876d39..54a6edadd4 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -204,5 +204,7 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); + batch._glColor4f(1.0f, 1.0f, 1.0f, Interpolate::calculateFadeRatio(_fadeStartTime)); + batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0); }; diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 46716e5bab..6d3dab4647 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -18,6 +18,7 @@ #include #include "RenderableEntityItem.h" #include +#include #include @@ -30,9 +31,9 @@ public: virtual void render(RenderArgs* args) override; virtual void update(const quint64& now) override; - virtual bool needsToCallUpdate() const override { return true; }; + virtual bool needsToCallUpdate() const override { return true; } - bool isTransparent() override { return true; } + bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } SIMPLE_RENDERABLE(); @@ -49,7 +50,7 @@ protected: gpu::BufferView _uniformBuffer; unsigned int _numVertices; QVector _vertices; - + quint64 _fadeStartTime { usecTimestampNow() }; }; diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 9b7193bbfc..bfbe6d7e5a 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -39,7 +39,7 @@ void main(void) { vec3 color = varColor.rgb; packDeferredFragmentTranslucent( interpolatedNormal * frontCondition, - texel.a, + texel.a * varColor.a, polyline.color * texel.rgb, vec3(0.01, 0.01, 0.01), 10.0); From 6604c27f232f61a842cc78ccf775a81cf858e200 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 4 Aug 2016 10:55:37 -0700 Subject: [PATCH 029/279] polyvox fade WIP --- .../src/RenderableEntityItem.cpp | 2 +- .../src/RenderablePolyLineEntityItem.h | 2 +- .../src/RenderablePolyVoxEntityItem.cpp | 6 +++++ .../src/RenderablePolyVoxEntityItem.h | 2 ++ libraries/entities-renderer/src/polyvox.slf | 23 +++++++++++++++++-- 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index bd34506250..359b050803 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -1,4 +1,4 @@ - // +// // RenderableEntityItem.cpp // interface/src // diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 6d3dab4647..c06f4a721a 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -33,7 +33,7 @@ public: virtual void update(const quint64& now) override; virtual bool needsToCallUpdate() const override { return true; } - bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } + bool isTransparent() override { return true; } SIMPLE_RENDERABLE(); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index eb6db2874f..769670b99c 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -596,6 +596,9 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, true, gpu::LESS_EQUAL); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); _pipeline = gpu::Pipeline::create(program, state); } @@ -642,6 +645,9 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { int voxelVolumeSizeLocation = _pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize"); batch._glUniform3f(voxelVolumeSizeLocation, voxelVolumeSize.x, voxelVolumeSize.y, voxelVolumeSize.z); + int alphaLocation = _pipeline->getProgram()->getUniforms().findLocation("alpha"); + batch._glUniform1f(alphaLocation, 0.5f); + batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)mesh->getNumIndices(), 0); } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index c46a26deb5..cec6ddf7c5 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -131,6 +131,8 @@ public: void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; }); } + bool isTransparent() override { return true; } + private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions // may not match _voxelVolumeSize. diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf index b7682913a7..a3c8315b62 100644 --- a/libraries/entities-renderer/src/polyvox.slf +++ b/libraries/entities-renderer/src/polyvox.slf @@ -1,7 +1,7 @@ <@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> -// model.frag +// polyvox.frag // fragment shader // // Created by Seth Alves on 2015-8-3 @@ -23,6 +23,7 @@ uniform sampler2D xMap; uniform sampler2D yMap; uniform sampler2D zMap; uniform vec3 voxelVolumeSize; +uniform float alpha; void main(void) { vec3 worldNormal = cross(dFdy(_worldPosition.xyz), dFdx(_worldPosition.xyz)); @@ -41,5 +42,23 @@ void main(void) { vec3 yzDiffuseScaled = yzDiffuse.rgb * abs(worldNormal.x); vec4 diffuse = vec4(xyDiffuseScaled + xzDiffuseScaled + yzDiffuseScaled, 1.0); - packDeferredFragment(_normal, 1.0, vec3(diffuse), DEFAULT_ROUGHNESS, DEFAULT_METALLIC, DEFAULT_EMISSIVE, DEFAULT_OCCLUSION, DEFAULT_SCATTERING); + const float ALPHA_THRESHOLD = 0.999; + if (alpha < ALPHA_THRESHOLD) { + packDeferredFragmentTranslucent( + _normal, + alpha, + vec3(diffuse), + DEFAULT_FRESNEL, + DEFAULT_ROUGHNESS); + } else { + packDeferredFragment( + _normal, + 1.0, + vec3(diffuse), + DEFAULT_ROUGHNESS, + DEFAULT_METALLIC, + DEFAULT_EMISSIVE, + DEFAULT_OCCLUSION, + DEFAULT_SCATTERING); + } } From ac9a80131a6780e5c223a970357e146bab90b7a2 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 4 Aug 2016 12:34:46 -0700 Subject: [PATCH 030/279] refactoring _fadeStartTime and isTransparent, make simple renderables switch to transparent when fade finishes --- libraries/entities-renderer/src/RenderableEntityItem.h | 10 +++++++++- .../entities-renderer/src/RenderableModelEntityItem.h | 3 +++ .../src/RenderablePolyLineEntityItem.cpp | 2 ++ .../src/RenderablePolyLineEntityItem.h | 2 -- .../src/RenderableShapeEntityItem.cpp | 7 ++++--- .../entities-renderer/src/RenderableShapeEntityItem.h | 6 ++---- .../entities-renderer/src/RenderableTextEntityItem.cpp | 1 + .../entities-renderer/src/RenderableTextEntityItem.h | 4 ---- .../entities-renderer/src/RenderableWebEntityItem.cpp | 2 ++ .../entities-renderer/src/RenderableWebEntityItem.h | 5 ----- libraries/entities/src/EntityItem.h | 5 +++-- 11 files changed, 26 insertions(+), 21 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 9840bf3150..09d6d88c6a 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -96,8 +96,16 @@ public: \ virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override { _renderHelper.removeFromScene(self, scene, pendingChanges); } \ virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \ virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \ + void checkTransparency() { \ + bool transparent = isTransparent(); \ + if (transparent != prevIsTransparent) { \ + _renderHelper.notifyChanged(); \ + prevIsTransparent = transparent; \ + } \ + } \ private: \ - SimpleRenderableEntityItem _renderHelper; + SimpleRenderableEntityItem _renderHelper; \ + bool prevIsTransparent { isTransparent() }; #endif // hifi_RenderableEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 339c907532..c50dcde62c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -91,6 +91,9 @@ public: render::ItemID getMetaRenderItem() { return _myMetaItem; } + // Transparency is handled in ModelMeshPartPayload + bool isTransparent() override { return false; } + private: QVariantMap parseTexturesToMap(QString textures); void remapTextures(); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 54a6edadd4..ef4c9d6b5d 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -167,6 +167,8 @@ void RenderablePolyLineEntityItem::update(const quint64& now) { } void RenderablePolyLineEntityItem::render(RenderArgs* args) { + checkTransparency(); + QWriteLocker lock(&_quadReadWriteLock); if (_points.size() < 2 || _normals.size () < 2 || _strokeWidths.size() < 2) { return; diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index c06f4a721a..75b2bcd58a 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -18,7 +18,6 @@ #include #include "RenderableEntityItem.h" #include -#include #include @@ -50,7 +49,6 @@ protected: gpu::BufferView _uniformBuffer; unsigned int _numVertices; QVector _vertices; - quint64 _fadeStartTime { usecTimestampNow() }; }; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 352777e958..5bfd669a7c 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -71,18 +71,19 @@ void RenderableShapeEntityItem::setUserData(const QString& value) { } } -bool RenderableShapeEntityItem::isTransparent() { +/*bool RenderableShapeEntityItem::isTransparent() { if (_procedural && _procedural->ready()) { return Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f; } else { - return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; + return EntityItem::isTransparent(); } -} +}*/ void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); //Q_ASSERT(getType() == EntityTypes::Shape); Q_ASSERT(args->_batch); + checkTransparency(); if (!_procedural) { _procedural.reset(new Procedural(getUserData())); diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 716daf1d03..68b36f7e45 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -11,7 +11,6 @@ #include #include -#include #include "RenderableEntityItem.h" @@ -22,18 +21,17 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties); - RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) {} + RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) { _procedural.reset(nullptr); } void render(RenderArgs* args) override; void setUserData(const QString& value) override; - bool isTransparent() override; +// bool isTransparent() override; SIMPLE_RENDERABLE(); private: QSharedPointer _procedural; - quint64 _fadeStartTime { usecTimestampNow() }; }; diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 11c5f21a59..ccdaa39bdd 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -27,6 +27,7 @@ EntityItemPointer RenderableTextEntityItem::factory(const EntityItemID& entityID void RenderableTextEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableTextEntityItem::render"); Q_ASSERT(getType() == EntityTypes::Text); + checkTransparency(); static const float SLIGHTLY_BEHIND = -0.005f; float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h index e0ddcad266..cbe2b11c27 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.h +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h @@ -14,7 +14,6 @@ #include #include -#include #include "RenderableEntityItem.h" @@ -28,13 +27,10 @@ public: virtual void render(RenderArgs* args) override; - bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } - SIMPLE_RENDERABLE(); private: TextRenderer3D* _textRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f); - quint64 _fadeStartTime { usecTimestampNow() }; }; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index bb5cb7e2f8..19388a28e4 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -164,6 +164,8 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { } void RenderableWebEntityItem::render(RenderArgs* args) { + checkTransparency(); + #ifdef WANT_EXTRA_DEBUGGING { gpu::Batch& batch = *args->_batch; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 049575ec95..4125be61dd 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -12,7 +12,6 @@ #include #include -#include #include "RenderableEntityItem.h" @@ -36,8 +35,6 @@ public: void update(const quint64& now) override; bool needsToCallUpdate() const override { return _webSurface != nullptr; } - bool isTransparent() override { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } - SIMPLE_RENDERABLE(); private: @@ -56,8 +53,6 @@ private: QMetaObject::Connection _mouseReleaseConnection; QMetaObject::Connection _mouseMoveConnection; QMetaObject::Connection _hoverLeaveConnection; - - quint64 _fadeStartTime; }; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index f1715a2525..198928da4e 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "EntityItemID.h" #include "EntityItemPropertiesDefaults.h" @@ -435,7 +436,7 @@ public: QUuid getOwningAvatarID() const { return _owningAvatarID; } void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } - virtual bool isTransparent() { return false; } + virtual bool isTransparent() { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } protected: @@ -566,7 +567,7 @@ protected: quint64 _lastUpdatedAngularVelocityTimestamp { 0 }; quint64 _lastUpdatedAccelerationTimestamp { 0 }; - + quint64 _fadeStartTime { usecTimestampNow() }; }; #endif // hifi_EntityItem_h From 004b0158a4683248f8c05c8fc470571cf0260425 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 4 Aug 2016 13:09:09 -0700 Subject: [PATCH 031/279] fix warnings on osx --- .../src/RenderablePolyVoxEntityItem.h | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index cec6ddf7c5..615451180a 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -50,7 +50,7 @@ public: void initializePolyVox(); - virtual void somethingChangedNotification() { + virtual void somethingChangedNotification() override { // This gets called from EnityItem::readEntityDataFromBuffer every time a packet describing // this entity comes from the entity-server. It gets called even if nothing has actually changed // (see the comment in EntityItem.cpp). If that gets fixed, this could be used to know if we @@ -58,19 +58,19 @@ public: // _needsModelReload = true; } - virtual uint8_t getVoxel(int x, int y, int z); - virtual bool setVoxel(int x, int y, int z, uint8_t toValue); + virtual uint8_t getVoxel(int x, int y, int z) override; + virtual bool setVoxel(int x, int y, int z, uint8_t toValue) override; - void render(RenderArgs* args); - virtual bool supportsDetailedRayIntersection() const { return true; } + void render(RenderArgs* args) override; + virtual bool supportsDetailedRayIntersection() const override { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const; + void** intersectedObject, bool precisionPicking) const override; - virtual void setVoxelData(QByteArray voxelData); - virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize); - virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle); + virtual void setVoxelData(QByteArray voxelData) override; + virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize) override; + virtual void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) override; glm::vec3 getSurfacePositionAdjustment() const; glm::mat4 voxelToWorldMatrix() const; @@ -78,45 +78,45 @@ public: glm::mat4 voxelToLocalMatrix() const; glm::mat4 localToVoxelMatrix() const; - virtual ShapeType getShapeType() const; - virtual bool shouldBePhysical() const { return !isDead(); } - virtual bool isReadyToComputeShape(); - virtual void computeShapeInfo(ShapeInfo& info); + virtual ShapeType getShapeType() const override; + virtual bool shouldBePhysical() const override { return !isDead(); } + virtual bool isReadyToComputeShape() override; + virtual void computeShapeInfo(ShapeInfo& info) override; - virtual glm::vec3 voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const; - virtual glm::vec3 worldCoordsToVoxelCoords(glm::vec3& worldCoords) const; - virtual glm::vec3 voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const; - virtual glm::vec3 localCoordsToVoxelCoords(glm::vec3& localCoords) const; + virtual glm::vec3 voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const override; + virtual glm::vec3 worldCoordsToVoxelCoords(glm::vec3& worldCoords) const override; + virtual glm::vec3 voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const override; + virtual glm::vec3 localCoordsToVoxelCoords(glm::vec3& localCoords) const override; // coords are in voxel-volume space - virtual bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue); - virtual bool setVoxelInVolume(glm::vec3 position, uint8_t toValue); + virtual bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) override; + virtual bool setVoxelInVolume(glm::vec3 position, uint8_t toValue) override; // coords are in world-space - virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue); - virtual bool setAll(uint8_t toValue); - virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue); + virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue) override; + virtual bool setAll(uint8_t toValue) override; + virtual bool setCuboid(const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int toValue) override; - virtual void setXTextureURL(QString xTextureURL); - virtual void setYTextureURL(QString yTextureURL); - virtual void setZTextureURL(QString zTextureURL); + virtual void setXTextureURL(QString xTextureURL) override; + virtual void setYTextureURL(QString yTextureURL) override; + virtual void setZTextureURL(QString zTextureURL) override; virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, - render::PendingChanges& pendingChanges); + render::PendingChanges& pendingChanges) override; virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, - render::PendingChanges& pendingChanges); + render::PendingChanges& pendingChanges) override; - virtual void setXNNeighborID(const EntityItemID& xNNeighborID); - virtual void setYNNeighborID(const EntityItemID& yNNeighborID); - virtual void setZNNeighborID(const EntityItemID& zNNeighborID); + virtual void setXNNeighborID(const EntityItemID& xNNeighborID) override; + virtual void setYNNeighborID(const EntityItemID& yNNeighborID) override; + virtual void setZNNeighborID(const EntityItemID& zNNeighborID) override; - virtual void setXPNeighborID(const EntityItemID& xPNeighborID); - virtual void setYPNeighborID(const EntityItemID& yPNeighborID); - virtual void setZPNeighborID(const EntityItemID& zPNeighborID); + virtual void setXPNeighborID(const EntityItemID& xPNeighborID) override; + virtual void setYPNeighborID(const EntityItemID& yPNeighborID) override; + virtual void setZPNeighborID(const EntityItemID& zPNeighborID) override; - virtual void updateRegistrationPoint(const glm::vec3& value); + virtual void updateRegistrationPoint(const glm::vec3& value) override; void setVoxelsFromData(QByteArray uncompressedData, quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize); void forEachVoxelValue(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize, @@ -163,7 +163,7 @@ private: // these are run off the main thread void decompressVolumeData(); void compressVolumeDataAndSendEditPacket(); - virtual void getMesh(); // recompute mesh + virtual void getMesh() override; // recompute mesh void computeShapeInfoWorker(); // these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID From 3107d63ad8db677c70afc23c9f8c91883ac380a0 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 4 Aug 2016 14:31:44 -0700 Subject: [PATCH 032/279] one more warning --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 9cd55b7a75..b1370e72a7 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -211,7 +211,6 @@ void RenderableWebEntityItem::render(RenderArgs* args) { } float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime); - bool transparent = fadeRatio < 1.0f; batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); DependencyManager::get()->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch); From 8044910bf2bd8b35770cfc2872c5b5595089e31f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 4 Aug 2016 14:39:53 -0700 Subject: [PATCH 033/279] Add show-address-bar when disconnected from domain --- interface/src/Application.cpp | 1 + interface/src/Application.h | 4 ++ interface/src/ConnectionMonitor.cpp | 58 +++++++++++++++++++++++++++++ interface/src/ConnectionMonitor.h | 34 +++++++++++++++++ interface/src/ui/DialogsManager.cpp | 4 ++ interface/src/ui/DialogsManager.h | 1 + 6 files changed, 102 insertions(+) create mode 100644 interface/src/ConnectionMonitor.cpp create mode 100644 interface/src/ConnectionMonitor.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5d50a1c9fe..544458f9b8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -811,6 +811,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } UserActivityLogger::getInstance().logAction("launch", properties); + _connectionMonitor.init(); // Tell our entity edit sender about our known jurisdictions _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions); diff --git a/interface/src/Application.h b/interface/src/Application.h index 0af65f665f..c81d56e0aa 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -47,6 +47,7 @@ #include "avatar/MyAvatar.h" #include "Bookmarks.h" #include "Camera.h" +#include "ConnectionMonitor.h" #include "FileLogger.h" #include "gpu/Context.h" #include "Menu.h" @@ -562,6 +563,9 @@ private: bool _recentlyClearedDomain { false }; QString _returnFromFullScreenMirrorTo; + + ConnectionMonitor _connectionMonitor; }; + #endif // hifi_Application_h diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp new file mode 100644 index 0000000000..462efc1bf9 --- /dev/null +++ b/interface/src/ConnectionMonitor.cpp @@ -0,0 +1,58 @@ +// +// ConnectionMonitor.cpp +// interface/src +// +// Created by Ryan Huffman on 8/4/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ConnectionMonitor.h" + +#include "ui/DialogsManager.h" + +#include +#include +#include +#include + +static const int DISPLAY_AFTER_DISCONNECTED_FOR_X_MS = 5000; + +void ConnectionMonitor::init() { + // Connect to domain disconnected message + auto nodeList = DependencyManager::get(); + const DomainHandler& domainHandler = nodeList->getDomainHandler(); + connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &ConnectionMonitor::disconnectedFromDomain); + connect(&domainHandler, &DomainHandler::connectedToDomain, this, &ConnectionMonitor::connectedToDomain); + + // Connect to AddressManager::hostChanged + auto addressManager = DependencyManager::get(); + connect(addressManager.data(), &AddressManager::hostChanged, this, &ConnectionMonitor::hostChanged); + + _timer.setSingleShot(true); + _timer.setInterval(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS); + _timer.start(); + + connect(&_timer, &QTimer::timeout, this, []() { + qDebug() << "CM: Showing address bar!"; + DependencyManager::get()->showAddressBar(); + }); +} + +void ConnectionMonitor::disconnectedFromDomain() { + qDebug() << "CM: DISCONNECTED FROM DOMAIN!"; + _timer.start(); +} + +void ConnectionMonitor::connectedToDomain(const QString& name) { + qDebug() << "CM: CONNECTED FROM DOMAIN! - " << name; + _timer.stop(); +} + +void ConnectionMonitor::hostChanged(const QString& name) { + qDebug() << "CM: host changed: " << name; + _timer.start(); + +} diff --git a/interface/src/ConnectionMonitor.h b/interface/src/ConnectionMonitor.h new file mode 100644 index 0000000000..bba420715e --- /dev/null +++ b/interface/src/ConnectionMonitor.h @@ -0,0 +1,34 @@ +// +// ConnectionMonitor.h +// interface/src +// +// Created by Ryan Huffman on 8/4/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ConnectionMonitor_h +#define hifi_ConnectionMonitor_h + +#include +#include + +class QString; + +class ConnectionMonitor : public QObject { + Q_OBJECT +public: + void init(); + +private slots: + void disconnectedFromDomain(); + void connectedToDomain(const QString& name); + void hostChanged(const QString& name); + +private: + QTimer _timer; +}; + +#endif // hifi_ConnectionMonitor_h \ No newline at end of file diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 2f826146ae..dc06c50626 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -50,6 +50,10 @@ void DialogsManager::toggleAddressBar() { emit addressBarToggled(); } +void DialogsManager::showAddressBar() { + AddressBarDialog::show(); +} + void DialogsManager::toggleDiskCacheEditor() { maybeCreateDialog(_diskCacheEditor); _diskCacheEditor->toggle(); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index c48c6df0e6..5b4995029f 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -44,6 +44,7 @@ public: public slots: void toggleAddressBar(); + void showAddressBar(); void toggleDiskCacheEditor(); void toggleLoginDialog(); void showLoginDialog(); From a37bcdafdaa9ebd5c5cab24041088a29ed8046a8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 4 Aug 2016 14:40:41 -0700 Subject: [PATCH 034/279] Remove debug logging in ConnectionMonitor --- interface/src/ConnectionMonitor.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp index 462efc1bf9..be1ffb41e8 100644 --- a/interface/src/ConnectionMonitor.cpp +++ b/interface/src/ConnectionMonitor.cpp @@ -42,17 +42,13 @@ void ConnectionMonitor::init() { } void ConnectionMonitor::disconnectedFromDomain() { - qDebug() << "CM: DISCONNECTED FROM DOMAIN!"; _timer.start(); } void ConnectionMonitor::connectedToDomain(const QString& name) { - qDebug() << "CM: CONNECTED FROM DOMAIN! - " << name; _timer.stop(); } void ConnectionMonitor::hostChanged(const QString& name) { - qDebug() << "CM: host changed: " << name; _timer.start(); - } From d775a92dabf9f0e6b530f1abbf6d0fdd61dc035d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 4 Aug 2016 14:45:10 -0700 Subject: [PATCH 035/279] Update implementation of ConnectionMonitor timeout --- interface/src/ConnectionMonitor.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp index be1ffb41e8..1956ace67b 100644 --- a/interface/src/ConnectionMonitor.cpp +++ b/interface/src/ConnectionMonitor.cpp @@ -35,10 +35,8 @@ void ConnectionMonitor::init() { _timer.setInterval(DISPLAY_AFTER_DISCONNECTED_FOR_X_MS); _timer.start(); - connect(&_timer, &QTimer::timeout, this, []() { - qDebug() << "CM: Showing address bar!"; - DependencyManager::get()->showAddressBar(); - }); + auto dialogsManager = DependencyManager::get(); + connect(&_timer, &QTimer::timeout, dialogsManager.data(), &DialogsManager::showAddressBar); } void ConnectionMonitor::disconnectedFromDomain() { From c0cfee371e1923b1b201b3d7fa3c65fcfe0f025d Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 4 Aug 2016 16:08:51 -0700 Subject: [PATCH 036/279] cancel mode for teleporting --- .../system/assets/models/teleport-cancel.fbx | Bin 0 -> 177324 bytes .../assets/models/teleport-destination.fbx | Bin 0 -> 183372 bytes scripts/system/assets/models/teleport.fbx | Bin 189244 -> 0 bytes scripts/system/controllers/teleport.js | 68 +++++++++++++++--- 4 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 scripts/system/assets/models/teleport-cancel.fbx create mode 100644 scripts/system/assets/models/teleport-destination.fbx delete mode 100644 scripts/system/assets/models/teleport.fbx diff --git a/scripts/system/assets/models/teleport-cancel.fbx b/scripts/system/assets/models/teleport-cancel.fbx new file mode 100644 index 0000000000000000000000000000000000000000..1c12e28159056cadaafb840f7e449e683489d311 GIT binary patch literal 177324 zcmdSC2UHZx6E8gEpn{kK3Zj_FBAF#45(U8sxWE7_o7`P;G@_yc=753`GiGuy5){G6 zRV1iLmYiYXJ3TYNAS`gb|MR`~=6F2A%+#-|s;jH3yQ_OmiM}pG22s*TZ=<9hiArSn zN=ia=ph|HFau9$9t|>-(8`r=@7not_!-Avkg7_c^nmq8@4rYLtCN(?| zUv)~YywK%381k{OrHT0k+&oYY{Mc_=y^D5 zfdN<^2>OM4!Mh12GE7PZAgBlTl+T<-Wx1OOLJ%azH7Eg(0nyil7lNQAxQD!JXr2s{ z$q)n)a8LQoNmNf3Y{Cse&<6Z-8`zmfbup2FAc%!~KEa$sCX<-x%a(qj`mZePOC<<` z1Q0=K3>#Spg7nBR)dglqE@iTaPGlIVArs4Gk}{HtvMQ@3mohz_B;~=MO0uhE71s$s z5MXEt1VKVJ&M*}=p}Nv+rb7^9Or|*z$>2|jYw!(FfGrbd7!X;+h1{qjCP5Iy3#wqq zTTSdOsOSM@yFd^$k*8k>2of}hSww)fozvGlaWDARlUQWf29Te{Jr=O>blMI(vyc}L za4-0EJz4HF2J-w3?zx~T?CVWqxG<47<@h(&uonqfBJ!pW_eMaUMxnq|w2&c}0nXqj zS}|yJn86~!Oci-D4{GLWz>chJCO{BmKy&s4D7Fl;4IneMVKGQlHyfZ5@C*PWAIiDX zXih|itgDmH6AVTZg%JUtFr5De379%C)0VBHqUPZqs-mY#KQ`HdAljmg+| zK=O%0dOM&sU}ZA$3NrEvk_wousmsgDa|@#2|FP!?0nG!uM9{v%24=BHR5#{otb`$+ zANU?7j%J9b+$V_Yd>N{9e&7x1x;`W(;uuL(7Scpbs4UnGM%}py=;)tfWT5Nhva%5# zBqO;|M&sVN_^klpMJ9Kgh@Az!!s1MIl5%Wl>2i z8)qUJHX=HM_yyFv3(eDs3?sJ|o)jmnx(!fEYanD|C^{$#u=4>%{yo~f2@nLjVa$U7 z^VFp{kzguIpGKxJFo#K|F|3XCY=AkVj{!3nMh0HmkxSu(AAjJTwoi}o5NI3%3wER0y>0J zj0CiC%tnzQm`6n`kqV{jbg+gQebN$3$5W%D_a%RF^B5`P%3r_^@l>tI}-;Zb3T2VCzXY`iK~+! z2n1O3K)~7mu^B6KBJdj!A`24W4&M3U;D

m6Oo!b@8BHY-pYgXV?;rK!F9*27^&V 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

pf4{QakR4SQ?k?U>@n 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%0A+wcw$vE7a;M|kN2C>zu;{TX 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

y*>ar`ls37 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®O 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÷SWV1jnw>IQ5?vc<$}{I8o)&bY&=BvyF4F+rpz~ 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 M{z5JVq+Fr=Kk4v^F#rGn literal 0 HcmV?d00001 diff --git a/scripts/system/assets/models/teleport.fbx b/scripts/system/assets/models/teleport.fbx deleted file mode 100644 index 831f152add043dcdb04c4a567037167ba434eb97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 189244 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 diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 5cd8460172..4699dfe64a 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -12,7 +12,7 @@ var inTeleportMode = false; // var NUMBER_OF_STEPS = 0; // var SMOOTH_ARRIVAL_SPACING = 0; -// // slow +// // slow n // var SMOOTH_ARRIVAL_SPACING = 150; // var NUMBER_OF_STEPS = 2; @@ -28,7 +28,8 @@ var NUMBER_OF_STEPS = 6; // var SMOOTH_ARRIVAL_SPACING = 10; // var NUMBER_OF_STEPS = 20; -var TARGET_MODEL_URL = Script.resolvePath("../assets/models/teleport.fbx"); +var TARGET_MODEL_URL = Script.resolvePath("../assets/models/teleport-destination.fbx"); +var TOO_CLOSE_MODEL_URL = Script.resolvePath("../assets/models/teleport-cancel.fbx"); var TARGET_MODEL_DIMENSIONS = { x: 1.15, y: 0.5, @@ -47,7 +48,14 @@ var COLORS_TELEPORT_CANNOT_TELEPORT = { blue: 141 }; -var MAX_AVATAR_SPEED = 0.25; +var COLORS_TELEPORT_TOO_CLOSE = { + red: 255, + green: 184, + blue: 73 +}; + + +var TELEPORT_CANCEL_RANGE = 1.25; function ThumbPad(hand) { this.hand = hand; @@ -89,6 +97,7 @@ function Teleporter() { this.updateConnected = null; this.smoothArrivalInterval = null; this.teleportHand = null; + this.tooClose=false; this.initialize = function() { this.createMappings(); @@ -235,7 +244,12 @@ function Teleporter() { var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity]); if (rightIntersection.intersects) { - this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); + if (this.tooClose===true) { + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); + } else { + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); + } + if (this.targetOverlay !== null) { this.updateTargetOverlay(rightIntersection); } else { @@ -275,7 +289,14 @@ function Teleporter() { if (leftIntersection.intersects) { - this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); + if (this.tooClose===true) { + this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); + + } else { + this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); + } + + if (this.targetOverlay !== null) { this.updateTargetOverlay(leftIntersection); } else { @@ -362,10 +383,23 @@ function Teleporter() { y: intersection.intersection.y + TARGET_MODEL_DIMENSIONS.y / 2, z: intersection.intersection.z } - Overlays.editOverlay(this.targetOverlay, { - position: position, - rotation: Quat.fromPitchYawRollDegrees(0, euler.y, 0), - }); + + var tooClose = isTooCloseToTeleport(position); + this.tooClose=tooClose; + if (tooClose === false) { + Overlays.editOverlay(this.targetOverlay, { + url: TARGET_MODEL_URL, + position: position, + rotation: Quat.fromPitchYawRollDegrees(0, euler.y, 0), + }); + } + if (tooClose === true) { + Overlays.editOverlay(this.targetOverlay, { + url: TOO_CLOSE_MODEL_URL, + position: position, + rotation: Quat.fromPitchYawRollDegrees(0, euler.y, 0), + }); + } }; @@ -383,10 +417,17 @@ function Teleporter() { }; this.teleport = function(value) { + if (value === undefined) { this.exitTeleportMode(); } + if (this.intersection !== null) { + if (isTooCloseToTeleport(this.intersection.intersection)) { + this.deleteTargetOverlay(); + this.exitTeleportMode(); + return; + } var offset = getAvatarFootOffset(); this.intersection.intersection.y += offset; this.exitTeleportMode(); @@ -507,6 +548,15 @@ function isMoving() { } } +function isTooCloseToTeleport(position) { + var distance = Vec3.distance(MyAvatar.position, position); + if (distance <= TELEPORT_CANCEL_RANGE) { + return true + } else { + return false + } +} + function registerMappings() { mappingName = 'Hifi-Teleporter-Dev-' + Math.random(); teleportMapping = Controller.newMapping(mappingName); From f93ed561241b7fa3843e09b838f517c2bfa108a2 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 4 Aug 2016 16:17:21 -0700 Subject: [PATCH 037/279] cleaner code --- scripts/system/controllers/teleport.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 4699dfe64a..bd77ffd2a1 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -97,7 +97,7 @@ function Teleporter() { this.updateConnected = null; this.smoothArrivalInterval = null; this.teleportHand = null; - this.tooClose=false; + this.tooClose = false; this.initialize = function() { this.createMappings(); @@ -244,7 +244,7 @@ function Teleporter() { var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity]); if (rightIntersection.intersects) { - if (this.tooClose===true) { + if (this.tooClose === true) { this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); } else { this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); @@ -289,7 +289,7 @@ function Teleporter() { if (leftIntersection.intersects) { - if (this.tooClose===true) { + if (this.tooClose === true) { this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); } else { @@ -385,7 +385,7 @@ function Teleporter() { } var tooClose = isTooCloseToTeleport(position); - this.tooClose=tooClose; + this.tooClose = tooClose; if (tooClose === false) { Overlays.editOverlay(this.targetOverlay, { url: TARGET_MODEL_URL, @@ -549,12 +549,7 @@ function isMoving() { } function isTooCloseToTeleport(position) { - var distance = Vec3.distance(MyAvatar.position, position); - if (distance <= TELEPORT_CANCEL_RANGE) { - return true - } else { - return false - } + return Vec3.distance(MyAvatar.position, position) <= TELEPORT_CANCEL_RANGE; } function registerMappings() { From 63aa0f8c3f7362715ad73fdd812c6f2035e20ab5 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 4 Aug 2016 16:18:13 -0700 Subject: [PATCH 038/279] remove weird n --- scripts/system/controllers/teleport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index bd77ffd2a1..c5010a0422 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -12,7 +12,7 @@ var inTeleportMode = false; // var NUMBER_OF_STEPS = 0; // var SMOOTH_ARRIVAL_SPACING = 0; -// // slow n +// // slow // var SMOOTH_ARRIVAL_SPACING = 150; // var NUMBER_OF_STEPS = 2; From fb8263a8822410612a36a9a70acf5815381e2703 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 4 Aug 2016 16:35:50 -0700 Subject: [PATCH 039/279] cleanup --- scripts/system/controllers/teleport.js | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index c5010a0422..a67a8521f2 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -384,22 +384,13 @@ function Teleporter() { z: intersection.intersection.z } - var tooClose = isTooCloseToTeleport(position); - this.tooClose = tooClose; - if (tooClose === false) { - Overlays.editOverlay(this.targetOverlay, { - url: TARGET_MODEL_URL, - position: position, - rotation: Quat.fromPitchYawRollDegrees(0, euler.y, 0), - }); - } - if (tooClose === true) { - Overlays.editOverlay(this.targetOverlay, { - url: TOO_CLOSE_MODEL_URL, - position: position, - rotation: Quat.fromPitchYawRollDegrees(0, euler.y, 0), - }); - } + this.tooClose = isTooCloseToTeleport(position); + + Overlays.editOverlay(this.targetOverlay, { + url: this.tooClose ? TOO_CLOSE_MODEL_URL : TARGET_MODEL_URL, + position: position, + rotation: Quat.fromPitchYawRollDegrees(0, euler.y, 0), + }); }; From 80b6ca2b86d50915e9c916d964a3ae164066b340 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 4 Aug 2016 16:47:10 -0700 Subject: [PATCH 040/279] single call to istooclose --- scripts/system/controllers/teleport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index a67a8521f2..8357643629 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -414,7 +414,7 @@ function Teleporter() { } if (this.intersection !== null) { - if (isTooCloseToTeleport(this.intersection.intersection)) { + if (this.tooClose===true) { this.deleteTargetOverlay(); this.exitTeleportMode(); return; From 7d37f86337cb2c836925c8a92202cca7c6491477 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Thu, 4 Aug 2016 23:45:43 -0700 Subject: [PATCH 041/279] different swapping method --- scripts/system/controllers/teleport.js | 135 +++++++++++++++++++------ 1 file changed, 106 insertions(+), 29 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 8357643629..39a134887f 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -94,6 +94,7 @@ function Teleporter() { this.rightOverlayLine = null; this.leftOverlayLine = null; this.targetOverlay = null; + this.cancelOverlay = null; this.updateConnected = null; this.smoothArrivalInterval = null; this.teleportHand = null; @@ -104,19 +105,6 @@ function Teleporter() { this.disableGrab(); }; - this.createTargetOverlay = function() { - - if (_this.targetOverlay !== null) { - return; - } - var targetOverlayProps = { - url: TARGET_MODEL_URL, - dimensions: TARGET_MODEL_DIMENSIONS, - visible: true - }; - - _this.targetOverlay = Overlays.addOverlay("model", targetOverlayProps); - }; this.createMappings = function() { teleporter.telporterMappingInternalName = 'Hifi-Teleporter-Internal-Dev-' + Math.random(); @@ -153,6 +141,50 @@ function Teleporter() { }; + this.createTargetOverlay = function() { + + if (_this.targetOverlay !== null) { + return; + } + var targetOverlayProps = { + url: TARGET_MODEL_URL, + dimensions: TARGET_MODEL_DIMENSIONS, + visible: true + }; + + var cancelOverlayProps = { + url: TOO_CLOSE_MODEL_URL, + dimensions: TARGET_MODEL_DIMENSIONS, + visible: true + }; + + _this.targetOverlay = Overlays.addOverlay("model", targetOverlayProps); + + }; + + this.createCancelOverlay = function() { + + if (_this.cancelOverlay !== null) { + return; + } + + var cancelOverlayProps = { + url: TOO_CLOSE_MODEL_URL, + dimensions: TARGET_MODEL_DIMENSIONS, + visible: true + }; + + _this.cancelOverlay = Overlays.addOverlay("model", cancelOverlayProps); + }; + + this.deleteCancelOverlay = function() { + if (this.cancelOverlay === null) { + return; + } + Overlays.deleteOverlay(this.cancelOverlay); + this.cancelOverlay = null; + } + this.deleteTargetOverlay = function() { if (this.targetOverlay === null) { return; @@ -245,16 +277,26 @@ function Teleporter() { if (rightIntersection.intersects) { if (this.tooClose === true) { + this.deleteTargetOverlay(); + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); + if (this.cancelOverlay !== null) { + this.updateCancelOverlay(rightIntersection); + } else { + this.createCancelOverlay(); + } } else { + this.deleteCancelOverlay(); + this.rightLineOn(rightPickRay.origin, rightIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); + if (this.targetOverlay !== null) { + this.updateTargetOverlay(rightIntersection); + } else { + this.createTargetOverlay(); + } + } - if (this.targetOverlay !== null) { - this.updateTargetOverlay(rightIntersection); - } else { - this.createTargetOverlay(); - } } else { @@ -290,19 +332,26 @@ function Teleporter() { if (leftIntersection.intersects) { if (this.tooClose === true) { + this.deleteTargetOverlay(); this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_TOO_CLOSE); - + if (this.cancelOverlay !== null) { + this.updateCancelOverlay(leftIntersection); + } else { + this.createCancelOverlay(); + } } else { + this.deleteCancelOverlay(); this.leftLineOn(leftPickRay.origin, leftIntersection.intersection, COLORS_TELEPORT_CAN_TELEPORT); + + if (this.targetOverlay !== null) { + this.updateTargetOverlay(leftIntersection); + } else { + this.createTargetOverlay(); + } + } - if (this.targetOverlay !== null) { - this.updateTargetOverlay(leftIntersection); - } else { - this.createTargetOverlay(); - } - } else { this.deleteTargetOverlay(); @@ -385,13 +434,39 @@ function Teleporter() { } this.tooClose = isTooCloseToTeleport(position); + var towardUs = Quat.fromPitchYawRollDegrees(0, euler.y, 0); + Overlays.editOverlay(this.targetOverlay, { - url: this.tooClose ? TOO_CLOSE_MODEL_URL : TARGET_MODEL_URL, position: position, - rotation: Quat.fromPitchYawRollDegrees(0, euler.y, 0), + rotation: towardUs, }); + + + }; + + + this.updateCancelOverlay = function(intersection) { + _this.intersection = intersection; + + var rotation = Quat.lookAt(intersection.intersection, MyAvatar.position, Vec3.UP) + var euler = Quat.safeEulerAngles(rotation) + var position = { + x: intersection.intersection.x, + y: intersection.intersection.y + TARGET_MODEL_DIMENSIONS.y / 2, + z: intersection.intersection.z + } + + this.tooClose = isTooCloseToTeleport(position); + var towardUs = Quat.fromPitchYawRollDegrees(0, euler.y, 0); + + + + Overlays.editOverlay(this.cancelOverlay, { + position: position, + rotation: towardUs, + }); }; this.disableGrab = function() { @@ -414,9 +489,9 @@ function Teleporter() { } if (this.intersection !== null) { - if (this.tooClose===true) { - this.deleteTargetOverlay(); + if (this.tooClose === true) { this.exitTeleportMode(); + this.deleteCancelOverlay(); return; } var offset = getAvatarFootOffset(); @@ -465,6 +540,7 @@ function Teleporter() { if (_this.arrivalPoints.length === 1 || _this.arrivalPoints.length === 0) { _this.deleteTargetOverlay(); + _this.deleteCancelOverlay(); } }, SMOOTH_ARRIVAL_SPACING); @@ -609,6 +685,7 @@ function cleanup() { teleportMapping.disable(); teleporter.disableMappings(); teleporter.deleteTargetOverlay(); + teleporter.deleteCancelOverlay(); teleporter.turnOffOverlayBeams(); if (teleporter.updateConnected !== null) { Script.update.disconnect(teleporter.update); From 80da690680b3f35768981167e76d2fc336f5a899 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Fri, 5 Aug 2016 00:04:30 -0700 Subject: [PATCH 042/279] incease distance a bit --- scripts/system/controllers/teleport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 39a134887f..1441d7ec2e 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -55,7 +55,7 @@ var COLORS_TELEPORT_TOO_CLOSE = { }; -var TELEPORT_CANCEL_RANGE = 1.25; +var TELEPORT_CANCEL_RANGE = 1.5; function ThumbPad(hand) { this.hand = hand; From 83280aa3f265abd320125e3cb1d07ef0e36a4c1f Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 5 Aug 2016 09:42:57 -0700 Subject: [PATCH 043/279] implement support for updating the render items of models when they finish their fade --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 6 ++++++ libraries/render-utils/src/MeshPartPayload.cpp | 7 +++++++ libraries/render-utils/src/MeshPartPayload.h | 4 ++++ libraries/render-utils/src/Model.cpp | 1 + libraries/render-utils/src/Model.h | 4 ++++ 5 files changed, 22 insertions(+) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 564a58708f..6c4cbe7a87 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -371,6 +371,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RMEIrender"); assert(getType() == EntityTypes::Model); + // When the individual mesh parts of a model finish fading, they will mark their Model as needing updating + // we will watch for that and ask the model to update it's render items + if (_model && _model->getRenderItemsNeedUpdate()) { + _model->updateRenderItems(); + } + if (hasModel()) { // Prepare the current frame { diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index fe914f4d1a..3b78023496 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -526,6 +526,13 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { return; // bail asap } + // When an individual mesh parts like this finishes its fade, we will mark the Model as + // having render items that need updating + if (_wasFading && !isStillFading()) { + _model->setRenderItemsNeedUpdate(); + } + _wasFading = isStillFading(); + gpu::Batch& batch = *(args->_batch); if (!getShapeKey().isValid()) { diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 54878f3352..c2305f3741 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -12,6 +12,8 @@ #ifndef hifi_MeshPartPayload_h #define hifi_MeshPartPayload_h +#include + #include #include @@ -85,6 +87,7 @@ public: void startFade() { _fadeStartTime = usecTimestampNow(); } bool hasStartedFade() { return _hasStartedFade; } void setHasStartedFade(bool hasStartedFade) { _hasStartedFade = hasStartedFade; } + bool isStillFading() const { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } // Render Item interface render::ItemKey getKey() const override; @@ -108,6 +111,7 @@ public: private: quint64 _fadeStartTime { 0 }; bool _hasStartedFade { false }; + mutable bool _wasFading { false }; }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2140f2a803..d41a518974 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -174,6 +174,7 @@ void Model::setOffset(const glm::vec3& offset) { void Model::updateRenderItems() { _needsUpdateClusterMatrices = true; + _renderItemsNeedUpdate = false; // queue up this work for later processing, at the end of update and just before rendering. // the application will ensure only the last lambda is actually invoked. diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index b95c0318b4..afaf5600ee 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -103,6 +103,8 @@ public: bool isVisible() const { return _isVisible; } void updateRenderItems(); + void setRenderItemsNeedUpdate() { _renderItemsNeedUpdate = true; } + bool getRenderItemsNeedUpdate() { return _renderItemsNeedUpdate; } AABox getRenderableMeshBound() const; bool maybeStartBlender(); @@ -396,6 +398,8 @@ protected: bool _geometryRequestFailed { false }; + bool _renderItemsNeedUpdate { false }; + private slots: void handleGeometryResourceFailure() { _geometryRequestFailed = true; } }; From 130e64aabae475db0b917753bfa9ca066ba4a227 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 5 Aug 2016 10:36:48 -0700 Subject: [PATCH 044/279] remove polyvox fade, possibly fix web fade --- .../src/RenderablePolyVoxEntityItem.cpp | 6 ---- .../src/RenderablePolyVoxEntityItem.h | 3 +- libraries/entities-renderer/src/polyvox.slf | 29 ++++++------------- libraries/render-utils/src/GeometryCache.cpp | 2 +- 4 files changed, 12 insertions(+), 28 deletions(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 769670b99c..eb6db2874f 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -596,9 +596,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_BACK); state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); _pipeline = gpu::Pipeline::create(program, state); } @@ -645,9 +642,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) { int voxelVolumeSizeLocation = _pipeline->getProgram()->getUniforms().findLocation("voxelVolumeSize"); batch._glUniform3f(voxelVolumeSizeLocation, voxelVolumeSize.x, voxelVolumeSize.y, voxelVolumeSize.z); - int alphaLocation = _pipeline->getProgram()->getUniforms().findLocation("alpha"); - batch._glUniform1f(alphaLocation, 0.5f); - batch.drawIndexed(gpu::TRIANGLES, (gpu::uint32)mesh->getNumIndices(), 0); } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 615451180a..44186073b2 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -131,7 +131,8 @@ public: void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; }); } - bool isTransparent() override { return true; } + // Transparent polyvox didn't seem to be working so disable for now + bool isTransparent() override { return false; } private: // The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf index a3c8315b62..bebefa9434 100644 --- a/libraries/entities-renderer/src/polyvox.slf +++ b/libraries/entities-renderer/src/polyvox.slf @@ -23,7 +23,6 @@ uniform sampler2D xMap; uniform sampler2D yMap; uniform sampler2D zMap; uniform vec3 voxelVolumeSize; -uniform float alpha; void main(void) { vec3 worldNormal = cross(dFdy(_worldPosition.xyz), dFdx(_worldPosition.xyz)); @@ -42,23 +41,13 @@ void main(void) { vec3 yzDiffuseScaled = yzDiffuse.rgb * abs(worldNormal.x); vec4 diffuse = vec4(xyDiffuseScaled + xzDiffuseScaled + yzDiffuseScaled, 1.0); - const float ALPHA_THRESHOLD = 0.999; - if (alpha < ALPHA_THRESHOLD) { - packDeferredFragmentTranslucent( - _normal, - alpha, - vec3(diffuse), - DEFAULT_FRESNEL, - DEFAULT_ROUGHNESS); - } else { - packDeferredFragment( - _normal, - 1.0, - vec3(diffuse), - DEFAULT_ROUGHNESS, - DEFAULT_METALLIC, - DEFAULT_EMISSIVE, - DEFAULT_OCCLUSION, - DEFAULT_SCATTERING); - } + packDeferredFragment( + _normal, + 1.0, + vec3(diffuse), + DEFAULT_ROUGHNESS, + DEFAULT_METALLIC, + DEFAULT_EMISSIVE, + DEFAULT_OCCLUSION, + DEFAULT_SCATTERING); } diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 443421f8b8..dcd36946cb 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1785,7 +1785,7 @@ gpu::PipelinePointer GeometryCache::getSimpleSRGBTexturedUnlitNoTexAlphaPipeline auto state = std::make_shared(); state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(true, true, gpu::LESS_EQUAL); - state->setBlendFunction(false, + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); From b13edc7b6adde15e8f917529290881b96fb54835 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 5 Aug 2016 10:48:09 -0700 Subject: [PATCH 045/279] fixed web fade fo' real --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index b1370e72a7..d6c1c1f761 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -214,7 +214,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); DependencyManager::get()->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch); - DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); + DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio)); } void RenderableWebEntityItem::setSourceUrl(const QString& value) { From f2ee57f2de8b26a9b8848797be3cfff144057e64 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 5 Aug 2016 11:17:02 -0700 Subject: [PATCH 046/279] light fade --- libraries/entities-renderer/src/RenderableLightEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index fb6061e94f..fccd52d58c 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -35,7 +35,7 @@ void RenderableLightEntityItem::render(RenderArgs* args) { glm::vec3 color = toGlm(getXColor()); - float intensity = getIntensity(); + float intensity = getIntensity() * Interpolate::calculateFadeRatio(_fadeStartTime); float falloffRadius = getFalloffRadius(); float exponent = getExponent(); float cutoff = glm::radians(getCutoff()); From 84944000476ac3722faec4b05456dd69f4fd40eb Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 5 Aug 2016 11:27:56 -0700 Subject: [PATCH 047/279] don't use web entity texel color alpha as per tony's suggestion --- .../src/simple_srgb_textured_unlit_no_tex_alpha.slf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf b/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf index 6f8e1d7eb8..38b7e1002c 100644 --- a/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf +++ b/libraries/render-utils/src/simple_srgb_textured_unlit_no_tex_alpha.slf @@ -28,10 +28,10 @@ void main(void) { texel = colorToLinearRGBA(texel); const float ALPHA_THRESHOLD = 0.999; - if (_color.a * texel.a < ALPHA_THRESHOLD) { + if (_color.a < ALPHA_THRESHOLD) { packDeferredFragmentTranslucent( normalize(_normal), - _color.a * texel.a, + _color.a, _color.rgb * texel.rgb, DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); From b794259b7dc59ba829771654c995b8703688f565 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 5 Aug 2016 16:15:10 -0700 Subject: [PATCH 048/279] fixed procedural entity fade --- .../src/RenderableShapeEntityItem.cpp | 6 +++--- .../entities-renderer/src/RenderableShapeEntityItem.h | 10 +++++----- libraries/procedural/src/procedural/Procedural.cpp | 8 +++++++- libraries/procedural/src/procedural/Procedural.h | 1 + libraries/render-utils/src/simple.slf | 3 +-- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 5bfd669a7c..42b63ee2c2 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -71,13 +71,13 @@ void RenderableShapeEntityItem::setUserData(const QString& value) { } } -/*bool RenderableShapeEntityItem::isTransparent() { +bool RenderableShapeEntityItem::isTransparent() { if (_procedural && _procedural->ready()) { return Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f; } else { return EntityItem::isTransparent(); } -}*/ +} void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); @@ -91,7 +91,7 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { _procedural->_fragmentSource = simple_frag; _procedural->_state->setCullMode(gpu::State::CULL_NONE); _procedural->_state->setDepthTest(true, true, gpu::LESS_EQUAL); - _procedural->_state->setBlendFunction(false, + _procedural->_state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 68b36f7e45..7eefe0e7a4 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -21,17 +21,17 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItemPointer boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItemPointer sphereFactory(const EntityItemID& entityID, const EntityItemProperties& properties); - RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) { _procedural.reset(nullptr); } + RenderableShapeEntityItem(const EntityItemID& entityItemID) : ShapeEntityItem(entityItemID) {} void render(RenderArgs* args) override; void setUserData(const QString& value) override; -// bool isTransparent() override; - - SIMPLE_RENDERABLE(); + bool isTransparent() override; private: - QSharedPointer _procedural; + std::unique_ptr _procedural { nullptr }; + + SIMPLE_RENDERABLE(); }; diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 1c7fcade18..7f8ab2db41 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -175,6 +175,10 @@ void Procedural::parse(const QJsonObject& proceduralData) { } bool Procedural::ready() { + if (!_hasStartedFade) { + _fadeStartTime = usecTimestampNow(); + } + // Load any changes to the procedural // Check for changes atomically, in case they are currently being made if (_proceduralDataDirty) { @@ -184,7 +188,6 @@ bool Procedural::ready() { // Reset dirty flag after reading _proceduralData, but before releasing lock // to avoid resetting it after more data is set _proceduralDataDirty = false; - _fadeStartTime = usecTimestampNow(); } if (!_enabled) { @@ -203,6 +206,9 @@ bool Procedural::ready() { } } + if (!_hasStartedFade) { + _hasStartedFade = true; + } return true; } diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index f8d0c963f4..dea55f197b 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -109,6 +109,7 @@ private: void setupChannels(bool shouldCreate); quint64 _fadeStartTime; + bool _hasStartedFade { false }; }; #endif diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index 85d85b3db7..228560f394 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -52,11 +52,10 @@ void main(void) { const float ALPHA_THRESHOLD = 0.999; if (_color.a < ALPHA_THRESHOLD) { if (emissiveAmount > 0.0) { - // TODO: transparent emissive? packDeferredFragmentTranslucent( normal, _color.a, - diffuse, + specular, DEFAULT_FRESNEL, DEFAULT_ROUGHNESS); } else { From b23d3cd35ab833ae3b407ed86bf711475c628a74 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 5 Aug 2016 18:35:16 -0700 Subject: [PATCH 049/279] fixed messed up transparency on edit selection of shapes --- libraries/entities-renderer/src/RenderableShapeEntityItem.cpp | 2 +- scripts/system/libraries/entitySelectionTool.js | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 42b63ee2c2..f557625db2 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -75,7 +75,7 @@ bool RenderableShapeEntityItem::isTransparent() { if (_procedural && _procedural->ready()) { return Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f; } else { - return EntityItem::isTransparent(); + return getLocalRenderAlpha() < 1.0f || EntityItem::isTransparent(); } } diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 2003df3652..461204b7aa 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1109,9 +1109,6 @@ SelectionDisplay = (function() { } - Entities.editEntity(entityID, { - localRenderAlpha: 0.1 - }); Overlays.editOverlay(highlightBox, { visible: false }); From 38b53143228d8d350a42350523527e6f34e5521f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 6 Aug 2016 17:53:59 +1200 Subject: [PATCH 050/279] Disable Asset Browser's right-click popup menu if in HMD mode --- interface/resources/qml/AssetServer.qml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index 1ad2d1a1e4..050bc8e99e 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -521,14 +521,15 @@ ScrollingWindow { anchors.fill: parent acceptedButtons: Qt.RightButton onClicked: { - var index = treeView.indexAt(mouse.x, mouse.y); - - treeView.selection.setCurrentIndex(index, 0x0002); - - contextMenu.currentIndex = index; - contextMenu.popup(); + if (!HMD.active) { // Popup only displays properly on desktop + var index = treeView.indexAt(mouse.x, mouse.y); + treeView.selection.setCurrentIndex(index, 0x0002); + contextMenu.currentIndex = index; + contextMenu.popup(); + } } } + } HifiControls.ContentSection { id: uploadSection From 4f2c00af46158387212c129b06951992835ecade Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 5 Aug 2016 23:38:45 -0700 Subject: [PATCH 051/279] faster TextureUsage::process2DImageColor() --- libraries/model/src/model/TextureMap.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 3e6016d7c0..6d96932e91 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -59,29 +59,27 @@ const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& val const uint8 OPAQUE_ALPHA = 255; const uint8 TRANSPARENT_ALPHA = 0; if (image.hasAlphaChannel()) { - std::map alphaHistogram; - if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } - - // Actual alpha channel? create the histogram - for (int y = 0; y < image.height(); ++y) { + // Count the opaque and transparent pixels + int numOpaques = 0; + int numTransparents = 0; + int height = image.height(); + int width = image.width(); + for (int y = 0; y < height; ++y) { const QRgb* data = reinterpret_cast(image.constScanLine(y)); - for (int x = 0; x < image.width(); ++x) { + for (int x = 0; x < width; ++x) { auto alpha = qAlpha(data[x]); - alphaHistogram[alpha] ++; - validAlpha = validAlpha || (alpha != OPAQUE_ALPHA); + numOpaques += (int)(alpha == OPAQUE_ALPHA); + numTransparents += (int)(alpha == TRANSPARENT_ALPHA); } } // If alpha was meaningfull refine - if (validAlpha && (alphaHistogram.size() > 1)) { - auto totalNumPixels = image.height() * image.width(); - auto numOpaques = alphaHistogram[OPAQUE_ALPHA]; - auto numTransparents = alphaHistogram[TRANSPARENT_ALPHA]; + auto totalNumPixels = height * width; + if (numOpaques != totalNumPixels) { auto numTranslucents = totalNumPixels - numOpaques - numTransparents; - alphaAsMask = ((numTranslucents / (double)totalNumPixels) < 0.05); } } From f759fd10330292982ae1ca342d4262d49bd2a176 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 7 Aug 2016 08:54:20 -0700 Subject: [PATCH 052/279] early exit when determining if alpha mask possible also don't bother scanning for alpha for cube maps --- libraries/model/src/model/TextureMap.cpp | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 6d96932e91..cceaaf6541 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -62,26 +62,27 @@ const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& val if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } - // Count the opaque and transparent pixels + + // Figure out if we can use a mask for alpha or not int numOpaques = 0; int numTransparents = 0; int height = image.height(); int width = image.width(); + const int MAX_TRANSPARENT_PIXELS_FOR_ALPHAMASK = (int)(0.05f * (float)(width * height)); for (int y = 0; y < height; ++y) { const QRgb* data = reinterpret_cast(image.constScanLine(y)); for (int x = 0; x < width; ++x) { auto alpha = qAlpha(data[x]); numOpaques += (int)(alpha == OPAQUE_ALPHA); - numTransparents += (int)(alpha == TRANSPARENT_ALPHA); + if (alpha == TRANSPARENT_ALPHA) { + if (++numTransparents > MAX_TRANSPARENT_PIXELS_FOR_ALPHAMASK) { + alphaAsMask = false; + break; + } + } } } - - // If alpha was meaningfull refine - auto totalNumPixels = height * width; - if (numOpaques != totalNumPixels) { - auto numTranslucents = totalNumPixels - numOpaques - numTransparents; - alphaAsMask = ((numTranslucents / (double)totalNumPixels) < 0.05); - } + validAlpha = (numOpaques != width * height); } if (!validAlpha && image.format() != QImage::Format_RGB888) { @@ -658,13 +659,12 @@ const CubeLayout CubeLayout::CUBEMAP_LAYOUTS[] = { const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) / sizeof(CubeLayout); gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance) { - - bool validAlpha = false; - bool alphaAsMask = true; - QImage image = process2DImageColor(srcImage, validAlpha, alphaAsMask); - gpu::Texture* theTexture = nullptr; - if ((image.width() > 0) && (image.height() > 0)) { + if ((srcImage.width() > 0) && (srcImage.height() > 0)) { + QImage image = srcImage; + if (image.format() != QImage::Format_RGB888) { + image = image.convertToFormat(QImage::Format_RGB888); + } gpu::Element formatGPU; gpu::Element formatMip; @@ -672,7 +672,7 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm // Find the layout of the cubemap in the 2D image int foundLayout = CubeLayout::findLayout(image.width(), image.height()); - + std::vector faces; // If found, go extract the faces as separate images if (foundLayout >= 0) { From 4e23ecfb2cd6d1ce70cb266d76e7c814f518f945 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 7 Aug 2016 10:50:25 -0700 Subject: [PATCH 053/279] count the _translucent_ pixels and fix break logic --- libraries/model/src/model/TextureMap.cpp | 28 +++++++++++------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index cceaaf6541..bbf411b109 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -65,24 +65,22 @@ const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& val // Figure out if we can use a mask for alpha or not int numOpaques = 0; - int numTransparents = 0; - int height = image.height(); - int width = image.width(); - const int MAX_TRANSPARENT_PIXELS_FOR_ALPHAMASK = (int)(0.05f * (float)(width * height)); - for (int y = 0; y < height; ++y) { - const QRgb* data = reinterpret_cast(image.constScanLine(y)); - for (int x = 0; x < width; ++x) { - auto alpha = qAlpha(data[x]); - numOpaques += (int)(alpha == OPAQUE_ALPHA); - if (alpha == TRANSPARENT_ALPHA) { - if (++numTransparents > MAX_TRANSPARENT_PIXELS_FOR_ALPHAMASK) { - alphaAsMask = false; - break; - } + int numTranslucents = 0; + const int NUM_PIXELS = image.width() * image.height(); + const int MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK = (int)(0.05f * (float)(NUM_PIXELS)); + const QRgb* data = reinterpret_cast(image.constBits()); + for (int i = 0; i < NUM_PIXELS; ++i) { + auto alpha = qAlpha(data[i]); + if (alpha == OPAQUE_ALPHA) { + numOpaques++; + } else if (alpha != TRANSPARENT_ALPHA) { + if (++numTranslucents > MAX_TRANSLUCENT_PIXELS_FOR_ALPHAMASK) { + alphaAsMask = false; + break; } } } - validAlpha = (numOpaques != width * height); + validAlpha = (numOpaques != NUM_PIXELS); } if (!validAlpha && image.format() != QImage::Format_RGB888) { From c594dcdf9f3b5083147a00b1fb42a86fa92d386d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 25 Jul 2016 10:52:31 -0700 Subject: [PATCH 054/279] Add priority loading for model entities --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 3 ++- libraries/entities-renderer/src/EntityTreeRenderer.h | 4 +++- .../entities-renderer/src/RenderableModelEntityItem.cpp | 6 +++++- libraries/render-utils/src/Model.cpp | 5 ++++- libraries/render-utils/src/Model.h | 2 ++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 1ec934be92..378e78d0cc 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -529,7 +529,7 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } -ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl) { +ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl, float priority) { ModelPointer model = nullptr; // Only create and delete models on the thread that owns the EntityTreeRenderer @@ -543,6 +543,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString } model = std::make_shared(std::make_shared()); + model->priority = priority; model->init(); model->setURL(QUrl(url)); model->setCollisionModelURL(QUrl(collisionUrl)); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index b0d0d2bacc..b0044b603f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -46,6 +46,8 @@ public: virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; } virtual void setTree(OctreePointer newTree); + glm::vec3 cameraPosition {}; + void shutdown(); void update(); @@ -66,7 +68,7 @@ public: void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them - Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl); + Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float priority = 0); /// if a renderable entity item needs to update the URL of a model, we will handle that for the entity Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index eba2d4cf4b..bfcd604288 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -484,7 +484,11 @@ ModelPointer RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { if (!getModelURL().isEmpty()) { // If we don't have a model, allocate one *immediately* if (!_model) { - _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL()); + auto dims = this->getDimensions(); + auto maxSize = glm::max(dims.x, dims.y, dims.z); + auto distance = glm::distance(renderer->cameraPosition, getPosition()); + float priority = atan2(maxSize / 2, distance); + _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL(), priority); _needsInitialSimulation = true; // If we need to change URLs, update it *after rendering* (to avoid access violations) } else if ((QUrl(getModelURL()) != _model->getURL() || QUrl(getCompoundShapeURL()) != _model->getCollisionURL())) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d755dc3aca..86d1116717 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -826,7 +826,10 @@ void Model::setURL(const QUrl& url) { invalidCalculatedMeshBoxes(); deleteGeometry(); - _renderWatcher.setResource(DependencyManager::get()->getGeometryResource(url)); + auto resource = DependencyManager::get()->getGeometryResource(url); + resource->setLoadPriority(this, priority); + qDebug() << "Setting priority to: " << priority; + _renderWatcher.setResource(resource); onInvalidate(); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index f7bf83ca5b..1713951366 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -238,6 +238,8 @@ public: // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); + float priority { 0 }; + public slots: void loadURLFinished(bool success); void loadCollisionModelURLFinished(bool success); From 77e993510eeb205da58febdeda9b96df626bf5ef Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 28 Jul 2016 09:57:01 -0700 Subject: [PATCH 055/279] Cleanup implementation of angular loading --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 4 ++-- libraries/entities-renderer/src/EntityTreeRenderer.h | 4 +--- libraries/render-utils/src/Model.cpp | 1 - libraries/render-utils/src/Model.h | 6 +++++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 378e78d0cc..24827ea111 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -529,7 +529,7 @@ void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const Sha std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } -ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl, float priority) { +ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority) { ModelPointer model = nullptr; // Only create and delete models on the thread that owns the EntityTreeRenderer @@ -543,7 +543,7 @@ ModelPointer EntityTreeRenderer::allocateModel(const QString& url, const QString } model = std::make_shared(std::make_shared()); - model->priority = priority; + model->setLoadingPriority(loadingPriority); model->init(); model->setURL(QUrl(url)); model->setCollisionModelURL(QUrl(collisionUrl)); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index b0044b603f..4111207e00 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -46,8 +46,6 @@ public: virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; } virtual void setTree(OctreePointer newTree); - glm::vec3 cameraPosition {}; - void shutdown(); void update(); @@ -68,7 +66,7 @@ public: void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them - Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float priority = 0); + Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority = 0); /// if a renderable entity item needs to update the URL of a model, we will handle that for the entity Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 86d1116717..e36c9d8d40 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -828,7 +828,6 @@ void Model::setURL(const QUrl& url) { auto resource = DependencyManager::get()->getGeometryResource(url); resource->setLoadPriority(this, priority); - qDebug() << "Setting priority to: " << priority; _renderWatcher.setResource(resource); onInvalidate(); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 1713951366..4b93d19d35 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -238,7 +238,7 @@ public: // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); - float priority { 0 }; + float setLoadingPriority(float priority) { _loadingPriority = priority; } public slots: void loadURLFinished(bool success); @@ -407,6 +407,10 @@ protected: bool _visualGeometryRequestFailed { false }; bool _collisionGeometryRequestFailed { false }; + +private: + float _loadingPriority { 0 }; + }; Q_DECLARE_METATYPE(ModelPointer) From 4f9be2ae72e3231297a46869536d464ee548cd8d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 28 Jul 2016 13:19:53 -0700 Subject: [PATCH 056/279] Fix camera position not being set for angular loading --- interface/src/Application.cpp | 2 ++ libraries/entities-renderer/src/EntityTreeRenderer.h | 2 ++ libraries/render-utils/src/Model.cpp | 2 +- libraries/render-utils/src/Model.h | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5d50a1c9fe..52677abff6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1635,6 +1635,8 @@ void Application::paintGL() { return; } + DependencyManager::get()->cameraPosition = getMyAvatar()->getPosition(); + _inPaint = true; Finally clearFlag([this] { _inPaint = false; }); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 4111207e00..ec14e2f269 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -46,6 +46,8 @@ public: virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; } virtual void setTree(OctreePointer newTree); + glm::vec3 cameraPosition {}; + void shutdown(); void update(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index e36c9d8d40..1ddc3cda47 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -827,7 +827,7 @@ void Model::setURL(const QUrl& url) { deleteGeometry(); auto resource = DependencyManager::get()->getGeometryResource(url); - resource->setLoadPriority(this, priority); + resource->setLoadPriority(this, _loadingPriority); _renderWatcher.setResource(resource); onInvalidate(); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 4b93d19d35..0ddfcd32c3 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -238,7 +238,7 @@ public: // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); - float setLoadingPriority(float priority) { _loadingPriority = priority; } + void setLoadingPriority(float priority) { _loadingPriority = priority; } public slots: void loadURLFinished(bool success); From d48bc96cf94b2a3b93870ec7d7ee5f2187aa2618 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 28 Jul 2016 14:29:36 -0700 Subject: [PATCH 057/279] Add ability to set number of concurrent downloads --- interface/src/Application.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 52677abff6..9b6ef9f9c2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -739,7 +739,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket); identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); - ResourceCache::setRequestLimit(MAX_CONCURRENT_RESOURCE_DOWNLOADS); + const char** constArgv = const_cast(argv); + QString concurrentDownloadsStr = getCmdOption(argc, constArgv, "--concurrent-downloads"); + bool success; + int concurrentDownloads = concurrentDownloadsStr.toInt(&success); + if (!success) { + concurrentDownloads = MAX_CONCURRENT_RESOURCE_DOWNLOADS; + } + ResourceCache::setRequestLimit(concurrentDownloads); _glWidget = new GLCanvas(); getApplicationCompositor().setRenderingWidget(_glWidget); From d367583426e0728a3d89bb6b8e83e678b197c156 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 Aug 2016 16:30:38 -0700 Subject: [PATCH 058/279] Clean up priority loading implementation --- interface/src/Application.cpp | 9 +++++++-- libraries/entities-renderer/src/EntityTreeRenderer.h | 11 ++++++++++- .../src/RenderableModelEntityItem.cpp | 6 +----- .../entities-renderer/src/RenderableModelEntityItem.h | 1 - 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9b6ef9f9c2..30d992ede9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1642,8 +1642,6 @@ void Application::paintGL() { return; } - DependencyManager::get()->cameraPosition = getMyAvatar()->getPosition(); - _inPaint = true; Finally clearFlag([this] { _inPaint = false; }); @@ -3249,6 +3247,13 @@ void Application::init() { getEntities()->setViewFrustum(_viewFrustum); } + getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) { + auto dims = item.getDimensions(); + auto maxSize = glm::max(dims.x, dims.y, dims.z); + auto distance = glm::distance(getMyAvatar()->getPosition(), item.getPosition()); + return atan2(maxSize, distance); + }); + ObjectMotionState::setShapeManager(&_shapeManager); _physicsEngine->init(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index ec14e2f269..2ead511048 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -28,11 +28,14 @@ class AbstractViewStateInterface; class Model; class ScriptEngine; class ZoneEntityItem; +class EntityItem; class Model; using ModelPointer = std::shared_ptr; using ModelWeakPointer = std::weak_ptr; +using CalculateEntityLoadingPriority = std::function; + // Generic client side Octree renderer class. class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public Dependency { Q_OBJECT @@ -46,7 +49,9 @@ public: virtual PacketType getExpectedPacketType() const { return PacketType::EntityData; } virtual void setTree(OctreePointer newTree); - glm::vec3 cameraPosition {}; + // Returns the priority at which an entity should be loaded. Higher values indicate higher priority. + float getEntityLoadingPriority(const EntityItem& item) const { return _calculateEntityLoadingPriorityFunc(item); } + void setEntityLoadingPriorityFunction(CalculateEntityLoadingPriority fn) { this->_calculateEntityLoadingPriorityFunc = fn; } void shutdown(); void update(); @@ -204,6 +209,10 @@ private: QList _entityIDsLastInScene; static int _entitiesScriptEngineCount; + + CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc = [](const EntityItem& item) -> float { + return 0.0f; + }; }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index bfcd604288..29992e897a 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -484,11 +484,7 @@ ModelPointer RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) { if (!getModelURL().isEmpty()) { // If we don't have a model, allocate one *immediately* if (!_model) { - auto dims = this->getDimensions(); - auto maxSize = glm::max(dims.x, dims.y, dims.z); - auto distance = glm::distance(renderer->cameraPosition, getPosition()); - float priority = atan2(maxSize / 2, distance); - _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL(), priority); + _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL(), renderer->getEntityLoadingPriority(*this)); _needsInitialSimulation = true; // If we need to change URLs, update it *after rendering* (to avoid access violations) } else if ((QUrl(getModelURL()) != _model->getURL() || QUrl(getCompoundShapeURL()) != _model->getCollisionURL())) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index f487e79880..f63ffcbdb4 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -52,7 +52,6 @@ public: bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject, bool precisionPicking) const override; - ModelPointer getModel(EntityTreeRenderer* renderer); virtual bool needsToCallUpdate() const override; From 2a071c4329df4cea2ef5a575df1395419caa612f Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 10:31:00 -0700 Subject: [PATCH 059/279] Fix improperly formatted floats --- libraries/entities-renderer/src/EntityTreeRenderer.h | 2 +- libraries/render-utils/src/Model.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 2ead511048..99c62ab5f6 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -73,7 +73,7 @@ public: void reloadEntityScripts(); /// if a renderable entity item needs a model, we will allocate it for them - Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority = 0); + Q_INVOKABLE ModelPointer allocateModel(const QString& url, const QString& collisionUrl, float loadingPriority = 0.0f); /// if a renderable entity item needs to update the URL of a model, we will handle that for the entity Q_INVOKABLE ModelPointer updateModel(ModelPointer original, const QString& newUrl, const QString& collisionUrl); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 0ddfcd32c3..aecbcf2510 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -409,7 +409,7 @@ protected: bool _collisionGeometryRequestFailed { false }; private: - float _loadingPriority { 0 }; + float _loadingPriority { 0.0f }; }; From fb14628a7ebff15c9bbf70d839aa83730f1d624e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 10:33:15 -0700 Subject: [PATCH 060/279] Handle case in entity loading priority function where max dim size is <= 0 --- interface/src/Application.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 30d992ede9..7695bd1b99 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3250,6 +3250,11 @@ void Application::init() { getEntities()->setEntityLoadingPriorityFunction([this](const EntityItem& item) { auto dims = item.getDimensions(); auto maxSize = glm::max(dims.x, dims.y, dims.z); + + if (maxSize <= 0.0f) { + return 0.0f; + } + auto distance = glm::distance(getMyAvatar()->getPosition(), item.getPosition()); return atan2(maxSize, distance); }); From f8c1d8f1238aba3da9af63dfefeab140a3b19760 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 8 Aug 2016 11:01:03 -0700 Subject: [PATCH 061/279] change property order and cleanup --- scripts/system/html/entityProperties.html | 63 ++++++++++++----------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index f2ade39144..5240b3da13 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -1389,23 +1389,13 @@ M

-
- -
- - - -
-
- -
@@ -1414,19 +1404,28 @@
- -
- +
-
-
-
+
+
+
-
- - - +
+
+ +
+
+
+
+
+
+
+ +
+ + +
@@ -1439,15 +1438,17 @@
-
- -
-
-
-
+
+
+
+
+
+ + +
+
-
From f31190798226bccb793d95aa02ee90e39e608da5 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 8 Aug 2016 11:08:01 -0700 Subject: [PATCH 062/279] fix merge mistake --- libraries/render-utils/src/Model.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 785977e5f1..52cfdc67cf 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -407,12 +407,8 @@ protected: bool _visualGeometryRequestFailed { false }; bool _collisionGeometryRequestFailed { false }; - bool _geometryRequestFailed { false }; bool _renderItemsNeedUpdate { false }; - -private slots: - void handleGeometryResourceFailure() { _geometryRequestFailed = true; } }; Q_DECLARE_METATYPE(ModelPointer) From bde46adee8ec7e1e014a8cc6ac82acc61d35332d Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 8 Aug 2016 11:31:43 -0700 Subject: [PATCH 063/279] update description --- scripts/system/html/entityProperties.html | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 5240b3da13..424795981d 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -440,10 +440,11 @@ var elWebSections = document.querySelectorAll(".web-section"); allSections.push(elWebSections); var elWebSourceURL = document.getElementById("property-web-source-url"); - + + var elDescription = document.getElementById("property-description"); var elHyperlinkHref = document.getElementById("property-hyperlink-href"); - var elHyperlinkDescription = document.getElementById("property-hyperlink-description"); + var elHyperlinkSections = document.querySelectorAll(".hyperlink-section"); @@ -651,7 +652,7 @@ setTextareaScrolling(elUserData); elHyperlinkHref.value = properties.href; - elHyperlinkDescription.value = properties.description; + elDescription.value = properties.description; for (var i = 0; i < allSections.length; i++) { for (var j = 0; j < allSections[i].length; j++) { @@ -818,7 +819,7 @@ elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked')); elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name')); elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href')); - elHyperlinkDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description')); + elDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description')); elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible')); var positionChangeFunction = createEmitVec3PropertyUpdateFunction( @@ -1363,6 +1364,10 @@
+
+ + +
@@ -1379,10 +1384,7 @@
- +
From 28b3ff9bcac97d81f43def3992583597ac361268 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 12:04:06 -0700 Subject: [PATCH 064/279] Update generation of srgb to linear lookup to include python script --- libraries/shared/src/ColorUtils.h | 49 +++++++++++++++++++++++++++++-- tools/srgb_gen.py | 22 ++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 tools/srgb_gen.py diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index 42d36ebd4b..e7b4a1e5c2 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -17,8 +17,53 @@ #include "DependencyManager.h" -static const float srgbToLinearLookupTable[256] = - { 0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f, 0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.00251584218443f, 0.00279619194822f, 0.00309396642819f, 0.00340946205345f, 0.00374296799396f, 0.00409476661624f, 0.00446513389425f, 0.00485433978143f, 0.00526264854875f, 0.00569031909303f, 0.00613760521883f, 0.00660475589722f, 0.00709201550367f, 0.00759962403765f, 0.00812781732551f, 0.00867682720861f, 0.00924688171802f, 0.00983820523704f, 0.0104510186528f, 0.0110855394981f, 0.0117419820834f, 0.0124205576216f, 0.0131214743443f, 0.0138449376117f, 0.0145911500156f, 0.0153603114768f, 0.0161526193372f, 0.0169682684465f, 0.0178074512441f, 0.0186703578377f, 0.0195571760767f, 0.0204680916222f, 0.0214032880141f, 0.0223629467344f, 0.0233472472675f, 0.0243563671578f, 0.0253904820647f, 0.026449765815f, 0.0275343904531f, 0.0286445262888f, 0.0297803419432f, 0.0309420043928f, 0.0321296790111f, 0.0333435296099f, 0.0345837184768f, 0.0358504064137f, 0.0371437527716f, 0.0384639154854f, 0.0398110511069f, 0.0411853148367f, 0.0425868605546f, 0.0440158408496f, 0.045472407048f, 0.046956709241f, 0.0484688963113f, 0.0500091159586f, 0.0515775147244f, 0.0531742380159f, 0.0547994301291f, 0.0564532342711f, 0.058135792582f, 0.0598472461555f, 0.0615877350593f, 0.063357398355f, 0.0651563741167f, 0.0669847994499f, 0.0688428105093f, 0.0707305425158f, 0.0726481297741f, 0.0745957056885f, 0.0765734027789f, 0.0785813526965f, 0.0806196862387f, 0.0826885333636f, 0.0847880232044f, 0.086918284083f, 0.0890794435236f, 0.0912716282659f, 0.0934949642776f, 0.0957495767668f, 0.0980355901944f, 0.100353128286f, 0.102702314041f, 0.10508326975f, 0.107496116997f, 0.109940976678f, 0.112417969007f, 0.114927213525f, 0.117468829116f, 0.120042934009f, 0.122649645793f, 0.125289081424f, 0.127961357236f, 0.130666588944f, 0.13340489166f, 0.136176379898f, 0.138981167581f, 0.141819368051f, 0.144691094076f, 0.147596457859f, 0.150535571041f, 0.153508544716f, 0.156515489432f, 0.1595565152f, 0.1626317315f, 0.16574124729f, 0.168885171012f, 0.172063610595f, 0.175276673468f, 0.178524466557f, 0.181807096302f, 0.185124668654f, 0.188477289086f, 0.191865062595f, 0.195288093712f, 0.198746486503f, 0.202240344578f, 0.205769771096f, 0.209334868766f, 0.212935739858f, 0.216572486205f, 0.220245209207f, 0.223954009837f, 0.227698988648f, 0.231480245773f, 0.235297880934f, 0.239151993444f, 0.243042682212f, 0.246970045747f, 0.250934182163f, 0.254935189183f, 0.258973164144f, 0.263048203998f, 0.267160405319f, 0.271309864307f, 0.27549667679f, 0.279720938228f, 0.283982743718f, 0.288282187998f, 0.292619365448f, 0.296994370096f, 0.30140729562f, 0.305858235354f, 0.310347282289f, 0.314874529074f, 0.319440068025f, 0.324043991126f, 0.32868639003f, 0.333367356062f, 0.338086980228f, 0.34284535321f, 0.347642565374f, 0.352478706774f, 0.357353867148f, 0.36226813593f, 0.367221602246f, 0.372214354918f, 0.37724648247f, 0.382318073128f, 0.387429214822f, 0.392579995191f, 0.397770501584f, 0.403000821062f, 0.408271040402f, 0.413581246099f, 0.418931524369f, 0.424321961148f, 0.4297526421f, 0.435223652615f, 0.440735077813f, 0.446287002544f, 0.451879511396f, 0.45751268869f, 0.463186618488f, 0.46890138459f, 0.474657070542f, 0.480453759632f, 0.486291534897f, 0.492170479122f, 0.498090674843f, 0.50405220435f, 0.510055149687f, 0.516099592656f, 0.522185614816f, 0.528313297489f, 0.534482721758f, 0.54069396847f, 0.546947118241f, 0.553242251452f, 0.559579448254f, 0.565958788573f, 0.572380352104f, 0.578844218319f, 0.585350466467f, 0.591899175574f, 0.598490424448f, 0.605124291677f, 0.611800855632f, 0.61852019447f, 0.625282386134f, 0.632087508355f, 0.638935638652f, 0.645826854338f, 0.652761232515f, 0.659738850081f, 0.66675978373f, 0.673824109951f, 0.680931905032f, 0.688083245062f, 0.695278205929f, 0.702516863324f, 0.709799292744f, 0.717125569488f, 0.724495768663f, 0.731909965185f, 0.739368233777f, 0.746870648974f, 0.754417285121f, 0.762008216379f, 0.76964351672f, 0.777323259932f, 0.785047519623f, 0.792816369214f, 0.800629881949f, 0.80848813089f, 0.816391188922f, 0.824339128751f, 0.832332022907f, 0.840369943747f, 0.848452963452f, 0.856581154031f, 0.864754587319f, 0.872973334984f, 0.881237468522f, 0.889547059261f, 0.897902178361f, 0.906302896816f, 0.914749285456f, 0.923241414944f, 0.931779355781f, 0.940363178305f, 0.948992952695f, 0.957668748966f, 0.966390636975f, 0.975158686423f }; +// Generated from python script in repository at `tools/srgb_gen.py` +static const float srgbToLinearLookupTable[256] = { + 0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f, + 0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.00251584218443f, 0.00279619194822f, + 0.00309396642819f, 0.00340946205345f, 0.00374296799396f, 0.00409476661624f, 0.00446513389425f, 0.00485433978143f, + 0.00526264854875f, 0.00569031909303f, 0.00613760521883f, 0.00660475589722f, 0.00709201550367f, 0.00759962403765f, + 0.00812781732551f, 0.00867682720861f, 0.00924688171802f, 0.00983820523704f, 0.0104510186528f, 0.0110855394981f, + 0.0117419820834f, 0.0124205576216f, 0.0131214743443f, 0.0138449376117f, 0.0145911500156f, 0.0153603114768f, + 0.0161526193372f, 0.0169682684465f, 0.0178074512441f, 0.0186703578377f, 0.0195571760767f, 0.0204680916222f, + 0.0214032880141f, 0.0223629467344f, 0.0233472472675f, 0.0243563671578f, 0.0253904820647f, 0.026449765815f, + 0.0275343904531f, 0.0286445262888f, 0.0297803419432f, 0.0309420043928f, 0.0321296790111f, 0.0333435296099f, + 0.0345837184768f, 0.0358504064137f, 0.0371437527716f, 0.0384639154854f, 0.0398110511069f, 0.0411853148367f, + 0.0425868605546f, 0.0440158408496f, 0.045472407048f, 0.046956709241f, 0.0484688963113f, 0.0500091159586f, + 0.0515775147244f, 0.0531742380159f, 0.0547994301291f, 0.0564532342711f, 0.058135792582f, 0.0598472461555f, + 0.0615877350593f, 0.063357398355f, 0.0651563741167f, 0.0669847994499f, 0.0688428105093f, 0.0707305425158f, + 0.0726481297741f, 0.0745957056885f, 0.0765734027789f, 0.0785813526965f, 0.0806196862387f, 0.0826885333636f, + 0.0847880232044f, 0.086918284083f, 0.0890794435236f, 0.0912716282659f, 0.0934949642776f, 0.0957495767668f, + 0.0980355901944f, 0.100353128286f, 0.102702314041f, 0.10508326975f, 0.107496116997f, 0.109940976678f, + 0.112417969007f, 0.114927213525f, 0.117468829116f, 0.120042934009f, 0.122649645793f, 0.125289081424f, + 0.127961357236f, 0.130666588944f, 0.13340489166f, 0.136176379898f, 0.138981167581f, 0.141819368051f, + 0.144691094076f, 0.147596457859f, 0.150535571041f, 0.153508544716f, 0.156515489432f, 0.1595565152f, + 0.1626317315f, 0.16574124729f, 0.168885171012f, 0.172063610595f, 0.175276673468f, 0.178524466557f, + 0.181807096302f, 0.185124668654f, 0.188477289086f, 0.191865062595f, 0.195288093712f, 0.198746486503f, + 0.202240344578f, 0.205769771096f, 0.209334868766f, 0.212935739858f, 0.216572486205f, 0.220245209207f, + 0.223954009837f, 0.227698988648f, 0.231480245773f, 0.235297880934f, 0.239151993444f, 0.243042682212f, + 0.246970045747f, 0.250934182163f, 0.254935189183f, 0.258973164144f, 0.263048203998f, 0.267160405319f, + 0.271309864307f, 0.27549667679f, 0.279720938228f, 0.283982743718f, 0.288282187998f, 0.292619365448f, + 0.296994370096f, 0.30140729562f, 0.305858235354f, 0.310347282289f, 0.314874529074f, 0.319440068025f, + 0.324043991126f, 0.32868639003f, 0.333367356062f, 0.338086980228f, 0.34284535321f, 0.347642565374f, + 0.352478706774f, 0.357353867148f, 0.36226813593f, 0.367221602246f, 0.372214354918f, 0.37724648247f, + 0.382318073128f, 0.387429214822f, 0.392579995191f, 0.397770501584f, 0.403000821062f, 0.408271040402f, + 0.413581246099f, 0.418931524369f, 0.424321961148f, 0.4297526421f, 0.435223652615f, 0.440735077813f, + 0.446287002544f, 0.451879511396f, 0.45751268869f, 0.463186618488f, 0.46890138459f, 0.474657070542f, + 0.480453759632f, 0.486291534897f, 0.492170479122f, 0.498090674843f, 0.50405220435f, 0.510055149687f, + 0.516099592656f, 0.522185614816f, 0.528313297489f, 0.534482721758f, 0.54069396847f, 0.546947118241f, + 0.553242251452f, 0.559579448254f, 0.565958788573f, 0.572380352104f, 0.578844218319f, 0.585350466467f, + 0.591899175574f, 0.598490424448f, 0.605124291677f, 0.611800855632f, 0.61852019447f, 0.625282386134f, + 0.632087508355f, 0.638935638652f, 0.645826854338f, 0.652761232515f, 0.659738850081f, 0.66675978373f, + 0.673824109951f, 0.680931905032f, 0.688083245062f, 0.695278205929f, 0.702516863324f, 0.709799292744f, + 0.717125569488f, 0.724495768663f, 0.731909965185f, 0.739368233777f, 0.746870648974f, 0.754417285121f, + 0.762008216379f, 0.76964351672f, 0.777323259932f, 0.785047519623f, 0.792816369214f, 0.800629881949f, + 0.80848813089f, 0.816391188922f, 0.824339128751f, 0.832332022907f, 0.840369943747f, 0.848452963452f, + 0.856581154031f, 0.864754587319f, 0.872973334984f, 0.881237468522f, 0.889547059261f, 0.897902178361f, + 0.906302896816f, 0.914749285456f, 0.923241414944f, 0.931779355781f, 0.940363178305f, 0.948992952695f, + 0.957668748966f, 0.966390636975f, 0.975158686423f +} + class ColorUtils { public: diff --git a/tools/srgb_gen.py b/tools/srgb_gen.py new file mode 100644 index 0000000000..e52f4a6418 --- /dev/null +++ b/tools/srgb_gen.py @@ -0,0 +1,22 @@ +NUM_VALUES = 256 +srgb_to_linear = [] + +# Calculate srgb to linear +for i in range(NUM_VALUES): + s = float(i) / 255 + if s < 0.04045: + l = s / 12.92 + else: + l = ((s + 0.044) / 1.055) ** 2.4 + srgb_to_linear.append(l) + +# Format and print +data = "{\n " +for i, v in enumerate(srgb_to_linear): + data += str(v) + "f" + if i < NUM_VALUES - 1: + data += ", " + if i > 0 and i % 6 == 0: + data += "\n " +data += "\n}" +print(data) From d40b783ce2caf4fe2a591eeb99becdcb0d2dfb27 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 12:06:09 -0700 Subject: [PATCH 065/279] Add comments to srgb_gen.py --- tools/srgb_gen.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/srgb_gen.py b/tools/srgb_gen.py index e52f4a6418..e17970209a 100644 --- a/tools/srgb_gen.py +++ b/tools/srgb_gen.py @@ -1,3 +1,15 @@ +# +# srgb_gen.py +# tools/ +# +# Created by Ryan Huffman on 8/8/2016. +# Copyright 2016 High Fidelity, Inc. +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +# Generates a lookup table for SRGB to Linear color transformations + NUM_VALUES = 256 srgb_to_linear = [] From 58ccc9581a82fd7a6f51eb089dca631790b69bee Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 8 Aug 2016 12:20:48 -0700 Subject: [PATCH 066/279] cleanup --- scripts/system/controllers/teleport.js | 57 +++++++------------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index 1441d7ec2e..fae9b98b96 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -1,33 +1,16 @@ // Created by james b. pollack @imgntn on 7/2/2016 // Copyright 2016 High Fidelity, Inc. // -// Creates a beam and target and then teleports you there. +// Creates a beam and target and then teleports you there. Release when its close to you to cancel. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html var inTeleportMode = false; -// instant -// var NUMBER_OF_STEPS = 0; -// var SMOOTH_ARRIVAL_SPACING = 0; - -// // slow -// var SMOOTH_ARRIVAL_SPACING = 150; -// var NUMBER_OF_STEPS = 2; - -// medium-slow -// var SMOOTH_ARRIVAL_SPACING = 100; -// var NUMBER_OF_STEPS = 4; - -// medium-fast var SMOOTH_ARRIVAL_SPACING = 33; var NUMBER_OF_STEPS = 6; -//fast -// var SMOOTH_ARRIVAL_SPACING = 10; -// var NUMBER_OF_STEPS = 20; - var TARGET_MODEL_URL = Script.resolvePath("../assets/models/teleport-destination.fbx"); var TOO_CLOSE_MODEL_URL = Script.resolvePath("../assets/models/teleport-cancel.fbx"); var TARGET_MODEL_DIMENSIONS = { @@ -54,7 +37,6 @@ var COLORS_TELEPORT_TOO_CLOSE = { blue: 73 }; - var TELEPORT_CANCEL_RANGE = 1.5; function ThumbPad(hand) { @@ -105,7 +87,6 @@ function Teleporter() { this.disableGrab(); }; - this.createMappings = function() { teleporter.telporterMappingInternalName = 'Hifi-Teleporter-Internal-Dev-' + Math.random(); teleporter.teleportMappingInternal = Controller.newMapping(teleporter.telporterMappingInternalName); @@ -140,7 +121,6 @@ function Teleporter() { this.updateConnected = true; }; - this.createTargetOverlay = function() { if (_this.targetOverlay !== null) { @@ -181,6 +161,7 @@ function Teleporter() { if (this.cancelOverlay === null) { return; } + Overlays.deleteOverlay(this.cancelOverlay); this.cancelOverlay = null; } @@ -189,6 +170,7 @@ function Teleporter() { if (this.targetOverlay === null) { return; } + Overlays.deleteOverlay(this.targetOverlay); this.intersection = null; this.targetOverlay = null; @@ -255,7 +237,7 @@ function Teleporter() { var rightControllerRotation = Controller.getPoseValue(Controller.Standard.RightHand).rotation; - var rightRotation = Quat.multiply(MyAvatar.orientation, rightControllerRotation) + var rightRotation = Quat.multiply(MyAvatar.orientation, rightControllerRotation); var rightFinal = Quat.multiply(rightRotation, Quat.angleAxis(90, { x: 1, @@ -297,7 +279,6 @@ function Teleporter() { } - } else { this.deleteTargetOverlay(); @@ -425,47 +406,41 @@ function Teleporter() { this.updateTargetOverlay = function(intersection) { _this.intersection = intersection; - var rotation = Quat.lookAt(intersection.intersection, MyAvatar.position, Vec3.UP) - var euler = Quat.safeEulerAngles(rotation) + var rotation = Quat.lookAt(intersection.intersection, MyAvatar.position, Vec3.UP); + var euler = Quat.safeEulerAngles(rotation); var position = { x: intersection.intersection.x, y: intersection.intersection.y + TARGET_MODEL_DIMENSIONS.y / 2, z: intersection.intersection.z - } + }; this.tooClose = isTooCloseToTeleport(position); var towardUs = Quat.fromPitchYawRollDegrees(0, euler.y, 0); - Overlays.editOverlay(this.targetOverlay, { position: position, - rotation: towardUs, + rotation: towardUs }); - - }; - this.updateCancelOverlay = function(intersection) { _this.intersection = intersection; - var rotation = Quat.lookAt(intersection.intersection, MyAvatar.position, Vec3.UP) - var euler = Quat.safeEulerAngles(rotation) + var rotation = Quat.lookAt(intersection.intersection, MyAvatar.position, Vec3.UP); + var euler = Quat.safeEulerAngles(rotation); var position = { x: intersection.intersection.x, y: intersection.intersection.y + TARGET_MODEL_DIMENSIONS.y / 2, z: intersection.intersection.z - } + }; this.tooClose = isTooCloseToTeleport(position); var towardUs = Quat.fromPitchYawRollDegrees(0, euler.y, 0); - - Overlays.editOverlay(this.cancelOverlay, { position: position, - rotation: towardUs, + rotation: towardUs }); }; @@ -501,7 +476,6 @@ function Teleporter() { } }; - this.findMidpoint = function(start, end) { var xy = Vec3.sum(start, end); var midpoint = Vec3.multiply(0.5, xy); @@ -547,7 +521,6 @@ function Teleporter() { } } - //related to repositioning the avatar after you teleport function getAvatarFootOffset() { var data = getJointData(); @@ -613,11 +586,11 @@ function isMoving() { } else { return false; } -} +}; function isTooCloseToTeleport(position) { return Vec3.distance(MyAvatar.position, position) <= TELEPORT_CANCEL_RANGE; -} +}; function registerMappings() { mappingName = 'Hifi-Teleporter-Dev-' + Math.random(); @@ -671,7 +644,7 @@ function registerMappings() { }, TELEPORT_DELAY) return; }); -} +}; registerMappings(); From 2ee511bae0972b0ee5ef02877445ebf2df55b979 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 12:30:11 -0700 Subject: [PATCH 067/279] Fix syntax error --- libraries/shared/src/ColorUtils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index e7b4a1e5c2..921b26399d 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -62,7 +62,7 @@ static const float srgbToLinearLookupTable[256] = { 0.856581154031f, 0.864754587319f, 0.872973334984f, 0.881237468522f, 0.889547059261f, 0.897902178361f, 0.906302896816f, 0.914749285456f, 0.923241414944f, 0.931779355781f, 0.940363178305f, 0.948992952695f, 0.957668748966f, 0.966390636975f, 0.975158686423f -} +}; class ColorUtils { From a5f3089fdaf9f0244f95c3e61d53e33d211ac58a Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 8 Aug 2016 13:14:14 -0700 Subject: [PATCH 068/279] fix ABXY mapping for touch --- interface/resources/controllers/oculus_touch.json | 9 +++++++-- interface/resources/controllers/standard.json | 3 --- interface/resources/controllers/xbox.json | 4 +++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/interface/resources/controllers/oculus_touch.json b/interface/resources/controllers/oculus_touch.json index 82f52e50db..ef5d8c59a8 100644 --- a/interface/resources/controllers/oculus_touch.json +++ b/interface/resources/controllers/oculus_touch.json @@ -1,8 +1,13 @@ { "name": "Oculus Touch to Standard", "channels": [ - { "from": "OculusTouch.A", "to": "Standard.RightPrimaryThumb" }, - { "from": "OculusTouch.X", "to": "Standard.LeftPrimaryThumb" }, + { "from": "OculusTouch.A", "to": "Standard.RightPrimaryThumb", "peek": true }, + { "from": "OculusTouch.X", "to": "Standard.LeftPrimaryThumb", "peek": true }, + + { "from": "OculusTouch.A", "to": "Standard.A" }, + { "from": "OculusTouch.B", "to": "Standard.B" }, + { "from": "OculusTouch.X", "to": "Standard.X" }, + { "from": "OculusTouch.Y", "to": "Standard.Y" }, { "from": "OculusTouch.LY", "to": "Standard.LY", "filters": [ diff --git a/interface/resources/controllers/standard.json b/interface/resources/controllers/standard.json index 5c0ea09939..222357ac9d 100644 --- a/interface/resources/controllers/standard.json +++ b/interface/resources/controllers/standard.json @@ -32,9 +32,6 @@ { "from": "Standard.Back", "to": "Actions.CycleCamera" }, { "from": "Standard.Start", "to": "Actions.ContextMenu" }, - { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, - { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" }, - { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, { "from": "Standard.RT", "to": "Actions.RightHandClick" }, diff --git a/interface/resources/controllers/xbox.json b/interface/resources/controllers/xbox.json index fdac70ff33..1234372a7e 100644 --- a/interface/resources/controllers/xbox.json +++ b/interface/resources/controllers/xbox.json @@ -15,12 +15,14 @@ { "from": "GamePad.Back", "to": "Standard.Back" }, { "from": "GamePad.Start", "to": "Standard.Start" }, - + + { "from": [ "GamePad.DU", "GamePad.DL", "GamePad.DR", "GamePad.DD" ], "to": "Standard.LeftPrimaryThumb", "peek": true }, { "from": "GamePad.DU", "to": "Standard.DU" }, { "from": "GamePad.DD", "to": "Standard.DD" }, { "from": "GamePad.DL", "to": "Standard.DL" }, { "from": "GamePad.DR", "to": "Standard.DR" }, + { "from": [ "GamePad.A", "GamePad.B", "GamePad.X", "GamePad.Y" ], "to": "Standard.RightPrimaryThumb", "peek": true }, { "from": "GamePad.A", "to": "Standard.A" }, { "from": "GamePad.B", "to": "Standard.B" }, { "from": "GamePad.X", "to": "Standard.X" }, From de900e85c059a951129f143e8cca0175cad327b1 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 8 Aug 2016 14:10:47 -0700 Subject: [PATCH 069/279] remove duplicate include --- libraries/render-utils/src/MeshPartPayload.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 3b78023496..c2c27fb298 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -13,8 +13,6 @@ #include -#include - #include "DeferredLightingEffect.h" #include "Model.h" From 43bbe790d6389396d37306c985b5c80b098e20a4 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Fri, 5 Aug 2016 17:01:33 -0700 Subject: [PATCH 070/279] Add atp support to qml --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 3 + .../networking/src/AssetResourceRequest.cpp | 5 -- libraries/networking/src/QmlAtpReply.cpp | 88 +++++++++++++++++++ libraries/networking/src/QmlAtpReply.h | 40 +++++++++ .../src/QmlNetworkAccessManager.cpp | 29 ++++++ .../networking/src/QmlNetworkAccessManager.h | 33 +++++++ libraries/ui/src/QmlWindowClass.cpp | 7 +- 7 files changed, 197 insertions(+), 8 deletions(-) create mode 100644 libraries/networking/src/QmlAtpReply.cpp create mode 100644 libraries/networking/src/QmlAtpReply.h create mode 100644 libraries/networking/src/QmlNetworkAccessManager.cpp create mode 100644 libraries/networking/src/QmlNetworkAccessManager.h diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 8c167fafdc..ebccc8a1fc 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "OffscreenGLCanvas.h" #include "GLEscrow.h" @@ -402,6 +403,8 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { // Create a QML engine. _qmlEngine = new QQmlEngine; + _qmlEngine->setNetworkAccessManagerFactory(new QmlNetworkAccessManagerFactory); + auto importList = _qmlEngine->importPathList(); importList.insert(importList.begin(), PathUtils::resourcesPath()); _qmlEngine->setImportPathList(importList); diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 6b94ee152a..a8311c6146 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -33,12 +33,7 @@ bool AssetResourceRequest::urlIsAssetHash() const { } void AssetResourceRequest::doSend() { - auto parts = _url.path().split(".", QString::SkipEmptyParts); - auto hash = parts.length() > 0 ? parts[0] : ""; - auto extension = parts.length() > 1 ? parts[1] : ""; - // We'll either have a hash or an ATP path to a file (that maps to a hash) - if (urlIsAssetHash()) { // We've detected that this is a hash - simply use AssetClient to request that asset auto parts = _url.path().split(".", QString::SkipEmptyParts); diff --git a/libraries/networking/src/QmlAtpReply.cpp b/libraries/networking/src/QmlAtpReply.cpp new file mode 100644 index 0000000000..fd1e1e5023 --- /dev/null +++ b/libraries/networking/src/QmlAtpReply.cpp @@ -0,0 +1,88 @@ +// +// QmlAtpReply.cpp +// libraries/networking/src +// +// Created by Zander Otavka on 8/4/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ResourceManager.h" +#include "QmlAtpReply.h" + +QmlAtpReply::QmlAtpReply(const QUrl& url, QObject* parent) : + _resourceRequest(ResourceManager::createResourceRequest(parent, url)) { + setOperation(QNetworkAccessManager::GetOperation); + + connect(_resourceRequest, &AssetResourceRequest::progress, this, &QmlAtpReply::downloadProgress); + connect(_resourceRequest, &AssetResourceRequest::finished, this, &QmlAtpReply::handleRequestFinish); + + _resourceRequest->send(); +} + +QmlAtpReply::~QmlAtpReply() { + if (_resourceRequest) { + _resourceRequest->deleteLater(); + _resourceRequest = nullptr; + } +} + +qint64 QmlAtpReply::bytesAvailable() const { + return _content.size() - _readOffset + QIODevice::bytesAvailable(); +} + +qint64 QmlAtpReply::readData(char* data, qint64 maxSize) { + if (_readOffset < _content.size()) { + qint64 readSize = qMin(maxSize, _content.size() - _readOffset); + memcpy(data, _content.constData() + _readOffset, readSize); + _readOffset += readSize; + return readSize; + } else { + return -1; + } +} + +void QmlAtpReply::handleRequestFinish() { + Q_ASSERT(_resourceRequest->getState() == ResourceRequest::State::Finished); + + switch (_resourceRequest->getResult()) { + case ResourceRequest::Result::Success: + setError(NoError, "Success"); + _content = _resourceRequest->getData(); + break; + case ResourceRequest::Result::InvalidURL: + setError(ContentNotFoundError, "Invalid URL"); + break; + case ResourceRequest::Result::NotFound: + setError(ContentNotFoundError, "Not found"); + break; + case ResourceRequest::Result::ServerUnavailable: + setError(ServiceUnavailableError, "Service unavailable"); + break; + case ResourceRequest::Result::AccessDenied: + setError(ContentAccessDenied, "Access denied"); + break; + case ResourceRequest::Result::Timeout: + setError(TimeoutError, "Timeout"); + break; + default: + setError(UnknownNetworkError, "Unknown error"); + break; + } + + open(ReadOnly | Unbuffered); + setHeader(QNetworkRequest::ContentLengthHeader, QVariant(_content.size())); + + if (error() != NoError) { + emit error(error()); + } + + setFinished(true); + emit readyRead(); + emit finished(); + + _resourceRequest->deleteLater(); + _resourceRequest = nullptr; +} \ No newline at end of file diff --git a/libraries/networking/src/QmlAtpReply.h b/libraries/networking/src/QmlAtpReply.h new file mode 100644 index 0000000000..7b0fa5686e --- /dev/null +++ b/libraries/networking/src/QmlAtpReply.h @@ -0,0 +1,40 @@ +// +// QmlAtpReply.h +// libraries/networking/src +// +// Created by Zander Otavka on 8/4/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_QmlAtpReply_h +#define hifi_QmlAtpReply_h + +#include +#include + +#include "AssetResourceRequest.h" + +class QmlAtpReply : public QNetworkReply { + Q_OBJECT +public: + QmlAtpReply(const QUrl& url, QObject* parent = Q_NULLPTR); + ~QmlAtpReply(); + qint64 bytesAvailable() const override; + void abort() override { } + bool isSequential() const override { return true; } + +protected: + qint64 readData(char* data, qint64 maxSize) override; + +private: + void handleRequestFinish(); + + ResourceRequest* _resourceRequest { nullptr }; + QByteArray _content; + qint64 _readOffset { 0 }; +}; + +#endif // hifi_QmlAtpReply_h \ No newline at end of file diff --git a/libraries/networking/src/QmlNetworkAccessManager.cpp b/libraries/networking/src/QmlNetworkAccessManager.cpp new file mode 100644 index 0000000000..9e86d3b85f --- /dev/null +++ b/libraries/networking/src/QmlNetworkAccessManager.cpp @@ -0,0 +1,29 @@ +// +// QmlNetworkAccessManager.cpp +// +// +// Created by Clement on 7/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "QmlAtpReply.h" +#include "QmlNetworkAccessManager.h" + +QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) { + return new QmlNetworkAccessManager(parent); +} + +QNetworkReply* QmlNetworkAccessManager::createRequest(Operation operation, const QNetworkRequest& request, QIODevice* device) { + if (request.url().scheme() == "atp" && operation == GetOperation) { + return new QmlAtpReply(request.url()); + //auto url = request.url().toString(); + //return QNetworkAccessManager::createRequest(operation, request, device); + } else { + return QNetworkAccessManager::createRequest(operation, request, device); + } +} \ No newline at end of file diff --git a/libraries/networking/src/QmlNetworkAccessManager.h b/libraries/networking/src/QmlNetworkAccessManager.h new file mode 100644 index 0000000000..72ca0a4cb4 --- /dev/null +++ b/libraries/networking/src/QmlNetworkAccessManager.h @@ -0,0 +1,33 @@ +// +// QmlNetworkAccessManager.h +// +// +// Created by Clement on 7/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_QmlNetworkAccessManager_h +#define hifi_QmlNetworkAccessManager_h + +#include +#include +#include + +class QmlNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory { +public: + QNetworkAccessManager* create(QObject* parent); +}; + + +class QmlNetworkAccessManager : public QNetworkAccessManager { + Q_OBJECT +public: + QmlNetworkAccessManager(QObject* parent = Q_NULLPTR) : QNetworkAccessManager(parent) { } +protected: + QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* device = Q_NULLPTR); +}; + +#endif // hifi_QmlNetworkAccessManager_h \ No newline at end of file diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index c3ca5f54d9..554ae7d8c2 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -59,9 +59,10 @@ QVariantMap QmlWindowClass::parseArguments(QScriptContext* context) { properties = context->argument(0).toVariant().toMap(); } - QString url = properties[SOURCE_PROPERTY].toString(); - if (!url.startsWith("http") && !url.startsWith("file://") && !url.startsWith("about:")) { - properties[SOURCE_PROPERTY] = QUrl::fromLocalFile(url).toString(); + QUrl url { properties[SOURCE_PROPERTY].toString() }; + if (url.scheme() != "http" && url.scheme() != "https" && url.scheme() != "file" && url.scheme() != "about" && + url.scheme() != "atp") { + properties[SOURCE_PROPERTY] = QUrl::fromLocalFile(url.toString()).toString(); } return properties; From 246a8457e176f8b8e95e098c9a10dc9c59917af3 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 8 Aug 2016 11:52:04 -0700 Subject: [PATCH 071/279] Add some asserts --- libraries/networking/src/ResourceCache.cpp | 1 + libraries/script-engine/src/ScriptCache.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 36828b3992..5fb686ca6f 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -462,6 +462,7 @@ int ResourceCache::getPendingRequestCount() { } bool ResourceCache::attemptRequest(QSharedPointer resource) { + Q_ASSERT(!resource.isNull()); auto sharedItems = DependencyManager::get(); if (_requestsActive >= _requestLimit) { diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 40234e8134..91d7f36102 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -167,6 +167,7 @@ void ScriptCache::scriptContentAvailable() { Lock lock(_containerLock); allCallbacks = _contentCallbacks.values(url); _contentCallbacks.remove(url); + Q_ASSERT(req->getState() == ResourceRequest::Finished); success = req->getResult() == ResourceRequest::Success; if (success) { From aebd18db7f50e207489e7a7da76f3ccadbd28baf Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 8 Aug 2016 13:38:14 -0700 Subject: [PATCH 072/279] Add EOF newlines --- libraries/networking/src/QmlAtpReply.cpp | 2 +- libraries/networking/src/QmlAtpReply.h | 2 +- libraries/networking/src/QmlNetworkAccessManager.cpp | 2 +- libraries/networking/src/QmlNetworkAccessManager.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/QmlAtpReply.cpp b/libraries/networking/src/QmlAtpReply.cpp index fd1e1e5023..a2e537ba1f 100644 --- a/libraries/networking/src/QmlAtpReply.cpp +++ b/libraries/networking/src/QmlAtpReply.cpp @@ -85,4 +85,4 @@ void QmlAtpReply::handleRequestFinish() { _resourceRequest->deleteLater(); _resourceRequest = nullptr; -} \ No newline at end of file +} diff --git a/libraries/networking/src/QmlAtpReply.h b/libraries/networking/src/QmlAtpReply.h index 7b0fa5686e..a8f6dfde14 100644 --- a/libraries/networking/src/QmlAtpReply.h +++ b/libraries/networking/src/QmlAtpReply.h @@ -37,4 +37,4 @@ private: qint64 _readOffset { 0 }; }; -#endif // hifi_QmlAtpReply_h \ No newline at end of file +#endif // hifi_QmlAtpReply_h diff --git a/libraries/networking/src/QmlNetworkAccessManager.cpp b/libraries/networking/src/QmlNetworkAccessManager.cpp index 9e86d3b85f..ef44eb8526 100644 --- a/libraries/networking/src/QmlNetworkAccessManager.cpp +++ b/libraries/networking/src/QmlNetworkAccessManager.cpp @@ -26,4 +26,4 @@ QNetworkReply* QmlNetworkAccessManager::createRequest(Operation operation, const } else { return QNetworkAccessManager::createRequest(operation, request, device); } -} \ No newline at end of file +} diff --git a/libraries/networking/src/QmlNetworkAccessManager.h b/libraries/networking/src/QmlNetworkAccessManager.h index 72ca0a4cb4..9b817d612e 100644 --- a/libraries/networking/src/QmlNetworkAccessManager.h +++ b/libraries/networking/src/QmlNetworkAccessManager.h @@ -30,4 +30,4 @@ protected: QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* device = Q_NULLPTR); }; -#endif // hifi_QmlNetworkAccessManager_h \ No newline at end of file +#endif // hifi_QmlNetworkAccessManager_h From cb153f2a29f59297fa8566918997a07335f741fb Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 8 Aug 2016 15:17:17 -0700 Subject: [PATCH 073/279] Fix file header comments --- libraries/networking/src/QmlNetworkAccessManager.cpp | 4 ++-- libraries/networking/src/QmlNetworkAccessManager.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/QmlNetworkAccessManager.cpp b/libraries/networking/src/QmlNetworkAccessManager.cpp index ef44eb8526..575bc02f8c 100644 --- a/libraries/networking/src/QmlNetworkAccessManager.cpp +++ b/libraries/networking/src/QmlNetworkAccessManager.cpp @@ -1,8 +1,8 @@ // // QmlNetworkAccessManager.cpp +// libraries/networking/src // -// -// Created by Clement on 7/1/14. +// Created by Zander Otavka on 8/4/16. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. diff --git a/libraries/networking/src/QmlNetworkAccessManager.h b/libraries/networking/src/QmlNetworkAccessManager.h index 9b817d612e..059d0ebba0 100644 --- a/libraries/networking/src/QmlNetworkAccessManager.h +++ b/libraries/networking/src/QmlNetworkAccessManager.h @@ -1,8 +1,8 @@ // // QmlNetworkAccessManager.h +// libraries/networking/src // -// -// Created by Clement on 7/1/14. +// Created by Zander Otavka on 8/4/16. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. From d36e60bbaa84e1b0bd6530a552bc27796025b7a0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 8 Aug 2016 15:20:58 -0700 Subject: [PATCH 074/279] eslintrc: accept spaces after function keyword in anonymous functions Anonymous functions `function () {}` and `function() {}` will both be accepted by eslint. --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 82dfe9e9bd..6183fa8aec 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -72,6 +72,6 @@ module.exports = { "spaced-comment": ["error", "always", { "line": { "markers": ["/"] } }], - "space-before-function-paren": ["error", {"anonymous": "always", "named": "never"}] + "space-before-function-paren": ["error", {"anonymous": "ignore", "named": "never"}] } }; From 45921a5c8db47ed6e5266f9903eee71acb9da7d2 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 20 Jun 2016 14:15:45 -0700 Subject: [PATCH 075/279] Restore split behavior for inputs/displays on quit --- plugins/openvr/src/OpenVrDisplayPlugin.cpp | 5 ++++- plugins/openvr/src/OpenVrHelpers.cpp | 8 +++++++- plugins/openvr/src/ViveControllerManager.cpp | 4 ++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 4e84c6d0fa..a532261014 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -161,7 +161,10 @@ static bool isBadPose(vr::HmdMatrix34_t* mat) { bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) { handleOpenVrEvents(); - + if (openVrQuitRequested()) { + QMetaObject::invokeMethod(qApp, "quit"); + return false; + } double displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float); double frameDuration = 1.f / displayFrequency; double vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float); diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index c93a2178b5..ee61a07da6 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -34,6 +34,11 @@ using Lock = std::unique_lock; static int refCount { 0 }; static Mutex mutex; static vr::IVRSystem* activeHmd { nullptr }; +static bool _openVrQuitRequested { false }; + +bool openVrQuitRequested() { + return _openVrQuitRequested; +} static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000; @@ -99,6 +104,7 @@ void releaseOpenVrSystem() { qCDebug(displayplugins) << "OpenVR: zero refcount, deallocate VR system"; #endif vr::VR_Shutdown(); + _openVrQuitRequested = false; activeHmd = nullptr; } } @@ -257,8 +263,8 @@ void handleOpenVrEvents() { while (activeHmd->PollNextEvent(&event, sizeof(event))) { switch (event.eventType) { case vr::VREvent_Quit: + _openVrQuitRequested = true; activeHmd->AcknowledgeQuit_Exiting(); - QMetaObject::invokeMethod(qApp, "quit"); break; case vr::VREvent_KeyboardDone: diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index 85feebda11..930b3dd450 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -214,6 +214,10 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { auto userInputMapper = DependencyManager::get(); handleOpenVrEvents(); + if (openVrQuitRequested()) { + deactivate(); + return; + } // because update mutates the internal state we need to lock userInputMapper->withLock([&, this]() { From cea0e182c0b24928aae74352355fe552167dcf4c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 14 Jul 2016 11:02:32 -0700 Subject: [PATCH 076/279] Interface login through steam --- interface/resources/images/steam-sign-in.png | Bin 0 -> 5861 bytes interface/resources/qml/LoginDialog.qml | 50 +++-- interface/src/Application.cpp | 7 +- interface/src/main.cpp | 9 +- interface/src/ui/LoginDialog.cpp | 15 ++ interface/src/ui/LoginDialog.h | 1 + libraries/networking/src/AccountManager.cpp | 23 +++ libraries/networking/src/AccountManager.h | 1 + .../src/steamworks-wrapper/SteamClient.cpp | 171 ++++++++++++++++++ .../src/steamworks-wrapper/SteamClient.h | 15 +- 10 files changed, 275 insertions(+), 17 deletions(-) create mode 100644 interface/resources/images/steam-sign-in.png diff --git a/interface/resources/images/steam-sign-in.png b/interface/resources/images/steam-sign-in.png new file mode 100644 index 0000000000000000000000000000000000000000..148e6ab280472d5b4b3f069a2c267790e19d13c2 GIT binary patch literal 5861 zcmVb^P)`OA)6Ov3q zNJ0{}gg}rTBA|j15CJI&ZYUtNb!oLerCO9)D{A@erc{9{{w)f!2#6s276=3r!jg~> zvqBcK%w(3#{(tApWXOhz7B&74C(mKd+<V9tuRpDPgM%0}v;zKqfw#7;2bD%$J?gTxT*0~o(&=<^5M@d+N*Ye1sk!#n z4)%cJs|Z1;It~G;WgN`z011u87o`nnVX)i+v8NQBsRj;*3%vu9QA-$g3rMHa0lUKj zm(z7?2YXOyGS|TA=inGg#ta+ujh!dnGaF8qgM&!@e{#iCD?n-vf=1E{Qi&24n+baB zRX9`)P=$SW!Bon8F>m;5h!4Gue=n-Z!j?0yAu=F|AAe>0M3}8j-)Zb8Cw$1q`{LZ+ zy6nraaX1{`tdWe-GPE5oI|tGE6VgaiwFtfCqcLar1JL;S!)~{+1wjxAaM~TXR$GI^ zxm!``Itg{S<6DnEEM^8G{gbeK8v{&_;juGtt*L_7+_Js@)xG~^?}Bdkbz?&sdGu6X zryZ6oS(G4?D|*@Ob~wniI$h46jA^x%1!Hl>>j>}`-FEqd&p`yX8N zPFPr&HYqUyp&`K#1c9HENv5N$tQemhTm>Qe3Pj?b38J}1is$G2m1`hdoFIs>arfU* zVa*zxpO4iz(%W=7WA0`c%i>Kzkg@0^V27Ag2QZt> zuvxA6XwM3meZEGJq(4dW?eI|spk7yvbESt-tvrL(pFfL5cfSb-^;P@$V5D{qvW)8> zR(9<|f_x&O4eF2F(l5#FxV+%Dcpt_mOcp(Y)RDZ+Oe5>o!ZVyVXt&nL?)2rRm8>Qc zM#Lo~ZAczC0CBN>@YUIK*!Ul$voMd%~=2C3KW){#{*NIgSFC^57v{a z?V1u96BeMcu^uv+3_Fjlg-Kn4@!Fqb>FkwAP8^9CZ34NEaacHQImU)AfmKEt^mqVRGtva+(iN=!^j9WYR5W8`m0ZF?I4BZ25d0E=jlcD2)s>48fBRtVB&s6>4j$vElS@5v>}ANALR` zu2hzTn-IoY$z3pyp;D=_?aQ?&wC=@&Bj19rTthBHgl+p*quTG}O}nqCpnfQ?yNH)& zZO8F~&Dfi_?xwCgCagaScLqQb7#zAJI!X)1PIL3lK^qm7I%IINHyOY`Z8i+*Ka~3& zT|)x~Cym9qFOLGDHIOQMhK-E~iYHQIghVVsUhy$5!PCb*&Ov3q(rvG zq43Z_9M9X18RHj|R+HrlN=s1)SOj5lYq zzsy<7>-SmgP8<11Ms@^2&ZJmxY+@!yB^67F(zXZE$<0yA@+=k$m+}FLLyIWt|wSGvuJsnM?Wg@ySiAW9-J-SaTzjv3@ErbI56A5k7j}GGa zQjwBNwVl4(?Dn5j28$&(m^Bp>O_VWwEv`LR*>Gc0Wdiaw-r55IT!IKU8eygPkm^a}?5;6&_RyzkSA|Meqml9DJ_z*NTHSzNeP1POd7?m`OSJocf=T7{6aV`{6 z&G)`GHV2!F&3U6fb_zW>kQPhxrmI!eP%4xN@DI>l$iGCuxi_Sxx;MZBD48D{tJH^W zkexow&1MQ&^e_?gOYA!skIh(tpQZf~k>+$XUy%{LceAeVqoxE-M^t1YvDHStAR`6& zNpV9Uw)kTA*>}156e#eud)C3gX6A(j(tC5)68uVxPMwTK3boi;su~MB%#jp1oL9~q zTd~p~qIzS;8=SW~Y#dDS9d}JRXEK?d5AW3r!6Cu;x}*eE)iqG7ReTeeNoppbm|<#3 z)p2m`Zga)Hvo?#R*m0}P%E4AB)#w*brnk>TNDP6ft*nRHriV!8_E9&Z3U#4)c*b%v z?N|7{**${A?$u8gfue>ZIF+{-eL@GrNA1s@q?ox8T|GWNxDpm+4W=aj0urYZ&6Z}E zi7e$*?|@5Y^Ug6O?oLb{z7!FGaYUr#Xf)Iyt?ym@_?)r7;m0nNWn#w2XRv7UoAmiK zCZs%o(}lZ{q#c3hXMV_!#f7IL^WtZ?YsjN`j*jik`3Qqz#$)-st-Q_7zxv=V+;iI# z{Cb{}yK)AThb}~PNPj+8%NVWqKqXV6HeU|O@)v(sx9p{tR^*<|dogWDDuxeB!-f0; zoIG_Jz7!-!X|)Io4WSLPa=O5i&-BS;MG!f;<76O;L?SNf?0}eSfP*&2LOx5t;DPB# z8$1c6Wd%5WVHd8NFF+E|3{ED$Yd%#k#*&N`6mHbQLIJ&t>`V(8V{RZSA_=3-3vsmg zU-%^JcaYhGAccw~*a(ZH0b+?5lMcc4{vWi zx(Y-4-pSkl{QOtw7d-;^jeZ7SoLa;8!PuB=s9fLD!2?4h?}J$CgZk=f zJ~q3@mT-xQQ**P0OzuF;4SyX+&TK@rr2uAu<`OFqs~LsKqaK36U?kxxp_tN#V(70> z={gO;<-ESZ?GofXSofT(ZY)QTM(fG8xYK4P0+RKqZYbyMS4FX@u~~<&tIqRtO1Z{+ zz0>7t(`J2ZX-u9GqP}ix=k8F`Si#5eSlKq29UC*PEkJs}T)2G29|}%poq9beFc2&x zQ7V;Ox_2D;8_cRoxI}hj76hSx)a{6mPK8{i;H1apYPDb%a1&!>0J8v_X(WAxL@MQ} zFcS%gl-eWGGB8#=4OKOzICT0G)Y|hQ(b&89nnQCO(nrt5<;$1dAq7!zcvv<2&geMX z1PiwaB8*6x$%T(sv6%bz(z0@{#Vj#UR&fC*D*p|!pUs=}ZeQ3&xAt4@+ivi$A2XVC z91su*&p75hpAv3#@z+7Ej zQ{hG20&3`v>{UrnHg7}>lU$^aBO#U9NR zxVA9r&;IN*xn9@AV>drdIA-7d8#J2?$jLu|oZ{VZYDj}`@TAnClVMiIb0{t@cE`5t zrUZdYrOV?uoF47u7J>PAb2C{fmnWWdaWGlP#qK9FXDiAbCn525-Z1RQ%HNHp)BcQ^ zW1dAxwMR3YxGun+Hn)bY#`xEs<#_1M<(P8YLR=`##DScRP!mYwl4s+k`6RrJ72fMN z9$JN8-~T?={OUY6(R@6om+Q1?x1NKon-Nc8blSjglzxdi*cCsufqb3~oxM6~YZ5_j z2_sfF4skK5p22Ny6Gz&>6p{hyR#pO)Hc>_z$&b<2GQiomEKE?T)m*xY%W{xgxEHmy z0!VylgLO(o`457>IvjmthLR-+q_C&a?TeibkCeKBE@5X`Ljo5kO{VuPON@OV79?&(Ako&vhGwRXR z`>zhhg$%xk)TAJx_b6!mgUFZJJ&%*X0c6@J@|ZWKjcfLB*q zvjY2!k&og2+n?p_^IvD;?^~YXn%Pxh>n_?E*fo%Lv5%|o)W&beD0LD9i2I^%>)B_rpvErWZ(373RMf$ZYd@@`22SL@I++Cgl%+MS>IO3l5;9?hNlA9W)e4 zvFSw0OkQnl4lmkj8+MOoUt9atWcXmju7x2!BqbSX^a zi}G=)FNpyOiueIblJ)wllljs)Q`CevL1(5=MqSD|miAG7& z5ttf{hzJ`%gvRXEOodW`iK>N1gNpTPSvEwov6IFY{wcAsmc?&!coGe$jv^dYn0qx3_w)r2zz`?3Al z`)F*s#?L=>|J#VyCUcM-6tJ@Jg}DQZ)r?oxEr8xw$NMGqPQ&7R|Hv&1%NDg1y|ErI zubD^I#L{68o|*GkM25uS<+bzCWUB9Qe#ZEv7&rJ{d~squcAs2-Jyh|J#0&G^gU(Qg zRUbcz@KCTUnipW0&89O?}Nv*<07UM?Ql2cfEk{ zz!(mEpl=vI78jo4?W2&XsH&k+1)x`8Zv^>;dyc6%Xf6GG6#o2tNPyOR4X^Cck4xw< zcX(hdh9*tqKn_iu(COZU1w`{Pr;lC4#KU`h&V;A=b=uH)icO6uuPSV_K70E(vQsw7 zf&3o$IWxOz9V+ZcQF`hGLKKM@7?%#Mwx2sWZ?Sp_kyI+UU+19XJa(i%rYV9`%F|df zsf5gpfKvSth?F-D>eEwaa!q@A%>opZX2ah%gm2Ed%bDIjE7vdKVbhqDsd(bP*Kqpc zKK$jAUwMSu+U5}$_fI9+cxBD}>&qhTWiLauFQb$1;o%Z1w-1?#{iil|qBQRD1AId< zW%vT@IJOQ!nlPmGo8&#uI78{x0$y8V)U|2l!S(k`_XOJfYdD7km(D|+r@_ena}Y~0 zsfA)bCf!b_+q}EuQ5Tm$nM_U^c!|f65n-|Xbq(UJ{ZH|i^go^4S)az<^Z9~wU zHct-I>IIBxe+MGxnTN%}EOG8m4GP>`cQ)s!K^bJ8P0*3)?GrJOzzU;ywyNEGbJxq= zqaVg+N7iC``Vy}3p1hIwU7hwAiMpeaKg4rI&TT{ll2V7`7dG&Oy-Xs&WWEM}e}6K) zYA6*7s8uQi1_V%N3$hBgAXE{Lu+T`G!M zNB&cS_I+Dt2|B%3&t4`(r((^rEMDJVb`m27PU+CMPsAXE2gl%S(Q*7|&nh0oPZ~D& zhGSOOmvfUo_s(ZAlEyPNH*p^=qWGz^dvSEjM>-rg)kk)x+9Y~hDmsP8$S4$jorMp# zzJ$}cpL0+Rh8mp8*?~_Fy-Lhej7Mg^2wg)R%IdQ{8J-)DyZ!Jwyt)1tIL_W?xKaR> z(w8=LHtwJJ%Z~kBZPR0&Rrf291~PN`yp~lJ5NWFK(C?1he#ZOmKeZW$PJhO=cI2SR z9r`gQ&C2e~e-n5H%ph&lr~~oY;k7W+Jj~>Gb|3bxe*59pNXHNVn-5yguB_E$UK25E z`cizn^{+TvvJW{Wds;X+diliR$p>G9zR85o54{Jyv#MJ|AItrMS62E)CF6~!Ho9@N3K`Gx+d4mQ**P z@=7s(E*hnchgz*cd3h-|?tB|pZFybhOPQa#d+dXVAQQLa$OkYO8!;f({Yxr?sj=f4 zTc;#DTfgU+QszTUGur!BK8x>a>MOh{&6I>OT&q|((mNuF`%xCw2!aCRhfJsVI;X>& zN@BnN*|myTc^nR&+KMJa1Ie!F`n>IdbaaQca)55hI%~y%6Kw`Zqjpa`eW`VA=_TZzLT4Ho880P&+owaw0rT< z&Q&m)^_~Q{n=~opJ~WOFFaFK_t1P8lP0U>0#$UI`=!U<>YAG6x=EwaNme_$>dm%QT zcnfI%x~U9pp5}j3X)-k8`Bk%#F>V17loXzHxLR9^Z3otpZ!7H3zwAm8PUd}yukw#| zx_)*oJ1?-be5*I}^rSs3Ok&rtSh%vf7}pxBu;=*4oUpJ`Tz&yt_PtLU7fb9lgsg-6 zIc09aaSmE(<)t=rG6sG4%nttP^t_hZ9!R09ry4#!KCbZ(h2U1{x#iFYwcRtVF6XVy z>dD2?9oEVMVvAk^sgLc}j`hG|Gf3TUUvy #include #include +#include #include #include #include #include #include -#include +#include #include #include -#include +#include #include #include #include @@ -2910,6 +2911,8 @@ void Application::idle(float nsecsElapsed) { PROFILE_RANGE(__FUNCTION__); + SteamClient::runCallbacks(); + float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND; // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus. diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 8fc0384aee..527b7f2331 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -8,6 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include #include @@ -20,12 +22,13 @@ #include #include +#include + #include "AddressManager.h" #include "Application.h" #include "InterfaceLogging.h" #include "UserActivityLogger.h" #include "MainWindow.h" -#include #ifdef HAS_BUGSPLAT #include @@ -137,6 +140,8 @@ int main(int argc, const char* argv[]) { // or in the main window ctor, before GL startup. Application::initPlugins(arguments); + SteamClient::init(); + int exitCode; { QSettings::setDefaultFormat(QSettings::IniFormat); @@ -202,6 +207,8 @@ int main(int argc, const char* argv[]) { Application::shutdownPlugins(); + SteamClient::shutdown(); + qCDebug(interfaceapp, "Normal exit."); #if !defined(DEBUG) && !defined(Q_OS_LINUX) // HACK: exit immediately (don't handle shutdown callbacks) for Release build diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 80d52b7a07..8240340381 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -14,6 +14,7 @@ #include #include +#include #include "AccountManager.h" #include "DependencyManager.h" @@ -82,6 +83,20 @@ void LoginDialog::login(const QString& username, const QString& password) { DependencyManager::get()->requestAccessToken(username, password); } +void LoginDialog::loginThroughSteam() { + qDebug() << "Attempting to login through Steam"; + setStatusText("Logging in..."); + + SteamClient::requestTicket([this](Ticket ticket) { + if (ticket.isNull()) { + setStatusText("Steam client not logged into an account"); + return; + } + + DependencyManager::get()->requestAccessTokenWithSteam(ticket); + }); +} + void LoginDialog::openUrl(const QString& url) { qDebug() << url; QDesktopServices::openUrl(url); diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h index 25ecf45898..0dd4b5e96f 100644 --- a/interface/src/ui/LoginDialog.h +++ b/interface/src/ui/LoginDialog.h @@ -41,6 +41,7 @@ protected: void handleLoginFailed(); Q_INVOKABLE void login(const QString& username, const QString& password); + Q_INVOKABLE void loginThroughSteam(); Q_INVOKABLE void openUrl(const QString& url); private: QString _statusText; diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index c4bfae7cac..8c0fa5ed92 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -505,6 +505,29 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); } +void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter()); + + QUrl grantURL = _authURL; + grantURL.setPath("/oauth/token"); + + const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner"; + + QByteArray postData; + postData.append("grant_type=password&"); + postData.append("ticket=" + QUrl::toPercentEncoding(authSessionTicket) + "&"); + postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE); + + request.setUrl(grantURL); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + + QNetworkReply* requestReply = networkAccessManager.post(request, postData); + connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished); + connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError))); +} void AccountManager::requestAccessTokenFinished() { QNetworkReply* requestReply = reinterpret_cast(sender()); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index 846cdb6220..eb4d224501 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -96,6 +96,7 @@ public: public slots: void requestAccessToken(const QString& login, const QString& password); + void requestAccessTokenWithSteam(QByteArray authSessionTicket); void requestAccessTokenFinished(); void requestProfileFinished(); diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index 0f06e03672..1dbfc0ce00 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -11,9 +11,180 @@ #include "SteamClient.h" +#include +#include + +#include + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverloaded-virtual" +#endif + #include +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + +static const Ticket INVALID_TICKET = Ticket(); + +class SteamTicketRequests { +public: + SteamTicketRequests(); + ~SteamTicketRequests(); + + HAuthTicket startRequest(TicketRequestCallback callback); + void stopRequest(HAuthTicket authTicket); + + STEAM_CALLBACK(SteamTicketRequests, onGetAuthSessionTicketResponse, + GetAuthSessionTicketResponse_t, _getAuthSessionTicketResponse); + +private: + void stopAll(); + + struct PendingTicket { + HAuthTicket authTicket; + Ticket ticket; + TicketRequestCallback callback; + }; + + std::vector _pendingTickets; +}; + +SteamTicketRequests::SteamTicketRequests() : + _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse) +{ +} + +SteamTicketRequests::~SteamTicketRequests() { + stopAll(); +} + +HAuthTicket SteamTicketRequests::startRequest(TicketRequestCallback callback) { + static const uint32 MAX_TICKET_SIZE { 1024 }; + uint32 ticketSize { 0 }; + char ticket[MAX_TICKET_SIZE]; + + auto authTicket = SteamUser()->GetAuthSessionTicket(ticket, MAX_TICKET_SIZE, &ticketSize); + qDebug() << "Got Steam auth session ticket:" << authTicket; + + if (authTicket == k_HAuthTicketInvalid) { + qWarning() << "Auth session ticket is invalid."; + callback(INVALID_TICKET); + } else { + PendingTicket pendingTicket{ authTicket, QByteArray(ticket, ticketSize).toHex(), callback }; + _pendingTickets.push_back(pendingTicket); + } + + return authTicket; +} + +void SteamTicketRequests::stopRequest(HAuthTicket authTicket) { + auto it = std::find_if(_pendingTickets.begin(), _pendingTickets.end(), [&authTicket](const PendingTicket& pendingTicket) { + return pendingTicket.authTicket == authTicket; + }); + + if (it != _pendingTickets.end()) { + SteamUser()->CancelAuthTicket(it->authTicket); + it->callback(INVALID_TICKET); + _pendingTickets.erase(it); + } +} + +void SteamTicketRequests::stopAll() { + auto steamUser = SteamUser(); + if (steamUser) { + for (const auto& pendingTicket : _pendingTickets) { + steamUser->CancelAuthTicket(pendingTicket.authTicket); + pendingTicket.callback(INVALID_TICKET); + } + } + _pendingTickets.clear(); +} + +void SteamTicketRequests::onGetAuthSessionTicketResponse(GetAuthSessionTicketResponse_t* pCallback) { + auto authTicket = pCallback->m_hAuthTicket; + + auto it = std::find_if(_pendingTickets.begin(), _pendingTickets.end(), [&authTicket](const PendingTicket& pendingTicket) { + return pendingTicket.authTicket == authTicket; + }); + + + if (it != _pendingTickets.end()) { + + if (pCallback->m_eResult == k_EResultOK) { + qDebug() << "Got steam callback, auth session ticket is valid. Send it." << authTicket; + it->callback(it->ticket); + } else { + qWarning() << "Steam auth session ticket callback encountered an error:" << pCallback->m_eResult; + it->callback(INVALID_TICKET); + } + + _pendingTickets.erase(it); + } else { + qWarning() << "Could not find steam auth session ticket in list of pending tickets:" << authTicket; + } +} +static std::atomic_bool initialized { false }; +static std::unique_ptr steamTicketRequests; + +bool SteamClient::init() { + if (!initialized) { + initialized = SteamAPI_Init(); + } + + if (!steamTicketRequests && initialized) { + steamTicketRequests.reset(new SteamTicketRequests()); + } + + return initialized; +} + +void SteamClient::shutdown() { + if (initialized) { + SteamAPI_Shutdown(); + } + + if (steamTicketRequests) { + steamTicketRequests.reset(); + } +} + +void SteamClient::runCallbacks() { + if (!initialized) { + init(); + } + + if (!initialized) { + qDebug() << "Steam not initialized"; + return; + } + + auto steamPipe = SteamAPI_GetHSteamPipe(); + if (!steamPipe) { + qDebug() << "Could not get SteamPipe"; + return; + } + + Steam_RunCallbacks(steamPipe, false); +} + +void SteamClient::requestTicket(TicketRequestCallback callback) { + if (!initialized) { + init(); + } + + if (!initialized) { + qDebug() << "Steam not initialized"; + return; + } + + steamTicketRequests->startRequest(callback); +} + diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h index 369641b0c7..ac5c648ead 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h @@ -13,8 +13,21 @@ #ifndef hifi_SteamClient_h #define hifi_SteamClient_h -class SteamClient { +#include +#include + +using Ticket = QByteArray; +using TicketRequestCallback = std::function; + +class SteamClient { +public: + static bool init(); + static void shutdown(); + + static void runCallbacks(); + + static void requestTicket(TicketRequestCallback callback); }; From 205df0cf5170a1ab663dd7ca94f7c0571f917f1d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 20 Jul 2016 10:23:10 -0700 Subject: [PATCH 077/279] login UI first draft --- interface/resources/qml/LoginDialog.qml | 355 ++++-------------- .../qml/LoginDialog/CompleteProfileBody.qml | 93 +++++ .../qml/LoginDialog/EmailSentBody.qml | 83 ++++ .../qml/LoginDialog/LinkAccountBody.qml | 165 ++++++++ .../qml/LoginDialog/RecoverPasswordBody.qml | 106 ++++++ .../resources/qml/LoginDialog/SignInBody.qml | 110 ++++++ .../qml/LoginDialog/UsernameCollisionBody.qml | 162 ++++++++ .../resources/qml/LoginDialog/WelcomeBody.qml | 83 ++++ interface/resources/qml/LoginDialogSave.qml | 197 ++++++++++ .../qml/controls-uit/HorizontalRule.qml | 20 + .../qml/controls-uit/HorizontalSpacer.qml | 21 ++ .../qml/controls-uit/VerticalSpacer.qml | 3 + .../resources/qml/dialogs/MessageDialog.qml | 6 +- .../resources/qml/styles-uit/ButtonLabel.qml | 18 + .../resources/qml/styles-uit/IconButton.qml | 20 + .../resources/qml/styles-uit/InputLabel.qml | 18 + .../resources/qml/styles-uit/ListItem.qml | 18 + interface/resources/qml/styles-uit/Logs.qml | 18 + .../resources/qml/styles-uit/MenuItem.qml | 19 + .../resources/qml/styles-uit/OverlayTitle.qml | 18 + .../resources/qml/styles-uit/SectionName.qml | 19 + .../resources/qml/styles-uit/ShortcutText.qml | 18 + .../resources/qml/styles-uit/TabName.qml | 19 + .../qml/styles-uit/TextFieldInput.qml | 18 + interface/src/Application.cpp | 9 +- interface/src/ui/LoginDialog.cpp | 33 +- interface/src/ui/LoginDialog.h | 17 +- .../networking/src/NetworkingConstants.h | 2 +- 28 files changed, 1321 insertions(+), 347 deletions(-) create mode 100644 interface/resources/qml/LoginDialog/CompleteProfileBody.qml create mode 100644 interface/resources/qml/LoginDialog/EmailSentBody.qml create mode 100644 interface/resources/qml/LoginDialog/LinkAccountBody.qml create mode 100644 interface/resources/qml/LoginDialog/RecoverPasswordBody.qml create mode 100644 interface/resources/qml/LoginDialog/SignInBody.qml create mode 100644 interface/resources/qml/LoginDialog/UsernameCollisionBody.qml create mode 100644 interface/resources/qml/LoginDialog/WelcomeBody.qml create mode 100644 interface/resources/qml/LoginDialogSave.qml create mode 100644 interface/resources/qml/controls-uit/HorizontalRule.qml create mode 100644 interface/resources/qml/controls-uit/HorizontalSpacer.qml create mode 100644 interface/resources/qml/styles-uit/ButtonLabel.qml create mode 100644 interface/resources/qml/styles-uit/IconButton.qml create mode 100644 interface/resources/qml/styles-uit/InputLabel.qml create mode 100644 interface/resources/qml/styles-uit/ListItem.qml create mode 100644 interface/resources/qml/styles-uit/Logs.qml create mode 100644 interface/resources/qml/styles-uit/MenuItem.qml create mode 100644 interface/resources/qml/styles-uit/OverlayTitle.qml create mode 100644 interface/resources/qml/styles-uit/SectionName.qml create mode 100644 interface/resources/qml/styles-uit/ShortcutText.qml create mode 100644 interface/resources/qml/styles-uit/TabName.qml create mode 100644 interface/resources/qml/styles-uit/TextFieldInput.qml diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index eccea00143..3e8747c076 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -10,320 +10,91 @@ import Hifi 1.0 import QtQuick 2.4 -import "controls" -import "styles" + +import "controls-uit" +import "styles-uit" import "windows" -ScrollingWindow { +import "LoginDialog" + +ModalWindow { id: root HifiConstants { id: hifi } objectName: "LoginDialog" - height: loginDialog.implicitHeight - width: loginDialog.implicitWidth - // FIXME make movable - anchors.centerIn: parent - destroyOnHidden: false - hideBackground: true - shown: false + implicitWidth: 520 + implicitHeight: 320 + destroyOnCloseButton: true + destroyOnHidden: true + visible: true + + property string iconText: "" + property int iconSize: 50 + + property string title: "" + property int titleWidth: 0 + + Component { + id: signInBody + SignInBody {} + } + Component { + id: welcomeBody + WelcomeBody {} + } LoginDialog { id: loginDialog - implicitWidth: backgroundRectangle.width - implicitHeight: backgroundRectangle.height - readonly property int inputWidth: 500 - readonly property int inputHeight: 60 - readonly property int borderWidth: 30 - readonly property int closeMargin: 16 - readonly property real tan30: 0.577 // tan(30°) - readonly property int inputSpacing: 16 - Rectangle { - id: backgroundRectangle - width: loginDialog.inputWidth + loginDialog.borderWidth * 2 - height: loginDialog.inputHeight * 6 + loginDialog.closeMargin * 2 - radius: loginDialog.closeMargin * 2 - color: "#2c86b1" - opacity: 0.85 + Loader { + id: bodyLoader + anchors.fill: parent + sourceComponent: signInBody } - Column { - id: mainContent - width: loginDialog.inputWidth - spacing: loginDialog.inputSpacing - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: parent.verticalCenter + Connections { + target: loginDialog + onHandleLoginCompleted: { + console.log("Login Succeeded") + bodyLoader.sourceComponent = welcomeBody + bodyLoader.active = true } - - Item { - // Offset content down a little - width: loginDialog.inputWidth - height: loginDialog.closeMargin + onHandleLoginFailed: { + console.log("Login Failed") } - - Rectangle { - width: loginDialog.inputWidth - height: loginDialog.inputHeight - radius: height / 2 - color: "#ebebeb" - - Image { - source: "../images/login-username.svg" - width: loginDialog.inputHeight * 0.65 - height: width - sourceSize: Qt.size(width, height); - anchors { - verticalCenter: parent.verticalCenter - left: parent.left - leftMargin: loginDialog.inputHeight / 4 - } - } - - TextInput { - id: username - anchors { - fill: parent - leftMargin: loginDialog.inputHeight - rightMargin: loginDialog.inputHeight / 2 - } - - helperText: "username or email" - color: hifi.colors.text - - KeyNavigation.tab: password - KeyNavigation.backtab: password - } - } - - Rectangle { - width: loginDialog.inputWidth - height: loginDialog.inputHeight - radius: height / 2 - color: "#ebebeb" - - Image { - source: "../images/login-password.svg" - width: loginDialog.inputHeight * 0.65 - height: width - sourceSize: Qt.size(width, height); - anchors { - verticalCenter: parent.verticalCenter - left: parent.left - leftMargin: loginDialog.inputHeight / 4 - } - } - - TextInput { - id: password - anchors { - fill: parent - leftMargin: loginDialog.inputHeight - rightMargin: loginDialog.inputHeight / 2 - } - - helperText: "password" - echoMode: TextInput.Password - color: hifi.colors.text - - KeyNavigation.tab: username - KeyNavigation.backtab: username - onFocusChanged: { - if (password.focus) { - password.selectAll() - } - } - } - } - - Item { - width: loginDialog.inputWidth - height: loginDialog.inputHeight / 2 - - Text { - id: messageText - - visible: loginDialog.statusText != "" && loginDialog.statusText != "Logging in..." - - width: loginDialog.inputWidth - height: loginDialog.inputHeight / 2 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - text: loginDialog.statusText - color: "white" - } - - Row { - id: messageSpinner - - visible: loginDialog.statusText == "Logging in..." - onVisibleChanged: visible ? messageSpinnerAnimation.restart() : messageSpinnerAnimation.stop() - - spacing: 24 - anchors { - verticalCenter: parent.verticalCenter - horizontalCenter: parent.horizontalCenter - } - - Rectangle { - id: spinner1 - width: 10 - height: 10 - color: "#ebebeb" - opacity: 0.05 - } - - Rectangle { - id: spinner2 - width: 10 - height: 10 - color: "#ebebeb" - opacity: 0.05 - } - - Rectangle { - id: spinner3 - width: 10 - height: 10 - color: "#ebebeb" - opacity: 0.05 - } - - SequentialAnimation { - id: messageSpinnerAnimation - running: messageSpinner.visible - loops: Animation.Infinite - NumberAnimation { target: spinner1; property: "opacity"; to: 1.0; duration: 1000 } - NumberAnimation { target: spinner2; property: "opacity"; to: 1.0; duration: 1000 } - NumberAnimation { target: spinner3; property: "opacity"; to: 1.0; duration: 1000 } - NumberAnimation { target: spinner1; property: "opacity"; to: 0.05; duration: 0 } - NumberAnimation { target: spinner2; property: "opacity"; to: 0.05; duration: 0 } - NumberAnimation { target: spinner3; property: "opacity"; to: 0.05; duration: 0 } - } - } - } - - Row { - width: loginDialog.inputWidth - height: loginDialog.inputHeight - - Rectangle { - width: loginDialog.inputWidth / 3 - height: loginDialog.inputHeight - radius: height / 2 - color: "#353535" - - TextInput { - anchors.fill: parent - text: "Login" - color: "white" - horizontalAlignment: Text.AlignHCenter - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - loginDialog.login(username.text, password.text) - } - } - } - - Image { - source: "../images/steam-sign-in.png" - width: loginDialog.inputWidth / 3 - height: loginDialog.inputHeight - anchors { - verticalCenter: parent.verticalCenter - right: parent.right - leftMargin: loginDialog.inputHeight / 4 - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - loginDialog.loginThroughSteam() - } - } - } - } - - Item { - anchors { left: parent.left; right: parent.right; } - height: loginDialog.inputHeight - - Image { - id: hifiIcon - source: "../images/hifi-logo-blackish.svg" - width: loginDialog.inputHeight - height: width - sourceSize: Qt.size(width, height); - anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter } - } - - Text { - anchors { verticalCenter: parent.verticalCenter; right: hifiIcon.left; margins: loginDialog.inputSpacing } - text: "Password?" - scale: 0.8 - font.underline: true - color: "#e0e0e0" - MouseArea { - anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 } - cursorShape: Qt.PointingHandCursor - onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new") - } - } - - Text { - anchors { verticalCenter: parent.verticalCenter; left: hifiIcon.right; margins: loginDialog.inputSpacing } - text: "Register" - scale: 0.8 - font.underline: true - color: "#e0e0e0" - - MouseArea { - anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 } - cursorShape: Qt.PointingHandCursor - onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/signup") - } - } - - } - } - } - - onShownChanged: { - if (!shown) { - username.text = "" - password.text = "" - loginDialog.statusText = "" - } else { - username.forceActiveFocus() } } Keys.onPressed: { - switch (event.key) { + if (!visible) { + return + } + + if (event.modifiers === Qt.ControlModifier) + switch (event.key) { + case Qt.Key_A: + event.accepted = true + detailedText.selectAll() + break + case Qt.Key_C: + event.accepted = true + detailedText.copy() + break + case Qt.Key_Period: + if (Qt.platform.os === "osx") { + event.accepted = true + content.reject() + } + break + } else switch (event.key) { case Qt.Key_Escape: case Qt.Key_Back: - root.shown = false; - event.accepted = true; - break; + event.accepted = true + destroy() + break case Qt.Key_Enter: case Qt.Key_Return: - if (username.activeFocus) { - event.accepted = true - password.forceActiveFocus() - } else if (password.activeFocus) { - event.accepted = true - if (username.text == "") { - username.forceActiveFocus() - } else { - loginDialog.login(username.text, password.text) - } - } + event.accepted = true break } } diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml new file mode 100644 index 0000000000..b6ef012e2a --- /dev/null +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -0,0 +1,93 @@ +// +// CompleteProfileBody.qml +// +// Created by Clement on 7/18/16 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.4 +import QtQuick.Controls.Styles 1.4 as OriginalStyles + +import "../controls-uit" +import "../styles-uit" + +Item { + id: completeProfileBody + clip: true + width: pane.width + height: pane.height + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, additionalTextContainer.contentWidth) + var targetHeight = 4 * hifi.dimensions.contentSpacing.y + buttons.height + additionalTextContainer.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + Row { + id: buttons + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: 3 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + width: 200 + + text: qsTr("Create your profile") + color: hifi.buttons.blue + } + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Cancel") + + + onClicked: root.destroy() + } + } + + ShortcutText { + id: additionalTextContainer + anchors { + top: buttons.bottom + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + text: "Already have a High Fidelity profile? Link to an existing profile here." + + font.underline: true + wrapMode: Text.WordWrap + color: hifi.colors.blueAccent + lineHeight: 2 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + Component.onCompleted: { + root.title = qsTr("Complete Your Profile") + root.iconText = "<" + d.resize(); + } +} diff --git a/interface/resources/qml/LoginDialog/EmailSentBody.qml b/interface/resources/qml/LoginDialog/EmailSentBody.qml new file mode 100644 index 0000000000..eede996412 --- /dev/null +++ b/interface/resources/qml/LoginDialog/EmailSentBody.qml @@ -0,0 +1,83 @@ +// +// EmailSentBody.qml +// +// Created by Clement on 7/18/16 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.4 + +import "../controls-uit" +import "../styles-uit" + +Item { + id: emailSentBody + clip: true + width: pane.width + height: pane.height + + property string email: "clement@highfidelity.com" + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth) + var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + MenuItem { + id: mainTextContainer + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + text: qsTr("An email with instructions on reseting your password was sent to
") + email + "" + wrapMode: Text.WordWrap + color: hifi.colors.baseGrayHighlight + lineHeight: 2 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + Row { + id: buttons + anchors { + top: mainTextContainer.bottom + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Close"); + + onClicked: root.destroy() + } + } + + Component.onCompleted: { + root.title = qsTr("Email Sent") + root.iconText = "" + d.resize(); + } +} diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml new file mode 100644 index 0000000000..b7ff756fa3 --- /dev/null +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -0,0 +1,165 @@ +// +// LinkAccountBody.qml +// +// Created by Clement on 7/18/16 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 as OriginalStyles + +import "../controls-uit" +import "../styles-uit" + +Item { + id: linkAccountBody + clip: true + width: pane.width + height: pane.height + + property bool existingEmail: true + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, mainTextContainer.visible ? mainTextContainer.contentWidth : 0) + var targetHeight = (mainTextContainer.visible ? mainTextContainer.height : 0) + + 4 * hifi.dimensions.contentSpacing.y + form.height + + 4 * hifi.dimensions.contentSpacing.y + buttons.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + MenuItem { + id: mainTextContainer + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + visible: existingEmail + + text: qsTr("Your Steam account's email matches an existing High Fidelity Profile") + wrapMode: Text.WordWrap + color: hifi.colors.redAccent + lineHeight: 2 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + + Column { + id: form + anchors { + top: mainTextContainer.bottom + left: parent.left + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + spacing: 2 * hifi.dimensions.contentSpacing.y + + Row { + spacing: hifi.dimensions.contentSpacing.x + + TextField { + id: usernameField + anchors { + verticalCenter: parent.verticalCenter + } + width: 350 + + label: "User Name or Email" + } + + ShortcutText { + anchors { + verticalCenter: parent.verticalCenter + } + + text: "Need help?" + + color: hifi.colors.blueAccent + font.underline: true + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + Row { + spacing: hifi.dimensions.contentSpacing.x + + TextField { + id: passwordField + anchors { + verticalCenter: parent.verticalCenter + } + width: 350 + + label: "Password" + echoMode: TextInput.Password + } + + ShortcutText { + anchors { + verticalCenter: parent.verticalCenter + } + + text: "Need help?" + + color: hifi.colors.blueAccent + font.underline: true + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + + } + + Row { + id: buttons + anchors { + top: form.bottom + right: parent.right + margins: 0 + topMargin: 3 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + width: 200 + + text: qsTr("Link Account") + color: hifi.buttons.blue + } + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Cancel") + + onClicked: root.destroy() + } + } + + Component.onCompleted: { + root.title = qsTr("Sign Into High Fidelity") + root.iconText = "<" + d.resize(); + } +} diff --git a/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml b/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml new file mode 100644 index 0000000000..74dcb18054 --- /dev/null +++ b/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml @@ -0,0 +1,106 @@ +// +// RecoverPasswordBody.qml +// +// Created by Clement on 7/18/16 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 as OriginalStyles + +import "../controls-uit" +import "../styles-uit" + +Item { + id: recoverPasswordBody + clip: true + width: pane.width + height: pane.height + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth) + var targetHeight = mainTextContainer.height + + 3 * hifi.dimensions.contentSpacing.y + emailField.height + + 4 * hifi.dimensions.contentSpacing.y + buttons.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + MenuItem { + id: mainTextContainer + anchors { + top: parent.top + left: parent.left + right: parent.right + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + text: qsTr("In order to help you reset your password, we will send an
email with instructions to your email address.") + wrapMode: Text.WordWrap + color: hifi.colors.baseGrayHighlight + lineHeight: 1 + horizontalAlignment: Text.AlignHLeft + } + + + TextField { + id: emailField + anchors { + top: mainTextContainer.bottom + left: parent.left + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + + width: 350 + + label: "Email address" + } + + Row { + id: buttons + anchors { + top: emailField.bottom + right: parent.right + margins: 0 + topMargin: 3 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + width: 200 + + text: qsTr("Send recovery email") + color: hifi.buttons.blue + } + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Back") + } + } + + Component.onCompleted: { + root.title = qsTr("Recover Password") + root.iconText = "<" + d.resize(); + } +} diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml new file mode 100644 index 0000000000..d3f6926bd2 --- /dev/null +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -0,0 +1,110 @@ +// +// SignInBody.qml +// +// Created by Clement on 7/18/16 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.4 +import QtQuick.Controls.Styles 1.4 as OriginalStyles + +import "../controls-uit" +import "../styles-uit" + +Item { + id: signInBody + clip: true + width: pane.width + height: pane.height + + property bool required: false + + function login() { + console.log("Trying to log in") + loginDialog.loginThroughSteam() + } + + function cancel() { + root.destroy() + } + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth) + var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + MenuItem { + id: mainTextContainer + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + text: required ? qsTr("This domain's owner requires that you sign in:") + : qsTr("Sign in to access your user account:") + wrapMode: Text.WordWrap + color: hifi.colors.baseGrayHighlight + lineHeight: 2 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + Row { + id: buttons + anchors { + top: mainTextContainer.bottom + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + + width: undefined // invalidate so that the image's size sets the width + height: undefined // invalidate so that the image's size sets the height + focus: true + + style: OriginalStyles.ButtonStyle { + background: Image { + id: buttonImage + source: "../../images/steam-sign-in.png" + } + } + onClicked: signInBody.login() + } + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Cancel"); + + onClicked: signInBody.cancel() + } + } + + Component.onCompleted: { + root.title = required ? qsTr("Sign In Required") + : qsTr("Sign In") + root.iconText = "" + d.resize(); + } +} diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml new file mode 100644 index 0000000000..ea5a9bb614 --- /dev/null +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -0,0 +1,162 @@ +// +// UsernameCollisionBody.qml +// +// Created by Clement on 7/18/16 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 as OriginalStyles + +import "../controls-uit" +import "../styles-uit" + +Item { + id: usernameCollisionBody + clip: true + width: pane.width + height: pane.height + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, mainTextContainer.visible ? mainTextContainer.contentWidth : 0) + var targetHeight = (mainTextContainer.visible ? mainTextContainer.height : 0) + + 4 * hifi.dimensions.contentSpacing.y + form.height + + 4 * hifi.dimensions.contentSpacing.y + buttons.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + MenuItem { + id: mainTextContainer + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + text: qsTr("Choose your High Fidelity user name:") + wrapMode: Text.WordWrap + color: hifi.colors.baseGrayHighlight + lineHeight: 1 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + + Column { + id: form + anchors { + top: mainTextContainer.bottom + left: parent.left + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + spacing: 2 * hifi.dimensions.contentSpacing.y + + Row { + spacing: hifi.dimensions.contentSpacing.x + + TextField { + id: usernameField + anchors { + verticalCenter: parent.verticalCenter + } + width: 350 + + label: "User Name or Email" + } + + ShortcutText { + anchors { + verticalCenter: parent.verticalCenter + } + + text: "Need help?" + + color: hifi.colors.blueAccent + font.underline: true + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + Row { + spacing: hifi.dimensions.contentSpacing.x + + TextField { + id: passwordField + anchors { + verticalCenter: parent.verticalCenter + } + width: 350 + + label: "Password" + echoMode: TextInput.Password + } + + ShortcutText { + anchors { + verticalCenter: parent.verticalCenter + } + + text: "Need help?" + + color: hifi.colors.blueAccent + font.underline: true + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + + } + + Row { + id: buttons + anchors { + top: form.bottom + right: parent.right + margins: 0 + topMargin: 3 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + width: 200 + + text: qsTr("Create your profile") + color: hifi.buttons.blue + } + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Cancel") + + onClicked: root.destroy() + } + } + + Component.onCompleted: { + root.title = qsTr("Complete Your Profile") + root.iconText = "<" + d.resize(); + } +} diff --git a/interface/resources/qml/LoginDialog/WelcomeBody.qml b/interface/resources/qml/LoginDialog/WelcomeBody.qml new file mode 100644 index 0000000000..8b771eac1f --- /dev/null +++ b/interface/resources/qml/LoginDialog/WelcomeBody.qml @@ -0,0 +1,83 @@ +// +// WelcomeBody.qml +// +// Created by Clement on 7/18/16 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.4 + +import "../controls-uit" +import "../styles-uit" + +Item { + id: welcomeBody + clip: true + width: pane.width + height: pane.height + + property bool welcomeBack: true + + QtObject { + id: d + readonly property int minWidth: 480 + readonly property int maxWidth: 1280 + readonly property int minHeight: 120 + readonly property int maxHeight: 720 + + function resize() { + var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth) + var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height + + root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) + root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + } + } + + MenuItem { + id: mainTextContainer + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: hifi.dimensions.contentSpacing.y + } + + text: qsTr("You are now signed into High Fidelity") + wrapMode: Text.WordWrap + color: hifi.colors.baseGrayHighlight + lineHeight: 2 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + Row { + id: buttons + anchors { + top: mainTextContainer.bottom + horizontalCenter: parent.horizontalCenter + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + spacing: hifi.dimensions.contentSpacing.x + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: qsTr("Close"); + + onClicked: root.destroy() + } + } + + Component.onCompleted: { + root.title = (welcomeBack ? qsTr("Welcome back ") : qsTr("Welcome ")) + Account.getUsername() + qsTr("!") + root.iconText = "" + d.resize(); + } +} diff --git a/interface/resources/qml/LoginDialogSave.qml b/interface/resources/qml/LoginDialogSave.qml new file mode 100644 index 0000000000..46246fc1a5 --- /dev/null +++ b/interface/resources/qml/LoginDialogSave.qml @@ -0,0 +1,197 @@ +Window { + id: root + HifiConstants { id: hifi } + + width: 550 + height: 200 + + anchors.centerIn: parent + resizable: true + + property bool required: false + + Component { + id: welcomeBody + + Column { + anchors.centerIn: parent + + OverlayTitle { + anchors.horizontalCenter: parent.horizontalCenter + + text: "Welcomeback Atlante45!" + color: hifi.colors.baseGrayHighlight + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + VerticalSpacer {} + + HorizontalRule {} + + MenuItem { + id: details + anchors.horizontalCenter: parent.horizontalCenter + + text: "You are now signed into High Fidelity" + color: hifi.colors.baseGrayHighlight + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + VerticalSpacer {} + + Button { + anchors.horizontalCenter: parent.horizontalCenter + + text: "Close" + } + } + } + + Component { + id: signInBody + + Column { + anchors.centerIn: parent + + OverlayTitle { + anchors.horizontalCenter: parent.horizontalCenter + + text: required ? "Sign In Required" : "Sign In" + color: hifi.colors.baseGrayHighlight + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + VerticalSpacer {} + + HorizontalRule {} + + MenuItem { + id: details + anchors.horizontalCenter: parent.horizontalCenter + + text: required ? "This domain's owner requires that you sign in:" + : "Sign in to access your user account:" + color: hifi.colors.baseGrayHighlight + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + VerticalSpacer {} + + Row { + anchors.horizontalCenter: parent.horizontalCenter + + Button { + anchors.verticalCenter: parent.verticalCenter + width: undefined // invalidate so that the image's size sets the width + height: undefined // invalidate so that the image's size sets the height + + style: Original.ButtonStyle { + background: Image { + id: buttonImage + source: "../images/steam-sign-in.png" + } + } + + onClicked: body.sourceComponent = completeProfileBody + } + + HorizontalSpacer {} + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: "Cancel" + + onClicked: required = !required + } + } + } + } + + Component { + id: completeProfileBody + + Column { + anchors.centerIn: parent + + Row { + anchors.horizontalCenter: parent.horizontalCenter + + HiFiGlyphs { + anchors.verticalCenter: parent.verticalCenter + + text: hifi.glyphs.avatar + } + + OverlayTitle { + anchors.verticalCenter: parent.verticalCenter + + text: "Complete Your Profile" + color: hifi.colors.baseGrayHighlight + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + + VerticalSpacer {} + + HorizontalRule {} + + VerticalSpacer {} + + Row { + anchors.horizontalCenter: parent.horizontalCenter + + Button { + anchors.verticalCenter: parent.verticalCenter + + width: 200 + + text: "Create your profile" + color: hifi.buttons.blue + + onClicked: body.sourceComponent = welcomeBody + } + + HorizontalSpacer {} + + Button { + anchors.verticalCenter: parent.verticalCenter + + text: "Cancel" + + onClicked: body.sourceComponent = signInBody + + } + } + + VerticalSpacer {} + + ShortcutText { + text: "Already have a High Fidelity profile? Link to an existing profile here." + + color: hifi.colors.blueAccent + font.underline: true + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + } + } + + Rectangle { + anchors.fill: root + color: hifi.colors.faintGray + radius: hifi.dimensions.borderRadius + + Loader { + id: body + anchors.centerIn: parent + sourceComponent: signInBody + } + } +} diff --git a/interface/resources/qml/controls-uit/HorizontalRule.qml b/interface/resources/qml/controls-uit/HorizontalRule.qml new file mode 100644 index 0000000000..425500f1d4 --- /dev/null +++ b/interface/resources/qml/controls-uit/HorizontalRule.qml @@ -0,0 +1,20 @@ +// +// HorizontalRule.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: hifi.colors.lightGray +} diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml new file mode 100644 index 0000000000..545154ab44 --- /dev/null +++ b/interface/resources/qml/controls-uit/HorizontalSpacer.qml @@ -0,0 +1,21 @@ +// +// HorizontalSpacer.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +import "../styles-uit" + +Item { + id: root + property alias size: root.width + + width: hifi.dimensions.controlInterlineHeight + height: 1 // Must be non-zero +} diff --git a/interface/resources/qml/controls-uit/VerticalSpacer.qml b/interface/resources/qml/controls-uit/VerticalSpacer.qml index 6fc49605c0..2df65f1002 100644 --- a/interface/resources/qml/controls-uit/VerticalSpacer.qml +++ b/interface/resources/qml/controls-uit/VerticalSpacer.qml @@ -13,6 +13,9 @@ import QtQuick 2.5 import "../styles-uit" Item { + id: root + property alias size: root.height + width: 1 // Must be non-zero height: hifi.dimensions.controlInterlineHeight } diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml index d390ea08bf..40c5a01e15 100644 --- a/interface/resources/qml/dialogs/MessageDialog.qml +++ b/interface/resources/qml/dialogs/MessageDialog.qml @@ -21,8 +21,6 @@ import "messageDialog" ModalWindow { id: root HifiConstants { id: hifi } - implicitWidth: 640 - implicitHeight: 320 destroyOnCloseButton: true destroyOnHidden: true visible: true @@ -70,7 +68,7 @@ ModalWindow { QtObject { id: d readonly property int minWidth: 480 - readonly property int maxWdith: 1280 + readonly property int maxWidth: 1280 readonly property int minHeight: 120 readonly property int maxHeight: 720 @@ -80,7 +78,7 @@ ModalWindow { + (informativeTextContainer.text != "" ? informativeTextContainer.contentHeight + 3 * hifi.dimensions.contentSpacing.y : 0) + buttons.height + (content.state === "expanded" ? details.implicitHeight + hifi.dimensions.contentSpacing.y : 0) - root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth) + root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWidth) ? d.maxWidth : targetWidth) root.height = (targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight) } } diff --git a/interface/resources/qml/styles-uit/ButtonLabel.qml b/interface/resources/qml/styles-uit/ButtonLabel.qml new file mode 100644 index 0000000000..aade5fb439 --- /dev/null +++ b/interface/resources/qml/styles-uit/ButtonLabel.qml @@ -0,0 +1,18 @@ +// +// ButtonLabel.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayBold { + font.pixelSize: hifi.fontSizes.buttonLabel +} diff --git a/interface/resources/qml/styles-uit/IconButton.qml b/interface/resources/qml/styles-uit/IconButton.qml new file mode 100644 index 0000000000..84c1ef14c1 --- /dev/null +++ b/interface/resources/qml/styles-uit/IconButton.qml @@ -0,0 +1,20 @@ +// +// IconButton.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.iconButton + font.capitalization: Font.AllUppercase + font.letterSpacing: 1.5 +} diff --git a/interface/resources/qml/styles-uit/InputLabel.qml b/interface/resources/qml/styles-uit/InputLabel.qml new file mode 100644 index 0000000000..59657a554d --- /dev/null +++ b/interface/resources/qml/styles-uit/InputLabel.qml @@ -0,0 +1,18 @@ +// +// InputLabel.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewaySemiBold { + font.pixelSize: hifi.fontSizes.inputLabel +} diff --git a/interface/resources/qml/styles-uit/ListItem.qml b/interface/resources/qml/styles-uit/ListItem.qml new file mode 100644 index 0000000000..f707686edc --- /dev/null +++ b/interface/resources/qml/styles-uit/ListItem.qml @@ -0,0 +1,18 @@ +// +// ListItem.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.listItem +} diff --git a/interface/resources/qml/styles-uit/Logs.qml b/interface/resources/qml/styles-uit/Logs.qml new file mode 100644 index 0000000000..577fe2f8d8 --- /dev/null +++ b/interface/resources/qml/styles-uit/Logs.qml @@ -0,0 +1,18 @@ +// +// Logs.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +AnonymousProRegular { + font.pixelSize: hifi.fontSizes.logs +} diff --git a/interface/resources/qml/styles-uit/MenuItem.qml b/interface/resources/qml/styles-uit/MenuItem.qml new file mode 100644 index 0000000000..4431c357bf --- /dev/null +++ b/interface/resources/qml/styles-uit/MenuItem.qml @@ -0,0 +1,19 @@ +// +// MenuItem.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewaySemiBold { + lineHeight: 2 + font.pixelSize: hifi.fontSizes.menuItem +} diff --git a/interface/resources/qml/styles-uit/OverlayTitle.qml b/interface/resources/qml/styles-uit/OverlayTitle.qml new file mode 100644 index 0000000000..e23b9eca14 --- /dev/null +++ b/interface/resources/qml/styles-uit/OverlayTitle.qml @@ -0,0 +1,18 @@ +// +// OverlayTitle.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.overlayTitle +} diff --git a/interface/resources/qml/styles-uit/SectionName.qml b/interface/resources/qml/styles-uit/SectionName.qml new file mode 100644 index 0000000000..5438fec7bc --- /dev/null +++ b/interface/resources/qml/styles-uit/SectionName.qml @@ -0,0 +1,19 @@ +// +// SectionName.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.sectionName + font.capitalization: Font.AllUppercase +} diff --git a/interface/resources/qml/styles-uit/ShortcutText.qml b/interface/resources/qml/styles-uit/ShortcutText.qml new file mode 100644 index 0000000000..a3ab351870 --- /dev/null +++ b/interface/resources/qml/styles-uit/ShortcutText.qml @@ -0,0 +1,18 @@ +// +// ShortcutText.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayLight { + font.pixelSize: hifi.fontSizes.shortcutText +} diff --git a/interface/resources/qml/styles-uit/TabName.qml b/interface/resources/qml/styles-uit/TabName.qml new file mode 100644 index 0000000000..eb4e790e7e --- /dev/null +++ b/interface/resources/qml/styles-uit/TabName.qml @@ -0,0 +1,19 @@ +// +// TabName.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +RalewayRegular { + font.pixelSize: hifi.fontSizes.tabName + font.capitalization: Font.AllUppercase +} diff --git a/interface/resources/qml/styles-uit/TextFieldInput.qml b/interface/resources/qml/styles-uit/TextFieldInput.qml new file mode 100644 index 0000000000..010b4d03ad --- /dev/null +++ b/interface/resources/qml/styles-uit/TextFieldInput.qml @@ -0,0 +1,18 @@ +// +// TextFieldInput.qml +// +// Created by Clement on 7/18/16 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "." + +FiraSansSemiBold { + font.pixelSize: hifi.fontSizes.textFieldInput +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1729a773c6..58284682f1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2242,11 +2242,12 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_X: - if (isShifted && isMeta) { + if (isMeta) { auto offscreenUi = DependencyManager::get(); - offscreenUi->togglePinned(); - //offscreenUi->getRootContext()->engine()->clearComponentCache(); - //OffscreenUi::information("Debugging", "Component cache cleared"); +// offscreenUi->togglePinned(); + offscreenUi->getRootContext()->engine()->clearComponentCache(); + qDebug() << "Component cache cleared"; +// OffscreenUi::information("Debugging", "Component cache cleared"); // placeholder for dialogs being converted to QML. } break; diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 8240340381..15a277f394 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -22,9 +22,7 @@ HIFI_QML_DEF(LoginDialog) -LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent), - _rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString()) -{ +LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) { auto accountManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::loginComplete, this, &LoginDialog::handleLoginCompleted); @@ -54,42 +52,16 @@ void LoginDialog::toggleAction() { } } -void LoginDialog::handleLoginCompleted(const QUrl&) { - hide(); -} - -void LoginDialog::handleLoginFailed() { - setStatusText("Invalid username or password"); -} - -void LoginDialog::setStatusText(const QString& statusText) { - if (statusText != _statusText) { - _statusText = statusText; - emit statusTextChanged(); - } -} - -QString LoginDialog::statusText() const { - return _statusText; -} - -QString LoginDialog::rootUrl() const { - return _rootUrl; -} - void LoginDialog::login(const QString& username, const QString& password) { qDebug() << "Attempting to login " << username; - setStatusText("Logging in..."); DependencyManager::get()->requestAccessToken(username, password); } void LoginDialog::loginThroughSteam() { qDebug() << "Attempting to login through Steam"; - setStatusText("Logging in..."); - SteamClient::requestTicket([this](Ticket ticket) { if (ticket.isNull()) { - setStatusText("Steam client not logged into an account"); + emit handleLoginFailed(); return; } @@ -98,6 +70,5 @@ void LoginDialog::loginThroughSteam() { } void LoginDialog::openUrl(const QString& url) { - qDebug() << url; QDesktopServices::openUrl(url); } diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h index 0dd4b5e96f..dcd0e04894 100644 --- a/interface/src/ui/LoginDialog.h +++ b/interface/src/ui/LoginDialog.h @@ -20,32 +20,19 @@ class LoginDialog : public OffscreenQmlDialog { Q_OBJECT HIFI_QML_DECL - Q_PROPERTY(QString statusText READ statusText WRITE setStatusText NOTIFY statusTextChanged) - Q_PROPERTY(QString rootUrl READ rootUrl) - public: static void toggleAction(); LoginDialog(QQuickItem* parent = nullptr); - void setStatusText(const QString& statusText); - QString statusText() const; - - QString rootUrl() const; - signals: - void statusTextChanged(); - -protected: - void handleLoginCompleted(const QUrl& authURL); + void handleLoginCompleted(); void handleLoginFailed(); +protected: Q_INVOKABLE void login(const QString& username, const QString& password); Q_INVOKABLE void loginThroughSteam(); Q_INVOKABLE void openUrl(const QString& url); -private: - QString _statusText; - const QString _rootUrl; }; #endif // hifi_LoginDialog_h diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index a512ae8887..d1e0a46c71 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -15,7 +15,7 @@ #include namespace NetworkingConstants { - const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com"); + const QUrl METAVERSE_SERVER_URL = QUrl("http://localhost:3000"); } #endif // hifi_NetworkingConstants_h From f0ff9752480485c9ddb5f8bae1afbb67550fe47c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 22 Jul 2016 19:48:52 -0700 Subject: [PATCH 078/279] UI wiring --- interface/resources/qml/LoginDialog.qml | 23 +-- .../qml/LoginDialog/CompleteProfileBody.qml | 37 +++++ .../qml/LoginDialog/EmailSentBody.qml | 6 +- .../qml/LoginDialog/LinkAccountBody.qml | 81 +++++++++- .../qml/LoginDialog/RecoverPasswordBody.qml | 38 ++++- .../resources/qml/LoginDialog/SignInBody.qml | 18 +++ .../qml/LoginDialog/UsernameCollisionBody.qml | 149 ++++++++---------- .../resources/qml/LoginDialog/WelcomeBody.qml | 17 +- .../scripting/AccountScriptingInterface.cpp | 3 + .../src/scripting/AccountScriptingInterface.h | 5 + interface/src/ui/LoginDialog.cpp | 92 +++++++++++ interface/src/ui/LoginDialog.h | 24 ++- libraries/networking/src/AccountManager.cpp | 1 + .../src/steamworks-wrapper/SteamClient.cpp | 23 ++- .../src/steamworks-wrapper/SteamClient.h | 2 + 15 files changed, 394 insertions(+), 125 deletions(-) diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 3e8747c076..1f84024e15 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -33,34 +33,13 @@ ModalWindow { property string title: "" property int titleWidth: 0 - Component { - id: signInBody - SignInBody {} - } - Component { - id: welcomeBody - WelcomeBody {} - } - LoginDialog { id: loginDialog Loader { id: bodyLoader anchors.fill: parent - sourceComponent: signInBody - } - - Connections { - target: loginDialog - onHandleLoginCompleted: { - console.log("Login Succeeded") - bodyLoader.sourceComponent = welcomeBody - bodyLoader.active = true - } - onHandleLoginFailed: { - console.log("Login Failed") - } + source: loginDialog.isSteamRunning() ? "LoginDialog/SignInBody.qml" : "LoginDialog/LinkAccountBody.qml" } } diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml index b6ef012e2a..12d2cee73e 100644 --- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -54,6 +54,8 @@ Item { text: qsTr("Create your profile") color: hifi.buttons.blue + + onClicked: loginDialog.createAccountFromStream() } Button { @@ -83,6 +85,15 @@ Item { lineHeight: 2 lineHeightMode: Text.ProportionalHeight horizontalAlignment: Text.AlignHCenter + + MouseArea { + anchors.fill: parent + onClicked: { + bodyLoader.source = "LinkAccountBody.qml" + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } } Component.onCompleted: { @@ -90,4 +101,30 @@ Item { root.iconText = "<" d.resize(); } + + Connections { + target: loginDialog + onHandleCreateCompleted: { + console.log("Create Succeeded") + + loginDialog.loginThroughSteam() + } + onHandleCreateFailed: { + console.log("Create Failed: " + error) + + bodyLoader.source = "UsernameCollisionBody.qml" + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLoginCompleted: { + console.log("Login Succeeded") + + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLoginFailed: { + console.log("Login Failed") + } + } } diff --git a/interface/resources/qml/LoginDialog/EmailSentBody.qml b/interface/resources/qml/LoginDialog/EmailSentBody.qml index eede996412..489385864b 100644 --- a/interface/resources/qml/LoginDialog/EmailSentBody.qml +++ b/interface/resources/qml/LoginDialog/EmailSentBody.qml @@ -17,10 +17,10 @@ import "../styles-uit" Item { id: emailSentBody clip: true - width: pane.width - height: pane.height + width: root.pane.width + height: root.pane.height - property string email: "clement@highfidelity.com" + property string email: "" QtObject { id: d diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index b7ff756fa3..da5f0f1a42 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -19,10 +19,14 @@ import "../styles-uit" Item { id: linkAccountBody clip: true - width: pane.width - height: pane.height + width: root.pane.width + height: root.pane.height - property bool existingEmail: true + property bool existingEmail: false + + function login() { + loginDialog.login(usernameField.text, passwordField.text) + } QtObject { id: d @@ -64,7 +68,7 @@ Item { Column { id: form anchors { - top: mainTextContainer.bottom + top: mainTextContainer.visible ? mainTextContainer.bottom : parent.top left: parent.left margins: 0 topMargin: 2 * hifi.dimensions.contentSpacing.y @@ -96,6 +100,15 @@ Item { verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter + + MouseArea { + anchors.fill: parent + onClicked: { + bodyLoader.source = "RecoverPasswordBody.qml" + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } } } Row { @@ -124,6 +137,15 @@ Item { verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter + + MouseArea { + anchors.fill: parent + onClicked: { + bodyLoader.source = "RecoverPasswordBody.qml" + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } } } @@ -141,11 +163,14 @@ Item { onHeightChanged: d.resize(); onWidthChanged: d.resize(); Button { + id: linkAccountButton anchors.verticalCenter: parent.verticalCenter width: 200 - text: qsTr("Link Account") + text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login") color: hifi.buttons.blue + + onClicked: linkAccountBody.login() } Button { @@ -161,5 +186,51 @@ Item { root.title = qsTr("Sign Into High Fidelity") root.iconText = "<" d.resize(); + + usernameField.forceActiveFocus() + } + + Connections { + target: loginDialog + onHandleLoginCompleted: { + console.log("Login Succeeded, linking steam account") + + if (loginDialog.isSteamRunning()) { + loginDialog.linkSteam() + } else { + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } + onHandleLoginFailed: { + console.log("Login Failed") + + } + onHandleLinkCompleted: { + console.log("Link Succeeded") + + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLinkFailed: { + console.log("Link Failed") + + } + } + + Keys.onPressed: { + if (!visible) { + return + } + + switch (event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + linkAccountBody.login() + break + } } } diff --git a/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml b/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml index 74dcb18054..3c6e101e2a 100644 --- a/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml +++ b/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml @@ -19,8 +19,16 @@ import "../styles-uit" Item { id: recoverPasswordBody clip: true - width: pane.width - height: pane.height + width: root.pane.width + height: root.pane.height + + function send() { + loginDialog.sendRecoveryEmail(emailField.text) + + bodyLoader.setSource("EmailSentBody.qml", { "email": emailField.text }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } QtObject { id: d @@ -70,6 +78,10 @@ Item { width: 350 label: "Email address" + + Component.onCompleted: { + emailField.forceActiveFocus() + } } Row { @@ -89,12 +101,20 @@ Item { text: qsTr("Send recovery email") color: hifi.buttons.blue + + onClicked: recoverPasswordBody.send() } Button { anchors.verticalCenter: parent.verticalCenter text: qsTr("Back") + + onClicked: { + bodyLoader.source = "LinkAccountBody.qml" + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } } } @@ -103,4 +123,18 @@ Item { root.iconText = "<" d.resize(); } + + Keys.onPressed: { + if (!visible) { + return + } + + switch (event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + recoverPasswordBody.send() + break + } + } } diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml index d3f6926bd2..2da0ea856d 100644 --- a/interface/resources/qml/LoginDialog/SignInBody.qml +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -107,4 +107,22 @@ Item { root.iconText = "" d.resize(); } + + Connections { + target: loginDialog + onHandleLoginCompleted: { + console.log("Login Succeeded") + + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLoginFailed: { + console.log("Login Failed") + + bodyLoader.source = "CompleteProfileBody.qml" + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } } diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index ea5a9bb614..f0663631a8 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -19,8 +19,8 @@ import "../styles-uit" Item { id: usernameCollisionBody clip: true - width: pane.width - height: pane.height + width: root.pane.width + height: root.pane.height QtObject { id: d @@ -30,26 +30,59 @@ Item { readonly property int maxHeight: 720 function resize() { - var targetWidth = Math.max(titleWidth, mainTextContainer.visible ? mainTextContainer.contentWidth : 0) - var targetHeight = (mainTextContainer.visible ? mainTextContainer.height : 0) + - 4 * hifi.dimensions.contentSpacing.y + form.height + - 4 * hifi.dimensions.contentSpacing.y + buttons.height + var targetWidth = Math.max(titleWidth, Math.max(mainTextContainer.contentWidth, + termsContainer.contentWidth)) + var targetHeight = mainTextContainer.height + + 2 * hifi.dimensions.contentSpacing.y + textField.height + + 5 * hifi.dimensions.contentSpacing.y + termsContainer.height + + 1 * hifi.dimensions.contentSpacing.y + buttons.height root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) } } - MenuItem { + ShortcutText { id: mainTextContainer anchors { top: parent.top - horizontalCenter: parent.horizontalCenter + left: parent.left margins: 0 topMargin: hifi.dimensions.contentSpacing.y } - text: qsTr("Choose your High Fidelity user name:") + text: qsTr("Your Steam username is not available.") + wrapMode: Text.WordWrap + color: hifi.colors.redAccent + lineHeight: 1 + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter + } + + + TextField { + id: textField + anchors { + top: mainTextContainer.bottom + left: parent.left + margins: 0 + topMargin: 2 * hifi.dimensions.contentSpacing.y + } + width: 250 + + placeholderText: "Choose your own" + } + + MenuItem { + id: termsContainer + anchors { + top: textField.bottom + left: parent.left + margins: 0 + topMargin: 3 * hifi.dimensions.contentSpacing.y + } + + text: qsTr("By creating this user profile, you agree to
High Fidelity's Terms of Service") wrapMode: Text.WordWrap color: hifi.colors.baseGrayHighlight lineHeight: 1 @@ -57,82 +90,13 @@ Item { horizontalAlignment: Text.AlignHCenter } - - Column { - id: form - anchors { - top: mainTextContainer.bottom - left: parent.left - margins: 0 - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - spacing: 2 * hifi.dimensions.contentSpacing.y - - Row { - spacing: hifi.dimensions.contentSpacing.x - - TextField { - id: usernameField - anchors { - verticalCenter: parent.verticalCenter - } - width: 350 - - label: "User Name or Email" - } - - ShortcutText { - anchors { - verticalCenter: parent.verticalCenter - } - - text: "Need help?" - - color: hifi.colors.blueAccent - font.underline: true - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } - } - Row { - spacing: hifi.dimensions.contentSpacing.x - - TextField { - id: passwordField - anchors { - verticalCenter: parent.verticalCenter - } - width: 350 - - label: "Password" - echoMode: TextInput.Password - } - - ShortcutText { - anchors { - verticalCenter: parent.verticalCenter - } - - text: "Need help?" - - color: hifi.colors.blueAccent - font.underline: true - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } - } - - } - Row { id: buttons anchors { - top: form.bottom + top: termsContainer.bottom right: parent.right margins: 0 - topMargin: 3 * hifi.dimensions.contentSpacing.y + topMargin: 1 * hifi.dimensions.contentSpacing.y } spacing: hifi.dimensions.contentSpacing.x onHeightChanged: d.resize(); onWidthChanged: d.resize(); @@ -143,6 +107,10 @@ Item { text: qsTr("Create your profile") color: hifi.buttons.blue + + onClicked: { + loginDialog.createAccountFromStream(textField.text) + } } Button { @@ -159,4 +127,25 @@ Item { root.iconText = "<" d.resize(); } + Connections { + target: loginDialog + onHandleCreateCompleted: { + console.log("Create Succeeded") + + loginDialog.loginThroughSteam() + } + onHandleCreateFailed: { + console.log("Create Failed: " + error) + } + onHandleLoginCompleted: { + console.log("Login Succeeded") + + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLoginFailed: { + console.log("Login Failed") + } + } } diff --git a/interface/resources/qml/LoginDialog/WelcomeBody.qml b/interface/resources/qml/LoginDialog/WelcomeBody.qml index 8b771eac1f..ecc848cdc0 100644 --- a/interface/resources/qml/LoginDialog/WelcomeBody.qml +++ b/interface/resources/qml/LoginDialog/WelcomeBody.qml @@ -20,7 +20,13 @@ Item { width: pane.width height: pane.height - property bool welcomeBack: true + property bool welcomeBack: false + + function setTitle() { + root.title = (welcomeBack ? qsTr("Welcome back ") : qsTr("Welcome ")) + Account.username + qsTr("!") + root.iconText = "" + d.resize(); + } QtObject { id: d @@ -75,9 +81,10 @@ Item { } } - Component.onCompleted: { - root.title = (welcomeBack ? qsTr("Welcome back ") : qsTr("Welcome ")) + Account.getUsername() + qsTr("!") - root.iconText = "" - d.resize(); + Component.onCompleted: welcomeBody.setTitle() + + Connections { + target: Account + onUsernameChanged: welcomeBody.setTitle() } } diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp index 1328197195..4090c99ac8 100644 --- a/interface/src/scripting/AccountScriptingInterface.cpp +++ b/interface/src/scripting/AccountScriptingInterface.cpp @@ -15,6 +15,9 @@ AccountScriptingInterface* AccountScriptingInterface::getInstance() { static AccountScriptingInterface sharedInstance; + auto accountManager = DependencyManager::get(); + QObject::connect(accountManager.data(), &AccountManager::profileChanged, + &sharedInstance, &AccountScriptingInterface::usernameChanged); return &sharedInstance; } diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h index 888149b836..49648781ce 100644 --- a/interface/src/scripting/AccountScriptingInterface.h +++ b/interface/src/scripting/AccountScriptingInterface.h @@ -17,6 +17,11 @@ class AccountScriptingInterface : public QObject { Q_OBJECT + Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) + +signals: + void usernameChanged(); + public slots: static AccountScriptingInterface* getInstance(); QString getUsername(); diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 15a277f394..b65e111b16 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -12,6 +12,8 @@ #include "LoginDialog.h" #include +#include +#include #include #include @@ -52,6 +54,10 @@ void LoginDialog::toggleAction() { } } +bool LoginDialog::isSteamRunning() { + return SteamClient::isRunning(); +} + void LoginDialog::login(const QString& username, const QString& password) { qDebug() << "Attempting to login " << username; DependencyManager::get()->requestAccessToken(username, password); @@ -69,6 +75,92 @@ void LoginDialog::loginThroughSteam() { }); } +void LoginDialog::linkSteam() { + qDebug() << "Attempting to link Steam account"; + SteamClient::requestTicket([this](Ticket ticket) { + if (ticket.isNull()) { + emit handleLoginFailed(); + return; + } + + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "linkCompleted"; + callbackParams.errorCallbackReceiver = this; + callbackParams.errorCallbackMethod = "linkFailed"; + + const QString LINK_STEAM_PATH = "api/v1/user/link_steam"; + + QJsonObject payload; + payload.insert("ticket", QJsonValue::fromVariant(QVariant(ticket))); + + auto accountManager = DependencyManager::get(); + accountManager->sendRequest(LINK_STEAM_PATH, AccountManagerAuth::Required, + QNetworkAccessManager::PostOperation, callbackParams, + QJsonDocument(payload).toJson()); + }); +} + +void LoginDialog::createAccountFromStream(QString username) { + qDebug() << "Attempting to create account from Steam info"; + SteamClient::requestTicket([this, username](Ticket ticket) { + if (ticket.isNull()) { + emit handleLoginFailed(); + return; + } + + JSONCallbackParameters callbackParams; + callbackParams.jsonCallbackReceiver = this; + callbackParams.jsonCallbackMethod = "createCompleted"; + callbackParams.errorCallbackReceiver = this; + callbackParams.errorCallbackMethod = "createFailed"; + + const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "api/v1/user/create_from_steam"; + + QJsonObject payload; + payload.insert("ticket", QJsonValue::fromVariant(QVariant(ticket))); + if (!username.isEmpty()) { + payload.insert("username", QJsonValue::fromVariant(QVariant(username))); + } + + auto accountManager = DependencyManager::get(); + accountManager->sendRequest(CREATE_ACCOUNT_FROM_STEAM_PATH, AccountManagerAuth::None, + QNetworkAccessManager::PostOperation, callbackParams, + QJsonDocument(payload).toJson()); + }); + +} + void LoginDialog::openUrl(const QString& url) { QDesktopServices::openUrl(url); } + +void LoginDialog::sendRecoveryEmail(const QString& email) { + const QString PASSWORD_RESET_PATH = "/users/password"; + + QJsonObject payload; + payload.insert("user_email", QJsonValue::fromVariant(QVariant(email))); + + + auto accountManager = DependencyManager::get(); + accountManager->sendRequest(PASSWORD_RESET_PATH, AccountManagerAuth::None, + QNetworkAccessManager::PostOperation, JSONCallbackParameters(), + QJsonDocument(payload).toJson()); +} + +void LoginDialog::linkCompleted(QNetworkReply& reply) { + emit handleLinkCompleted(); +} + +void LoginDialog::linkFailed(QNetworkReply& reply) { + emit handleLinkFailed(reply.errorString()); +} + +void LoginDialog::createCompleted(QNetworkReply& reply) { + emit handleCreateCompleted(); +} + +void LoginDialog::createFailed(QNetworkReply& reply) { + emit handleCreateFailed(reply.errorString()); +} + diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h index dcd0e04894..ef2c937201 100644 --- a/interface/src/ui/LoginDialog.h +++ b/interface/src/ui/LoginDialog.h @@ -16,6 +16,8 @@ #include +class QNetworkReply; + class LoginDialog : public OffscreenQmlDialog { Q_OBJECT HIFI_QML_DECL @@ -29,10 +31,30 @@ signals: void handleLoginCompleted(); void handleLoginFailed(); -protected: + void handleLinkCompleted(); + void handleLinkFailed(QString error); + + void handleCreateCompleted(); + void handleCreateFailed(QString error); + +public slots: + void linkCompleted(QNetworkReply& reply); + void linkFailed(QNetworkReply& reply); + + void createCompleted(QNetworkReply& reply); + void createFailed(QNetworkReply& reply); + +protected slots: + Q_INVOKABLE bool isSteamRunning(); + Q_INVOKABLE void login(const QString& username, const QString& password); Q_INVOKABLE void loginThroughSteam(); + Q_INVOKABLE void linkSteam(); + Q_INVOKABLE void createAccountFromStream(QString username = QString()); + Q_INVOKABLE void openUrl(const QString& url); + Q_INVOKABLE void sendRecoveryEmail(const QString& email); + }; #endif // hifi_LoginDialog_h diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 8c0fa5ed92..52d9e87636 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -568,6 +568,7 @@ void AccountManager::requestAccessTokenFinished() { void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) { // TODO: error handling qCDebug(networking) << "AccountManager requestError - " << error; + emit loginFailed(); } void AccountManager::requestProfile() { diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index 1dbfc0ce00..a8a57064de 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -133,8 +133,16 @@ void SteamTicketRequests::onGetAuthSessionTicketResponse(GetAuthSessionTicketRes static std::atomic_bool initialized { false }; static std::unique_ptr steamTicketRequests; -bool SteamClient::init() { + +bool SteamClient::isRunning() { if (!initialized) { + init(); + } + return initialized; +} + +bool SteamClient::init() { + if (SteamAPI_IsSteamRunning() && !initialized) { initialized = SteamAPI_Init(); } @@ -157,11 +165,6 @@ void SteamClient::shutdown() { void SteamClient::runCallbacks() { if (!initialized) { - init(); - } - - if (!initialized) { - qDebug() << "Steam not initialized"; return; } @@ -176,7 +179,13 @@ void SteamClient::runCallbacks() { void SteamClient::requestTicket(TicketRequestCallback callback) { if (!initialized) { - init(); + if (SteamAPI_IsSteamRunning()) { + init(); + } else { + qWarning() << "Steam is not running"; + callback(INVALID_TICKET); + return; + } } if (!initialized) { diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h index ac5c648ead..f7ca775829 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h @@ -22,6 +22,8 @@ using TicketRequestCallback = std::function; class SteamClient { public: + static bool isRunning(); + static bool init(); static void shutdown(); From 0663766074da67ea4377002d9fdaeca2edca655d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 25 Jul 2016 14:52:02 -0700 Subject: [PATCH 079/279] Replace metaverse server with testing URL --- libraries/networking/src/NetworkingConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index d1e0a46c71..154470201f 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -15,7 +15,7 @@ #include namespace NetworkingConstants { - const QUrl METAVERSE_SERVER_URL = QUrl("http://localhost:3000"); + const QUrl METAVERSE_SERVER_URL = QUrl("https://hifi.ngrok.io"); } #endif // hifi_NetworkingConstants_h From e5290076be650d63e1a97af0fefe4556c6ab2386 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 26 Jul 2016 17:25:52 -0700 Subject: [PATCH 080/279] First draft for Steam friends --- .../src/steamworks-wrapper/SteamClient.cpp | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index a8a57064de..de95269d1a 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -12,7 +12,6 @@ #include "SteamClient.h" #include -#include #include @@ -37,13 +36,15 @@ public: HAuthTicket startRequest(TicketRequestCallback callback); void stopRequest(HAuthTicket authTicket); + void stopAll(); STEAM_CALLBACK(SteamTicketRequests, onGetAuthSessionTicketResponse, GetAuthSessionTicketResponse_t, _getAuthSessionTicketResponse); -private: - void stopAll(); + STEAM_CALLBACK(SteamTicketRequests, onGameRichPresenceJoinRequested, + GameRichPresenceJoinRequested_t, _gameRichPresenceJoinRequestedResponse); +private: struct PendingTicket { HAuthTicket authTicket; Ticket ticket; @@ -54,7 +55,8 @@ private: }; SteamTicketRequests::SteamTicketRequests() : - _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse) + _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse), + _gameRichPresenceJoinRequestedResponse(this, &SteamTicketRequests::onGameRichPresenceJoinRequested) { } @@ -128,10 +130,35 @@ void SteamTicketRequests::onGetAuthSessionTicketResponse(GetAuthSessionTicketRes } } +#include +#include +#include +#include +#include +const QString PREFIX = "--url \""; +const QString SUFFIX = "\""; + + +void SteamTicketRequests::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) { + auto url = QString::fromLocal8Bit(pCallback->m_rgchConnect); + + if (url.startsWith(PREFIX) && url.endsWith(SUFFIX)) { + url.remove(0, PREFIX.size()); + url.remove(-SUFFIX.size(), SUFFIX.size()); + } + + qDebug() << "Joining:" << url; + auto mimeData = new QMimeData(); + mimeData->setUrls(QList() << QUrl(url)); + auto event = new QDropEvent(QPointF(0,0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier); + + QCoreApplication::postEvent(qApp, event); +} + static std::atomic_bool initialized { false }; -static std::unique_ptr steamTicketRequests; +static SteamTicketRequests steamTicketRequests; bool SteamClient::isRunning() { @@ -144,12 +171,12 @@ bool SteamClient::isRunning() { bool SteamClient::init() { if (SteamAPI_IsSteamRunning() && !initialized) { initialized = SteamAPI_Init(); - } - if (!steamTicketRequests && initialized) { - steamTicketRequests.reset(new SteamTicketRequests()); + if (initialized) { + SteamFriends()->SetRichPresence("status", "Localhost"); + SteamFriends()->SetRichPresence("connect", "--url \"hifi://10.0.0.185:40117/10,10,10\""); + } } - return initialized; } @@ -158,9 +185,7 @@ void SteamClient::shutdown() { SteamAPI_Shutdown(); } - if (steamTicketRequests) { - steamTicketRequests.reset(); - } + steamTicketRequests.stopAll(); } void SteamClient::runCallbacks() { @@ -193,7 +218,7 @@ void SteamClient::requestTicket(TicketRequestCallback callback) { return; } - steamTicketRequests->startRequest(callback); + steamTicketRequests.startRequest(callback); } From a13950752bd12e01e7780186ad266b9ac0bd8f95 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 27 Jul 2016 13:32:31 -0700 Subject: [PATCH 081/279] Update steam location with discoverability --- interface/src/DiscoverabilityManager.cpp | 15 ++-- .../src/steamworks-wrapper/SteamClient.cpp | 69 ++++++++++++------- .../src/steamworks-wrapper/SteamClient.h | 3 + 3 files changed, 55 insertions(+), 32 deletions(-) diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp index 4051bd8a1e..3b7d544394 100644 --- a/interface/src/DiscoverabilityManager.cpp +++ b/interface/src/DiscoverabilityManager.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -36,11 +37,11 @@ const QString SESSION_ID_KEY = "session_id"; void DiscoverabilityManager::updateLocation() { auto accountManager = DependencyManager::get(); - - if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) { - auto addressManager = DependencyManager::get(); - DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); + auto addressManager = DependencyManager::get(); + auto& domainHandler = DependencyManager::get()->getDomainHandler(); + + if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) { // construct a QJsonObject given the user's current address information QJsonObject rootObject; @@ -48,8 +49,6 @@ void DiscoverabilityManager::updateLocation() { QString pathString = addressManager->currentPath(); - const QString LOCATION_KEY_IN_ROOT = "location"; - const QString PATH_KEY_IN_LOCATION = "path"; locationObject.insert(PATH_KEY_IN_LOCATION, pathString); @@ -90,6 +89,7 @@ void DiscoverabilityManager::updateLocation() { // we have a changed location, send it now _lastLocationObject = locationObject; + const QString LOCATION_KEY_IN_ROOT = "location"; rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject); apiPath = API_USER_LOCATION_PATH; @@ -109,6 +109,9 @@ void DiscoverabilityManager::updateLocation() { accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation, callbackParameters); } + + // Update Steam + SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentAddress()); } void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) { diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index de95269d1a..f775efe2f6 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -13,7 +13,12 @@ #include +#include #include +#include +#include +#include +#include #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -41,9 +46,6 @@ public: STEAM_CALLBACK(SteamTicketRequests, onGetAuthSessionTicketResponse, GetAuthSessionTicketResponse_t, _getAuthSessionTicketResponse); - STEAM_CALLBACK(SteamTicketRequests, onGameRichPresenceJoinRequested, - GameRichPresenceJoinRequested_t, _gameRichPresenceJoinRequestedResponse); - private: struct PendingTicket { HAuthTicket authTicket; @@ -55,8 +57,7 @@ private: }; SteamTicketRequests::SteamTicketRequests() : - _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse), - _gameRichPresenceJoinRequestedResponse(this, &SteamTicketRequests::onGameRichPresenceJoinRequested) + _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse) { } @@ -130,24 +131,37 @@ void SteamTicketRequests::onGetAuthSessionTicketResponse(GetAuthSessionTicketRes } } -#include -#include -#include -#include -#include -const QString PREFIX = "--url \""; -const QString SUFFIX = "\""; +const QString CONNECT_PREFIX = "--url \""; +const QString CONNECT_SUFFIX = "\""; -void SteamTicketRequests::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) { +class SteamCallbackManager { +public: + SteamCallbackManager(); + + STEAM_CALLBACK(SteamCallbackManager, onGameRichPresenceJoinRequested, + GameRichPresenceJoinRequested_t, _gameRichPresenceJoinRequestedResponse); + + SteamTicketRequests& getTicketRequests() { return _steamTicketRequests; } + +private: + SteamTicketRequests _steamTicketRequests; +}; + +SteamCallbackManager::SteamCallbackManager() : +_gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested) +{ +} + +void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) { auto url = QString::fromLocal8Bit(pCallback->m_rgchConnect); - if (url.startsWith(PREFIX) && url.endsWith(SUFFIX)) { - url.remove(0, PREFIX.size()); - url.remove(-SUFFIX.size(), SUFFIX.size()); + if (url.startsWith(CONNECT_PREFIX) && url.endsWith(CONNECT_SUFFIX)) { + url.remove(0, CONNECT_PREFIX.size()); + url.remove(-CONNECT_SUFFIX.size(), CONNECT_SUFFIX.size()); } - qDebug() << "Joining:" << url; + qDebug() << "Joining Steam Friends at:" << url; auto mimeData = new QMimeData(); mimeData->setUrls(QList() << QUrl(url)); auto event = new QDropEvent(QPointF(0,0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier); @@ -156,9 +170,8 @@ void SteamTicketRequests::onGameRichPresenceJoinRequested(GameRichPresenceJoinRe } - static std::atomic_bool initialized { false }; -static SteamTicketRequests steamTicketRequests; +static SteamCallbackManager steamCallbackManager; bool SteamClient::isRunning() { @@ -171,11 +184,6 @@ bool SteamClient::isRunning() { bool SteamClient::init() { if (SteamAPI_IsSteamRunning() && !initialized) { initialized = SteamAPI_Init(); - - if (initialized) { - SteamFriends()->SetRichPresence("status", "Localhost"); - SteamFriends()->SetRichPresence("connect", "--url \"hifi://10.0.0.185:40117/10,10,10\""); - } } return initialized; } @@ -185,7 +193,7 @@ void SteamClient::shutdown() { SteamAPI_Shutdown(); } - steamTicketRequests.stopAll(); + steamCallbackManager.getTicketRequests().stopAll(); } void SteamClient::runCallbacks() { @@ -218,7 +226,16 @@ void SteamClient::requestTicket(TicketRequestCallback callback) { return; } - steamTicketRequests.startRequest(callback); + steamCallbackManager.getTicketRequests().startRequest(callback); } +void SteamClient::updateLocation(QString status, QUrl locationUrl) { + if (!initialized) { + return; + } + auto connectStr = locationUrl.isEmpty() ? "" : CONNECT_PREFIX + locationUrl.toString() + CONNECT_SUFFIX; + + SteamFriends()->SetRichPresence("status", status.toLocal8Bit().data()); + SteamFriends()->SetRichPresence("connect", connectStr.toLocal8Bit().data()); +} diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h index f7ca775829..9ce127f3cb 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h @@ -20,6 +20,8 @@ using Ticket = QByteArray; using TicketRequestCallback = std::function; +class QUrl; + class SteamClient { public: static bool isRunning(); @@ -30,6 +32,7 @@ public: static void runCallbacks(); static void requestTicket(TicketRequestCallback callback); + static void updateLocation(QString status, QUrl locationUrl); }; From 42c9a695ff5edd327329fa096879abb17765119e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 27 Jul 2016 17:45:45 -0700 Subject: [PATCH 082/279] Invite friends in HMD --- interface/src/Application.cpp | 4 +++ .../src/steamworks-wrapper/SteamClient.cpp | 14 +++++++-- .../src/steamworks-wrapper/SteamClient.h | 16 +++++++++++ scripts/system/assets/images/tools/steam.jpeg | Bin 0 -> 979 bytes scripts/system/steam.js | 27 ++++++++++++++++++ 5 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 scripts/system/assets/images/tools/steam.jpeg create mode 100644 scripts/system/steam.js diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 58284682f1..a1016d90fb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1599,6 +1599,8 @@ void Application::initializeUi() { rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface()); rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor()); + + rootContext->setContextProperty("Steam", new SteamScriptingInterface(engine)); _glWidget->installEventFilter(offscreenUi.data()); @@ -4799,6 +4801,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Users", DependencyManager::get().data()); + + scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine)); } bool Application::canAcceptURL(const QString& urlString) const { diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index f775efe2f6..ee6711e6f6 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -149,7 +149,7 @@ private: }; SteamCallbackManager::SteamCallbackManager() : -_gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested) + _gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested) { } @@ -161,7 +161,7 @@ void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinR url.remove(-CONNECT_SUFFIX.size(), CONNECT_SUFFIX.size()); } - qDebug() << "Joining Steam Friends at:" << url; + qDebug() << "Joining Steam Friend at:" << url; auto mimeData = new QMimeData(); mimeData->setUrls(QList() << QUrl(url)); auto event = new QDropEvent(QPointF(0,0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier); @@ -169,7 +169,6 @@ void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinR QCoreApplication::postEvent(qApp, event); } - static std::atomic_bool initialized { false }; static SteamCallbackManager steamCallbackManager; @@ -239,3 +238,12 @@ void SteamClient::updateLocation(QString status, QUrl locationUrl) { SteamFriends()->SetRichPresence("status", status.toLocal8Bit().data()); SteamFriends()->SetRichPresence("connect", connectStr.toLocal8Bit().data()); } + +void SteamClient::openInviteOverlay() { + if (!initialized) { + return; + } + + qDebug() << "Inviting steam friends"; + SteamFriends()->ActivateGameOverlayInviteDialog(CSteamID()); +} diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h index 9ce127f3cb..7c958c4b39 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h @@ -15,6 +15,7 @@ #include +#include #include using Ticket = QByteArray; @@ -33,6 +34,21 @@ public: static void requestTicket(TicketRequestCallback callback); static void updateLocation(QString status, QUrl locationUrl); + static void openInviteOverlay(); + +}; + +class SteamScriptingInterface : public QObject { + Q_OBJECT + + Q_PROPERTY(bool isRunning READ isRunning) + +public: + SteamScriptingInterface(QObject* parent) : QObject(parent) {} + +public slots: + bool isRunning() const { return SteamClient::isRunning(); } + void openInviteOverlay() const { SteamClient::openInviteOverlay(); } }; diff --git a/scripts/system/assets/images/tools/steam.jpeg b/scripts/system/assets/images/tools/steam.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..a39fdf9bc62c72467460026d639f13fc6910831c GIT binary patch literal 979 zcmex=o^zf-vy^0D~Y0gAs!fGoum%lOQ9rAmjfd4AKk?Ow5cRr@{dn3oAP_Bh&vQ z45k7MjLgi8EF3JXtPp8NCT11}RzV>)MZ-XLVG$+A#KK0S;7K1uohDv}biWfTeoY%~70>``pjeUEy0>($=!2;Y-a92A1ZY`BK`}T9%8N`dqFn?})C| zI`GWEwaDj~jO}#>yXw`>llf#%N#~yJYj>N#sLWC&XR@^DhSPGVwVMt&S|5H~JfR7`#um9y16f5)x(ibvX>(Na?nZq-m{`SqZ`D(I$!z#IMj z@>{-czVmqHrsf6peHA?)mH!1>f0lT2u591>6$h_g`(yF>+?!A_YwyCYt24jFMv1*y zGHtelWpH=EfdgMHmi{;e!m(3JJ0Q;4S>^Dg6ej(@dgylp#sORliY+bUGOf}6KR z<(KSpxkFP^@2bYWFc?i_THY?YUSd|p; z>^l@I+q!h6hL@cvLxD);@ZFFMj=1GxlF(C-BPYm>(a Date: Thu, 28 Jul 2016 16:54:17 -0700 Subject: [PATCH 083/279] Add steam.js to default scripts --- scripts/defaultScripts.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 0efcd0c140..46439541f1 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -9,6 +9,7 @@ // +Script.load("system/steam.js"); Script.load("system/progress.js"); Script.load("system/away.js"); Script.load("system/users.js"); From af76e47629751638fe9abfece4c1a1889c11cf02 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 28 Jul 2016 16:59:25 -0700 Subject: [PATCH 084/279] Fix linux build --- .../steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index ee6711e6f6..544868b2f7 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push From 6b861e680f46f5bddae958b67b55045f1360660c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 29 Jul 2016 11:46:21 -0700 Subject: [PATCH 085/279] More work on lobby creation/invites --- .../src/steamworks-wrapper/SteamClient.cpp | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index 544868b2f7..c28be7f344 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -142,6 +142,15 @@ public: STEAM_CALLBACK(SteamCallbackManager, onGameRichPresenceJoinRequested, GameRichPresenceJoinRequested_t, _gameRichPresenceJoinRequestedResponse); + STEAM_CALLBACK(SteamCallbackManager, onLobbyCreated, + LobbyCreated_t, _lobbyCreatedResponse); + + STEAM_CALLBACK(SteamCallbackManager, onGameLobbyJoinRequested, + GameLobbyJoinRequested_t, _gameLobbyJoinRequestedResponse); + + STEAM_CALLBACK(SteamCallbackManager, onLobbyEnter, + LobbyEnter_t, _lobbyEnterResponse); + SteamTicketRequests& getTicketRequests() { return _steamTicketRequests; } private: @@ -149,7 +158,10 @@ private: }; SteamCallbackManager::SteamCallbackManager() : - _gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested) + _gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested), + _lobbyCreatedResponse(this, &SteamCallbackManager::onLobbyCreated), + _gameLobbyJoinRequestedResponse(this, &SteamCallbackManager::onGameLobbyJoinRequested), + _lobbyEnterResponse(this, &SteamCallbackManager::onLobbyEnter) { } @@ -169,6 +181,38 @@ void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinR QCoreApplication::postEvent(qApp, event); } +void SteamCallbackManager::onLobbyCreated(LobbyCreated_t* pCallback) { + qDebug() << pCallback->m_eResult << pCallback->m_ulSteamIDLobby; + if (pCallback->m_eResult == k_EResultOK) { + qDebug() << "Inviting steam friends"; + + SteamMatchmaking()->SetLobbyData(pCallback->m_ulSteamIDLobby, "connect", + SteamFriends()->GetFriendRichPresence(SteamUser()->GetSteamID(), "connect")); + SteamMatchmaking()->SetLobbyMemberData(pCallback->m_ulSteamIDLobby, + "Creator", "true"); + SteamFriends()->ActivateGameOverlayInviteDialog(pCallback->m_ulSteamIDLobby); + } +} + +void SteamCallbackManager::onGameLobbyJoinRequested(GameLobbyJoinRequested_t* pCallback) { + qDebug() << "onGameLobbyJoinRequested"; + SteamMatchmaking()->JoinLobby(pCallback->m_steamIDLobby); +} + +void SteamCallbackManager::onLobbyEnter(LobbyEnter_t* pCallback) { + qDebug() << "onLobbyEnter"; + auto creator = SteamMatchmaking()->GetLobbyMemberData(pCallback->m_ulSteamIDLobby, SteamUser()->GetSteamID(), "creator"); + if (strcmp(creator, "true") == 0) { + qDebug() << "Created lobby"; + SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby); + } else if (pCallback->m_EChatRoomEnterResponse == k_EChatRoomEnterResponseSuccess) { + qDebug() << "Success"; + auto connectValue = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect"); + qDebug() << connectValue; + } +} + + static std::atomic_bool initialized { false }; static SteamCallbackManager steamCallbackManager; @@ -244,6 +288,7 @@ void SteamClient::openInviteOverlay() { return; } - qDebug() << "Inviting steam friends"; - SteamFriends()->ActivateGameOverlayInviteDialog(CSteamID()); + qDebug() << "Creating steam lobby"; + static const int MAX_LOBBY_SIZE = 20; + SteamMatchmaking()->CreateLobby(k_ELobbyTypePrivate, MAX_LOBBY_SIZE); } From 7ec2f98cf2a081e8073ca5d682c34d82d90e3997 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 29 Jul 2016 13:34:10 -0700 Subject: [PATCH 086/279] Join lobby on startup --- interface/src/Application.cpp | 8 +++ .../src/steamworks-wrapper/SteamClient.cpp | 54 +++++++++++-------- .../src/steamworks-wrapper/SteamClient.h | 1 + 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a1016d90fb..00a092f8c7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3224,6 +3224,14 @@ void Application::init() { addressLookupString = arguments().value(urlIndex + 1); } + // when +connect_lobby in command line, join steam lobby + const QString STEAM_LOBBY_COMMAND_LINE_KEY = "+connect_lobby"; + int lobbyIndex = arguments().indexOf(STEAM_LOBBY_COMMAND_LINE_KEY); + if (lobbyIndex != -1) { + QString lobbyId = arguments().value(lobbyIndex + 1); + SteamClient::joinLobby(lobbyId); + } + Setting::Handle firstRun { Settings::firstRun, true }; if (addressLookupString.isEmpty() && firstRun.get()) { qDebug() << "First run and no URL passed... attempting to go to Home or Entry..."; diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index c28be7f344..3efeba956a 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -165,9 +165,7 @@ SteamCallbackManager::SteamCallbackManager() : { } -void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) { - auto url = QString::fromLocal8Bit(pCallback->m_rgchConnect); - +void parseUrlAndGo(QString url) { if (url.startsWith(CONNECT_PREFIX) && url.endsWith(CONNECT_SUFFIX)) { url.remove(0, CONNECT_PREFIX.size()); url.remove(-CONNECT_SUFFIX.size(), CONNECT_SUFFIX.size()); @@ -176,40 +174,50 @@ void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinR qDebug() << "Joining Steam Friend at:" << url; auto mimeData = new QMimeData(); mimeData->setUrls(QList() << QUrl(url)); - auto event = new QDropEvent(QPointF(0,0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier); + auto event = new QDropEvent(QPointF(0, 0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier); QCoreApplication::postEvent(qApp, event); } +void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) { + auto url = QString::fromLocal8Bit(pCallback->m_rgchConnect); + + parseUrlAndGo(url); +} + + + void SteamCallbackManager::onLobbyCreated(LobbyCreated_t* pCallback) { qDebug() << pCallback->m_eResult << pCallback->m_ulSteamIDLobby; if (pCallback->m_eResult == k_EResultOK) { qDebug() << "Inviting steam friends"; - SteamMatchmaking()->SetLobbyData(pCallback->m_ulSteamIDLobby, "connect", - SteamFriends()->GetFriendRichPresence(SteamUser()->GetSteamID(), "connect")); - SteamMatchmaking()->SetLobbyMemberData(pCallback->m_ulSteamIDLobby, - "Creator", "true"); + auto url = SteamFriends()->GetFriendRichPresence(SteamUser()->GetSteamID(), "connect"); + SteamMatchmaking()->SetLobbyData(pCallback->m_ulSteamIDLobby, "connect", url); + SteamMatchmaking()->SetLobbyMemberData(pCallback->m_ulSteamIDLobby, "creator", "true"); SteamFriends()->ActivateGameOverlayInviteDialog(pCallback->m_ulSteamIDLobby); } } void SteamCallbackManager::onGameLobbyJoinRequested(GameLobbyJoinRequested_t* pCallback) { - qDebug() << "onGameLobbyJoinRequested"; + qDebug() << "Joining Steam lobby"; SteamMatchmaking()->JoinLobby(pCallback->m_steamIDLobby); } void SteamCallbackManager::onLobbyEnter(LobbyEnter_t* pCallback) { - qDebug() << "onLobbyEnter"; - auto creator = SteamMatchmaking()->GetLobbyMemberData(pCallback->m_ulSteamIDLobby, SteamUser()->GetSteamID(), "creator"); - if (strcmp(creator, "true") == 0) { - qDebug() << "Created lobby"; - SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby); - } else if (pCallback->m_EChatRoomEnterResponse == k_EChatRoomEnterResponseSuccess) { - qDebug() << "Success"; - auto connectValue = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect"); - qDebug() << connectValue; + if (pCallback->m_EChatRoomEnterResponse != k_EChatRoomEnterResponseSuccess) { + qWarning() << "An error occured while joing the Steam lobby:" << pCallback->m_EChatRoomEnterResponse; + return; } + + auto creator = SteamMatchmaking()->GetLobbyMemberData(pCallback->m_ulSteamIDLobby, + SteamUser()->GetSteamID(), "creator"); + if (strcmp(creator, "true") != 0) { + auto url = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect"); + parseUrlAndGo(url); + } + + SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby); } @@ -218,9 +226,6 @@ static SteamCallbackManager steamCallbackManager; bool SteamClient::isRunning() { - if (!initialized) { - init(); - } return initialized; } @@ -292,3 +297,10 @@ void SteamClient::openInviteOverlay() { static const int MAX_LOBBY_SIZE = 20; SteamMatchmaking()->CreateLobby(k_ELobbyTypePrivate, MAX_LOBBY_SIZE); } + + +void SteamClient::joinLobby(QString lobbyIdStr) { + qDebug() << "Trying to join Steam lobby:" << lobbyIdStr; + CSteamID lobbyId(lobbyIdStr.toULongLong()); + SteamMatchmaking()->JoinLobby(lobbyId); +} \ No newline at end of file diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h index 7c958c4b39..5bf0d4db56 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h @@ -35,6 +35,7 @@ public: static void requestTicket(TicketRequestCallback callback); static void updateLocation(QString status, QUrl locationUrl); static void openInviteOverlay(); + static void joinLobby(QString lobbyId); }; From 2af32ca60ed54c78df0771aeeb15d9f7c6cdb077 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 29 Jul 2016 13:55:39 -0700 Subject: [PATCH 087/279] Only leave lobby if you were invited --- .../src/steamworks-wrapper/SteamClient.cpp | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index 3efeba956a..f6cf13effd 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -188,19 +188,17 @@ void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinR void SteamCallbackManager::onLobbyCreated(LobbyCreated_t* pCallback) { - qDebug() << pCallback->m_eResult << pCallback->m_ulSteamIDLobby; if (pCallback->m_eResult == k_EResultOK) { - qDebug() << "Inviting steam friends"; + qDebug() << "Inviting steam friends" << pCallback->m_ulSteamIDLobby; auto url = SteamFriends()->GetFriendRichPresence(SteamUser()->GetSteamID(), "connect"); SteamMatchmaking()->SetLobbyData(pCallback->m_ulSteamIDLobby, "connect", url); - SteamMatchmaking()->SetLobbyMemberData(pCallback->m_ulSteamIDLobby, "creator", "true"); SteamFriends()->ActivateGameOverlayInviteDialog(pCallback->m_ulSteamIDLobby); } } void SteamCallbackManager::onGameLobbyJoinRequested(GameLobbyJoinRequested_t* pCallback) { - qDebug() << "Joining Steam lobby"; + qDebug() << "Joining Steam lobby" << pCallback->m_steamIDLobby.ConvertToUint64(); SteamMatchmaking()->JoinLobby(pCallback->m_steamIDLobby); } @@ -210,14 +208,14 @@ void SteamCallbackManager::onLobbyEnter(LobbyEnter_t* pCallback) { return; } - auto creator = SteamMatchmaking()->GetLobbyMemberData(pCallback->m_ulSteamIDLobby, - SteamUser()->GetSteamID(), "creator"); - if (strcmp(creator, "true") != 0) { - auto url = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect"); - parseUrlAndGo(url); - } + qDebug() << "Entered Steam lobby" << pCallback->m_ulSteamIDLobby; - SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby); + if (SteamMatchmaking()->GetLobbyOwner(pCallback->m_ulSteamIDLobby) != SteamUser()->GetSteamID()) { + auto url = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect"); + qDebug() << "Jumping to" << url; + parseUrlAndGo(url); + SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby); + } } @@ -293,13 +291,23 @@ void SteamClient::openInviteOverlay() { return; } - qDebug() << "Creating steam lobby"; + qDebug() << "Creating Steam lobby"; static const int MAX_LOBBY_SIZE = 20; SteamMatchmaking()->CreateLobby(k_ELobbyTypePrivate, MAX_LOBBY_SIZE); } void SteamClient::joinLobby(QString lobbyIdStr) { + if (!initialized) { + if (SteamAPI_IsSteamRunning()) { + init(); + } + else { + qWarning() << "Steam is not running"; + return; + } + } + qDebug() << "Trying to join Steam lobby:" << lobbyIdStr; CSteamID lobbyId(lobbyIdStr.toULongLong()); SteamMatchmaking()->JoinLobby(lobbyId); From aa2ae31aab3b5b9b6fe4b9359f40ee5e3efa9600 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 29 Jul 2016 19:30:20 -0700 Subject: [PATCH 088/279] CR UI fixes --- .../qml/LoginDialog/CompleteProfileBody.qml | 15 +- .../qml/LoginDialog/EmailSentBody.qml | 83 ----------- .../qml/LoginDialog/LinkAccountBody.qml | 51 ++----- .../qml/LoginDialog/RecoverPasswordBody.qml | 140 ------------------ .../qml/LoginDialog/UsernameCollisionBody.qml | 30 +++- interface/src/ui/LoginDialog.cpp | 4 +- .../networking/src/NetworkingConstants.h | 2 +- 7 files changed, 50 insertions(+), 275 deletions(-) delete mode 100644 interface/resources/qml/LoginDialog/EmailSentBody.qml delete mode 100644 interface/resources/qml/LoginDialog/RecoverPasswordBody.qml diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml index 12d2cee73e..fc7eac3d00 100644 --- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -77,22 +77,17 @@ Item { topMargin: hifi.dimensions.contentSpacing.y } - text: "Already have a High Fidelity profile? Link to an existing profile here." + text: "Already have a High Fidelity profile? Link to an existing profile here." - font.underline: true wrapMode: Text.WordWrap - color: hifi.colors.blueAccent lineHeight: 2 lineHeightMode: Text.ProportionalHeight horizontalAlignment: Text.AlignHCenter - MouseArea { - anchors.fill: parent - onClicked: { - bodyLoader.source = "LinkAccountBody.qml" - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } + onLinkActivated: { + bodyLoader.source = "LinkAccountBody.qml" + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height } } diff --git a/interface/resources/qml/LoginDialog/EmailSentBody.qml b/interface/resources/qml/LoginDialog/EmailSentBody.qml deleted file mode 100644 index 489385864b..0000000000 --- a/interface/resources/qml/LoginDialog/EmailSentBody.qml +++ /dev/null @@ -1,83 +0,0 @@ -// -// EmailSentBody.qml -// -// Created by Clement on 7/18/16 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import Hifi 1.0 -import QtQuick 2.4 - -import "../controls-uit" -import "../styles-uit" - -Item { - id: emailSentBody - clip: true - width: root.pane.width - height: root.pane.height - - property string email: "" - - QtObject { - id: d - readonly property int minWidth: 480 - readonly property int maxWidth: 1280 - readonly property int minHeight: 120 - readonly property int maxHeight: 720 - - function resize() { - var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth) - var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height - - root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) - root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) - } - } - - MenuItem { - id: mainTextContainer - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - text: qsTr("An email with instructions on reseting your password was sent to
") + email + "" - wrapMode: Text.WordWrap - color: hifi.colors.baseGrayHighlight - lineHeight: 2 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - } - - Row { - id: buttons - anchors { - top: mainTextContainer.bottom - horizontalCenter: parent.horizontalCenter - margins: 0 - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Close"); - - onClicked: root.destroy() - } - } - - Component.onCompleted: { - root.title = qsTr("Email Sent") - root.iconText = "" - d.resize(); - } -} diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index da5f0f1a42..137556964f 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -22,9 +22,8 @@ Item { width: root.pane.width height: root.pane.height - property bool existingEmail: false - function login() { + mainTextContainer.visible = false loginDialog.login(usernameField.text, passwordField.text) } @@ -36,8 +35,8 @@ Item { readonly property int maxHeight: 720 function resize() { - var targetWidth = Math.max(titleWidth, mainTextContainer.visible ? mainTextContainer.contentWidth : 0) - var targetHeight = (mainTextContainer.visible ? mainTextContainer.height : 0) + + var targetWidth = Math.max(titleWidth, form.contentWidth) + var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height + 4 * hifi.dimensions.contentSpacing.y + form.height + 4 * hifi.dimensions.contentSpacing.y + buttons.height @@ -46,29 +45,29 @@ Item { } } - MenuItem { + ShortcutText { id: mainTextContainer anchors { top: parent.top - horizontalCenter: parent.horizontalCenter + left: parent.left margins: 0 topMargin: hifi.dimensions.contentSpacing.y } - visible: existingEmail - text: qsTr("Your Steam account's email matches an existing High Fidelity Profile") + visible: false + + text: qsTr("Username or password incorrect.") wrapMode: Text.WordWrap color: hifi.colors.redAccent - lineHeight: 2 + lineHeight: 1 lineHeightMode: Text.ProportionalHeight horizontalAlignment: Text.AlignHCenter } - Column { id: form anchors { - top: mainTextContainer.visible ? mainTextContainer.bottom : parent.top + top: mainTextContainer.bottom left: parent.left margins: 0 topMargin: 2 * hifi.dimensions.contentSpacing.y @@ -93,22 +92,12 @@ Item { verticalCenter: parent.verticalCenter } - text: "Need help?" - - color: hifi.colors.blueAccent - font.underline: true + text: "Forgot Username?" verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - MouseArea { - anchors.fill: parent - onClicked: { - bodyLoader.source = "RecoverPasswordBody.qml" - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } - } + onLinkActivated: loginDialog.openUrl(link) } } Row { @@ -130,22 +119,12 @@ Item { verticalCenter: parent.verticalCenter } - text: "Need help?" - - color: hifi.colors.blueAccent - font.underline: true + text: "Forgot Password?" verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - MouseArea { - anchors.fill: parent - onClicked: { - bodyLoader.source = "RecoverPasswordBody.qml" - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } - } + onLinkActivated: loginDialog.openUrl(link) } } @@ -205,7 +184,7 @@ Item { } onHandleLoginFailed: { console.log("Login Failed") - + mainTextContainer.visible = true } onHandleLinkCompleted: { console.log("Link Succeeded") diff --git a/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml b/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml deleted file mode 100644 index 3c6e101e2a..0000000000 --- a/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml +++ /dev/null @@ -1,140 +0,0 @@ -// -// RecoverPasswordBody.qml -// -// Created by Clement on 7/18/16 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import Hifi 1.0 -import QtQuick 2.4 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 as OriginalStyles - -import "../controls-uit" -import "../styles-uit" - -Item { - id: recoverPasswordBody - clip: true - width: root.pane.width - height: root.pane.height - - function send() { - loginDialog.sendRecoveryEmail(emailField.text) - - bodyLoader.setSource("EmailSentBody.qml", { "email": emailField.text }) - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } - - QtObject { - id: d - readonly property int minWidth: 480 - readonly property int maxWidth: 1280 - readonly property int minHeight: 120 - readonly property int maxHeight: 720 - - function resize() { - var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth) - var targetHeight = mainTextContainer.height + - 3 * hifi.dimensions.contentSpacing.y + emailField.height + - 4 * hifi.dimensions.contentSpacing.y + buttons.height - - root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) - root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) - } - } - - MenuItem { - id: mainTextContainer - anchors { - top: parent.top - left: parent.left - right: parent.right - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - text: qsTr("In order to help you reset your password, we will send an
email with instructions to your email address.") - wrapMode: Text.WordWrap - color: hifi.colors.baseGrayHighlight - lineHeight: 1 - horizontalAlignment: Text.AlignHLeft - } - - - TextField { - id: emailField - anchors { - top: mainTextContainer.bottom - left: parent.left - margins: 0 - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - - width: 350 - - label: "Email address" - - Component.onCompleted: { - emailField.forceActiveFocus() - } - } - - Row { - id: buttons - anchors { - top: emailField.bottom - right: parent.right - margins: 0 - topMargin: 3 * hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - width: 200 - - text: qsTr("Send recovery email") - color: hifi.buttons.blue - - onClicked: recoverPasswordBody.send() - } - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Back") - - onClicked: { - bodyLoader.source = "LinkAccountBody.qml" - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } - } - } - - Component.onCompleted: { - root.title = qsTr("Recover Password") - root.iconText = "<" - d.resize(); - } - - Keys.onPressed: { - if (!visible) { - return - } - - switch (event.key) { - case Qt.Key_Enter: - case Qt.Key_Return: - event.accepted = true - recoverPasswordBody.send() - break - } - } -} diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index f0663631a8..7e22b11f8b 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -22,6 +22,11 @@ Item { width: root.pane.width height: root.pane.height + function create() { + mainTextContainer.visible = false + loginDialog.createAccountFromStream(textField.text) + } + QtObject { id: d readonly property int minWidth: 480 @@ -82,12 +87,14 @@ Item { topMargin: 3 * hifi.dimensions.contentSpacing.y } - text: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service") + text: qsTr("By creating this user profile, you agree to High Fidelity's Terms of Service") wrapMode: Text.WordWrap color: hifi.colors.baseGrayHighlight lineHeight: 1 lineHeightMode: Text.ProportionalHeight horizontalAlignment: Text.AlignHCenter + + onLinkActivated: loginDialog.openUrl(link) } Row { @@ -108,9 +115,7 @@ Item { text: qsTr("Create your profile") color: hifi.buttons.blue - onClicked: { - loginDialog.createAccountFromStream(textField.text) - } + onClicked: usernameCollisionBody.create() } Button { @@ -136,6 +141,9 @@ Item { } onHandleCreateFailed: { console.log("Create Failed: " + error) + + mainTextContainer.visible = true + mainTextContainer.text = "\"" + textField.text + qsTr("\" is invalid or already taken.") } onHandleLoginCompleted: { console.log("Login Succeeded") @@ -148,4 +156,18 @@ Item { console.log("Login Failed") } } + + Keys.onPressed: { + if (!visible) { + return + } + + switch (event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + event.accepted = true + usernameCollisionBody.create() + break + } + } } diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index b65e111b16..78aacb1216 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -132,7 +132,9 @@ void LoginDialog::createAccountFromStream(QString username) { } void LoginDialog::openUrl(const QString& url) { - QDesktopServices::openUrl(url); + auto offscreenUi = DependencyManager::get(); + auto browser = offscreenUi->load("Browser.qml"); + browser->setProperty("url", url); } void LoginDialog::sendRecoveryEmail(const QString& email) { diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 154470201f..bbbaa5ebbe 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -15,7 +15,7 @@ #include namespace NetworkingConstants { - const QUrl METAVERSE_SERVER_URL = QUrl("https://hifi.ngrok.io"); + const QUrl METAVERSE_SERVER_URL = QUrl("http://10.0.0.146:8080"); } #endif // hifi_NetworkingConstants_h From 51b45f8f73e58408b78d72ed3cea03016d3bfe6b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 3 Aug 2016 16:04:48 -0700 Subject: [PATCH 089/279] Steam invite are facing the current position --- interface/src/DiscoverabilityManager.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 12 ++++--- libraries/networking/src/AddressManager.cpp | 39 +++++++++++++++++++-- libraries/networking/src/AddressManager.h | 6 ++-- 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp index 3b7d544394..dd80dadca7 100644 --- a/interface/src/DiscoverabilityManager.cpp +++ b/interface/src/DiscoverabilityManager.cpp @@ -111,7 +111,7 @@ void DiscoverabilityManager::updateLocation() { } // Update Steam - SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentAddress()); + SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentFacingAddress()); } void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index de5455fc14..57e379a9ac 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -122,11 +122,13 @@ MyAvatar::MyAvatar(RigPointer rig) : _driveKeys[i] = 0.0f; } + + // Necessary to select the correct slot + using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool); + // connect to AddressManager signal for location jumps - connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, - [=](const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation){ - goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation); - }); + connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, + this, static_cast(&MyAvatar::goToLocation)); _characterController.setEnabled(true); @@ -1859,7 +1861,7 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition, glm::quat quatOrientation = cancelOutRollAndPitch(newOrientation); if (shouldFaceLocation) { - quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + quatOrientation = newOrientation * glm::angleAxis(PI, Vectors::UP); // move the user a couple units away const float DISTANCE_TO_USER = 2.0f; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index ae6aad3c4f..6760d44244 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -46,7 +47,7 @@ bool AddressManager::isConnected() { return DependencyManager::get()->getDomainHandler().isConnected(); } -const QUrl AddressManager::currentAddress() const { +QUrl AddressManager::currentAddress() const { QUrl hifiURL; hifiURL.setScheme(HIFI_URL_SCHEME); @@ -61,6 +62,21 @@ const QUrl AddressManager::currentAddress() const { return hifiURL; } +QUrl AddressManager::currentFacingAddress() const { + QUrl hifiURL; + + hifiURL.setScheme(HIFI_URL_SCHEME); + hifiURL.setHost(_host); + + if (_port != 0 && _port != DEFAULT_DOMAIN_SERVER_PORT) { + hifiURL.setPort(_port); + } + + hifiURL.setPath(currentFacingPath()); + + return hifiURL; +} + void AddressManager::loadSettings(const QString& lookupString) { if (lookupString.isEmpty()) { handleUrl(currentAddressHandle.get().toString(), LookupTrigger::StartupFromSettings); @@ -97,7 +113,7 @@ void AddressManager::storeCurrentAddress() { currentAddressHandle.set(currentAddress()); } -const QString AddressManager::currentPath(bool withOrientation) const { +QString AddressManager::currentPath(bool withOrientation) const { if (_positionGetter) { QString pathString = "/" + createByteArray(_positionGetter()); @@ -121,6 +137,25 @@ const QString AddressManager::currentPath(bool withOrientation) const { } } +QString AddressManager::currentFacingPath() const { + if (_positionGetter && _orientationGetter) { + auto position = _positionGetter(); + auto orientation = _orientationGetter(); + + // move the user a couple units away + const float DISTANCE_TO_USER = 2.0f; + position += orientation * Vectors::FRONT * DISTANCE_TO_USER; + + // rotate the user by 180 degrees + orientation = orientation * glm::angleAxis(PI, Vectors::UP); + + return "/" + createByteArray(position) + "/" + createByteArray(orientation); + } else { + qCDebug(networking) << "Cannot create address path without a getter for position/orientation."; + return QString(); + } +} + const JSONCallbackParameters& AddressManager::apiCallbackParameters() { static bool hasSetupParameters = false; static JSONCallbackParameters callbackParams; diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 2e9f177137..8ccddc5975 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -57,8 +57,10 @@ public: bool isConnected(); const QString& getProtocol() { return HIFI_URL_SCHEME; }; - const QUrl currentAddress() const; - const QString currentPath(bool withOrientation = true) const; + QUrl currentAddress() const; + QUrl currentFacingAddress() const; + QString currentPath(bool withOrientation = true) const; + QString currentFacingPath() const; const QUuid& getRootPlaceID() const { return _rootPlaceID; } const QString& getPlaceName() const { return _placeName; } From 61d07cf952c1ab2224d68c390825156e0ae1a362 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 5 Aug 2016 13:08:18 -0700 Subject: [PATCH 090/279] Restore mateverse URL --- libraries/networking/src/NetworkingConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index bbbaa5ebbe..a512ae8887 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -15,7 +15,7 @@ #include namespace NetworkingConstants { - const QUrl METAVERSE_SERVER_URL = QUrl("http://10.0.0.146:8080"); + const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com"); } #endif // hifi_NetworkingConstants_h From f08b2f9257f834380ca73c9fcbeacf5882b2cb36 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 8 Aug 2016 15:39:44 -0700 Subject: [PATCH 091/279] Remove temporary override to reload QML --- interface/src/Application.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 00a092f8c7..ac024f89c3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2244,12 +2244,11 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_X: - if (isMeta) { + if (isShifted && isMeta) { auto offscreenUi = DependencyManager::get(); -// offscreenUi->togglePinned(); - offscreenUi->getRootContext()->engine()->clearComponentCache(); - qDebug() << "Component cache cleared"; -// OffscreenUi::information("Debugging", "Component cache cleared"); + offscreenUi->togglePinned(); + //offscreenUi->getRootContext()->engine()->clearComponentCache(); + //OffscreenUi::information("Debugging", "Component cache cleared"); // placeholder for dialogs being converted to QML. } break; From 9b137570cfb2fefe613c96d7f888d9deaef0c7fe Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 15:21:35 -0700 Subject: [PATCH 092/279] Fix srgb_gen lookup table generation --- tools/srgb_gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/srgb_gen.py b/tools/srgb_gen.py index e17970209a..6db2c1da03 100644 --- a/tools/srgb_gen.py +++ b/tools/srgb_gen.py @@ -19,7 +19,7 @@ for i in range(NUM_VALUES): if s < 0.04045: l = s / 12.92 else: - l = ((s + 0.044) / 1.055) ** 2.4 + l = ((s + 0.055) / 1.055) ** 2.4 srgb_to_linear.append(l) # Format and print From b800aa793da8753f13279a06e32f6c2d8c24a1bd Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 15:45:05 -0700 Subject: [PATCH 093/279] Fix gamma correction adjusting uchar to float when unnecessary --- libraries/gpu/src/gpu/Texture.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index bd0ad0ce7b..25e4fa549c 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -788,9 +788,9 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< uint pixOffsetIndex = (x + y * width) * numComponents; // get color from texture and map to range [0, 1] - glm::vec3 clr(ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex]) * UCHAR_TO_FLOAT, - ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT, - ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT); + glm::vec3 clr(ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex]), + ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex + 1]), + ColorUtils::sRGB8ToLinearFloat(data[pixOffsetIndex + 2])); // scale color and add to previously accumulated coefficients sphericalHarmonicsScale(shBuffB.data(), order, From 43c1472b36681d4463d674c972c182fb9f0a518a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 15:47:53 -0700 Subject: [PATCH 094/279] Move srgbToLinear lookup table to external cpp --- libraries/shared/src/ColorUtils.cpp | 59 +++++++++++++++++++++++++++++ libraries/shared/src/ColorUtils.h | 51 ++----------------------- 2 files changed, 62 insertions(+), 48 deletions(-) create mode 100644 libraries/shared/src/ColorUtils.cpp diff --git a/libraries/shared/src/ColorUtils.cpp b/libraries/shared/src/ColorUtils.cpp new file mode 100644 index 0000000000..f0dfc89367 --- /dev/null +++ b/libraries/shared/src/ColorUtils.cpp @@ -0,0 +1,59 @@ +// +// ColorUtils.cpp +// libraries/shared/src +// +// Created by Ryan Huffman on 8/8/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ColorUtils.h" + +// Generated from python script in repository at `tools/srgb_gen.py` +const float srgbToLinearLookupTable[256] = { + 0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f, + 0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.0033465357639f, 0.00367650732405f, + 0.0040247170185f, 0.00439144203741f, 0.00477695348069f, 0.00518151670234f, 0.0056053916242f, 0.00604883302286f, + 0.00651209079259f, 0.00699541018727f, 0.00749903204323f, 0.00802319298538f, 0.00856812561807f, 0.00913405870222f, + 0.00972121732024f, 0.0103298230296f, 0.0109600940065f, 0.0116122451797f, 0.0122864883569f, 0.0129830323422f, + 0.0137020830473f, 0.0144438435961f, 0.0152085144229f, 0.0159962933655f, 0.0168073757529f, 0.0176419544884f, + 0.0185002201284f, 0.0193823609569f, 0.0202885630567f, 0.021219010376f, 0.0221738847934f, 0.0231533661781f, + 0.0241576324485f, 0.0251868596274f, 0.0262412218948f, 0.0273208916391f, 0.0284260395044f, 0.0295568344378f, + 0.030713443733f, 0.031896033073f, 0.0331047665709f, 0.0343398068087f, 0.035601314875f, 0.0368894504011f, + 0.0382043715953f, 0.0395462352767f, 0.0409151969069f, 0.0423114106208f, 0.043735029257f, 0.0451862043857f, + 0.0466650863369f, 0.0481718242269f, 0.0497065659841f, 0.051269458374f, 0.0528606470232f, 0.0544802764424f, + 0.0561284900496f, 0.0578054301911f, 0.059511238163f, 0.0612460542316f, 0.0630100176532f, 0.0648032666929f, + 0.0666259386438f, 0.0684781698444f, 0.0703600956966f, 0.0722718506823f, 0.0742135683801f, 0.0761853814813f, + 0.0781874218052f, 0.0802198203145f, 0.0822827071298f, 0.0843762115441f, 0.0865004620365f, 0.0886555862858f, + 0.0908417111834f, 0.0930589628467f, 0.095307466631f, 0.0975873471419f, 0.0998987282471f, 0.102241733088f, + 0.104616484091f, 0.107023102978f, 0.109461710778f, 0.111932427837f, 0.114435373827f, 0.116970667759f, + 0.119538427988f, 0.12213877223f, 0.124771817561f, 0.127437680436f, 0.13013647669f, 0.132868321554f, + 0.135633329655f, 0.138431615032f, 0.14126329114f, 0.144128470858f, 0.147027266498f, 0.149959789811f, + 0.152926151996f, 0.155926463708f, 0.158960835061f, 0.162029375639f, 0.165132194502f, 0.16826940019f, + 0.171441100733f, 0.174647403656f, 0.177888415984f, 0.18116424425f, 0.1844749945f, 0.187820772301f, + 0.191201682741f, 0.194617830442f, 0.19806931956f, 0.201556253794f, 0.20507873639f, 0.208636870145f, + 0.212230757414f, 0.215860500114f, 0.219526199729f, 0.223227957317f, 0.22696587351f, 0.230740048524f, + 0.234550582161f, 0.238397573812f, 0.242281122466f, 0.246201326708f, 0.25015828473f, 0.254152094331f, + 0.258182852922f, 0.26225065753f, 0.266355604803f, 0.270497791013f, 0.27467731206f, 0.278894263477f, + 0.28314874043f, 0.287440837727f, 0.291770649818f, 0.296138270798f, 0.300543794416f, 0.30498731407f, + 0.309468922818f, 0.313988713376f, 0.318546778125f, 0.323143209113f, 0.327778098057f, 0.332451536346f, + 0.337163615048f, 0.341914424909f, 0.346704056355f, 0.3515325995f, 0.356400144146f, 0.361306779784f, + 0.366252595599f, 0.371237680474f, 0.376262122991f, 0.381326011433f, 0.386429433787f, 0.39157247775f, + 0.396755230726f, 0.401977779832f, 0.407240211902f, 0.412542613484f, 0.417885070848f, 0.423267669986f, + 0.428690496614f, 0.434153636175f, 0.439657173841f, 0.445201194516f, 0.450785782838f, 0.45641102318f, + 0.462076999654f, 0.467783796112f, 0.473531496148f, 0.479320183101f, 0.485149940056f, 0.491020849848f, + 0.496932995061f, 0.502886458033f, 0.508881320855f, 0.514917665377f, 0.520995573204f, 0.527115125706f, + 0.533276404011f, 0.539479489012f, 0.54572446137f, 0.552011401512f, 0.558340389634f, 0.564711505705f, + 0.571124829465f, 0.57758044043f, 0.584078417891f, 0.590618840919f, 0.597201788364f, 0.603827338855f, + 0.610495570808f, 0.61720656242f, 0.623960391675f, 0.630757136346f, 0.637596873994f, 0.644479681971f, + 0.65140563742f, 0.658374817279f, 0.665387298282f, 0.672443156958f, 0.679542469633f, 0.686685312435f, + 0.693871761292f, 0.701101891933f, 0.708375779892f, 0.715693500506f, 0.723055128922f, 0.73046074009f, + 0.737910408773f, 0.74540420954f, 0.752942216776f, 0.760524504675f, 0.768151147248f, 0.775822218317f, + 0.783537791526f, 0.791297940333f, 0.799102738014f, 0.806952257669f, 0.814846572216f, 0.822785754396f, + 0.830769876775f, 0.838799011741f, 0.84687323151f, 0.854992608124f, 0.863157213454f, 0.871367119199f, + 0.879622396888f, 0.887923117882f, 0.896269353374f, 0.904661174391f, 0.913098651793f, 0.921581856277f, + 0.930110858375f, 0.938685728458f, 0.947306536733f, 0.955973353249f, 0.964686247894f, 0.973445290398f, + 0.982250550333f, 0.991102097114f, 1.0f +}; \ No newline at end of file diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index 921b26399d..fd0bbdd8ab 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -13,57 +13,12 @@ #define hifi_ColorUtils_h #include -#include + +#include "SharedUtil.h" #include "DependencyManager.h" -// Generated from python script in repository at `tools/srgb_gen.py` -static const float srgbToLinearLookupTable[256] = { - 0.0f, 0.000303526983549f, 0.000607053967098f, 0.000910580950647f, 0.0012141079342f, 0.00151763491774f, 0.00182116190129f, - 0.00212468888484f, 0.00242821586839f, 0.00273174285194f, 0.00303526983549f, 0.00251584218443f, 0.00279619194822f, - 0.00309396642819f, 0.00340946205345f, 0.00374296799396f, 0.00409476661624f, 0.00446513389425f, 0.00485433978143f, - 0.00526264854875f, 0.00569031909303f, 0.00613760521883f, 0.00660475589722f, 0.00709201550367f, 0.00759962403765f, - 0.00812781732551f, 0.00867682720861f, 0.00924688171802f, 0.00983820523704f, 0.0104510186528f, 0.0110855394981f, - 0.0117419820834f, 0.0124205576216f, 0.0131214743443f, 0.0138449376117f, 0.0145911500156f, 0.0153603114768f, - 0.0161526193372f, 0.0169682684465f, 0.0178074512441f, 0.0186703578377f, 0.0195571760767f, 0.0204680916222f, - 0.0214032880141f, 0.0223629467344f, 0.0233472472675f, 0.0243563671578f, 0.0253904820647f, 0.026449765815f, - 0.0275343904531f, 0.0286445262888f, 0.0297803419432f, 0.0309420043928f, 0.0321296790111f, 0.0333435296099f, - 0.0345837184768f, 0.0358504064137f, 0.0371437527716f, 0.0384639154854f, 0.0398110511069f, 0.0411853148367f, - 0.0425868605546f, 0.0440158408496f, 0.045472407048f, 0.046956709241f, 0.0484688963113f, 0.0500091159586f, - 0.0515775147244f, 0.0531742380159f, 0.0547994301291f, 0.0564532342711f, 0.058135792582f, 0.0598472461555f, - 0.0615877350593f, 0.063357398355f, 0.0651563741167f, 0.0669847994499f, 0.0688428105093f, 0.0707305425158f, - 0.0726481297741f, 0.0745957056885f, 0.0765734027789f, 0.0785813526965f, 0.0806196862387f, 0.0826885333636f, - 0.0847880232044f, 0.086918284083f, 0.0890794435236f, 0.0912716282659f, 0.0934949642776f, 0.0957495767668f, - 0.0980355901944f, 0.100353128286f, 0.102702314041f, 0.10508326975f, 0.107496116997f, 0.109940976678f, - 0.112417969007f, 0.114927213525f, 0.117468829116f, 0.120042934009f, 0.122649645793f, 0.125289081424f, - 0.127961357236f, 0.130666588944f, 0.13340489166f, 0.136176379898f, 0.138981167581f, 0.141819368051f, - 0.144691094076f, 0.147596457859f, 0.150535571041f, 0.153508544716f, 0.156515489432f, 0.1595565152f, - 0.1626317315f, 0.16574124729f, 0.168885171012f, 0.172063610595f, 0.175276673468f, 0.178524466557f, - 0.181807096302f, 0.185124668654f, 0.188477289086f, 0.191865062595f, 0.195288093712f, 0.198746486503f, - 0.202240344578f, 0.205769771096f, 0.209334868766f, 0.212935739858f, 0.216572486205f, 0.220245209207f, - 0.223954009837f, 0.227698988648f, 0.231480245773f, 0.235297880934f, 0.239151993444f, 0.243042682212f, - 0.246970045747f, 0.250934182163f, 0.254935189183f, 0.258973164144f, 0.263048203998f, 0.267160405319f, - 0.271309864307f, 0.27549667679f, 0.279720938228f, 0.283982743718f, 0.288282187998f, 0.292619365448f, - 0.296994370096f, 0.30140729562f, 0.305858235354f, 0.310347282289f, 0.314874529074f, 0.319440068025f, - 0.324043991126f, 0.32868639003f, 0.333367356062f, 0.338086980228f, 0.34284535321f, 0.347642565374f, - 0.352478706774f, 0.357353867148f, 0.36226813593f, 0.367221602246f, 0.372214354918f, 0.37724648247f, - 0.382318073128f, 0.387429214822f, 0.392579995191f, 0.397770501584f, 0.403000821062f, 0.408271040402f, - 0.413581246099f, 0.418931524369f, 0.424321961148f, 0.4297526421f, 0.435223652615f, 0.440735077813f, - 0.446287002544f, 0.451879511396f, 0.45751268869f, 0.463186618488f, 0.46890138459f, 0.474657070542f, - 0.480453759632f, 0.486291534897f, 0.492170479122f, 0.498090674843f, 0.50405220435f, 0.510055149687f, - 0.516099592656f, 0.522185614816f, 0.528313297489f, 0.534482721758f, 0.54069396847f, 0.546947118241f, - 0.553242251452f, 0.559579448254f, 0.565958788573f, 0.572380352104f, 0.578844218319f, 0.585350466467f, - 0.591899175574f, 0.598490424448f, 0.605124291677f, 0.611800855632f, 0.61852019447f, 0.625282386134f, - 0.632087508355f, 0.638935638652f, 0.645826854338f, 0.652761232515f, 0.659738850081f, 0.66675978373f, - 0.673824109951f, 0.680931905032f, 0.688083245062f, 0.695278205929f, 0.702516863324f, 0.709799292744f, - 0.717125569488f, 0.724495768663f, 0.731909965185f, 0.739368233777f, 0.746870648974f, 0.754417285121f, - 0.762008216379f, 0.76964351672f, 0.777323259932f, 0.785047519623f, 0.792816369214f, 0.800629881949f, - 0.80848813089f, 0.816391188922f, 0.824339128751f, 0.832332022907f, 0.840369943747f, 0.848452963452f, - 0.856581154031f, 0.864754587319f, 0.872973334984f, 0.881237468522f, 0.889547059261f, 0.897902178361f, - 0.906302896816f, 0.914749285456f, 0.923241414944f, 0.931779355781f, 0.940363178305f, 0.948992952695f, - 0.957668748966f, 0.966390636975f, 0.975158686423f -}; - +extern const float srgbToLinearLookupTable[256]; class ColorUtils { public: From 35ea9908cde7aa384fefcb0de36b4dd4ae6dc433 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 28 Jul 2016 22:37:06 -0700 Subject: [PATCH 095/279] Add default skybox --- interface/src/Application.cpp | 32 ++++++++++++++++++- .../src/EntityTreeRenderer.cpp | 1 - 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bd55f32629..5c3daaa9c2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -164,6 +164,13 @@ extern "C" { } #endif +#include +#include + +static model::Skybox* skybox{ new ProceduralSkybox() } ; +static NetworkTexturePointer skyboxTexture; +static bool skyboxTextureLoaded = false; + using namespace std; static QTimer locationUpdateTimer; @@ -1212,6 +1219,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); + auto textureCache = DependencyManager::get(); + skyboxTexture = textureCache->getTexture(QUrl("https://hifi-public.s3.amazonaws.com/images/SkyboxTextures/FullMoon1024Compressed.jpg"), NetworkTexture::CUBE_TEXTURE); + // After all of the constructor is completed, then set firstRun to false. Setting::Handle firstRun{ Settings::firstRun, true }; firstRun.set(false); @@ -4241,6 +4251,7 @@ namespace render { // Fall through: if no skybox is available, render the SKY_DOME case model::SunSkyStage::SKY_DOME: { + /* if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { PerformanceTimer perfTimer("stars"); PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), @@ -4250,6 +4261,25 @@ namespace render { static const float alpha = 1.0f; background->_stars.render(args, alpha); } + */ + + if (!skyboxTextureLoaded && skyboxTexture && skyboxTexture->isLoaded()) { + skybox->setColor({ 1.0, 1.0, 1.0 }); + skyboxTextureLoaded = true; + auto texture = skyboxTexture->getGPUTexture(); + if (texture) { + skybox->setCubemap(texture); + auto scene = DependencyManager::get()->getStage(); + auto sceneKeyLight = scene->getKeyLight(); + sceneKeyLight->setAmbientSphere(texture->getIrradiance()); + sceneKeyLight->setAmbientMap(texture); + } else { + skybox->setCubemap(nullptr); + } + } + if (skyboxTextureLoaded) { + skybox->render(batch, args->getViewFrustum()); + } } break; @@ -4443,7 +4473,6 @@ void Application::updateWindowTitle() const { #endif _window->setWindowTitle(title); } - void Application::clearDomainOctreeDetails() { // if we're about to quit, we really don't need to do any of these things... @@ -4469,6 +4498,7 @@ void Application::clearDomainOctreeDetails() { getEntities()->clear(); auto skyStage = DependencyManager::get()->getSkyStage(); + skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME); _recentlyClearedDomain = true; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 24827ea111..9eadd6d0b9 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -375,7 +375,6 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrsetBackgroundMode(model::SunSkyStage::SKY_DOME); // let the application background through - return; // Early exit } From 5b69ca03f04b6e26fdc3e04aeadc8e49142ccbc4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 4 Aug 2016 10:26:57 -0700 Subject: [PATCH 096/279] Move default skybox out of global and make irradiance gen optional --- interface/src/Application.cpp | 49 +++++++++---------- interface/src/Application.h | 11 +++++ .../src/model-networking/TextureCache.cpp | 12 +++-- .../src/model-networking/TextureCache.h | 2 +- libraries/model/src/model/TextureMap.cpp | 4 ++ libraries/model/src/model/TextureMap.h | 1 + 6 files changed, 48 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5c3daaa9c2..4739fedc26 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -164,13 +164,6 @@ extern "C" { } #endif -#include -#include - -static model::Skybox* skybox{ new ProceduralSkybox() } ; -static NetworkTexturePointer skyboxTexture; -static bool skyboxTextureLoaded = false; - using namespace std; static QTimer locationUpdateTimer; @@ -1219,8 +1212,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); - auto textureCache = DependencyManager::get(); - skyboxTexture = textureCache->getTexture(QUrl("https://hifi-public.s3.amazonaws.com/images/SkyboxTextures/FullMoon1024Compressed.jpg"), NetworkTexture::CUBE_TEXTURE); + auto textureCache = DependencyManager::get(); + + QString skyboxUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.jpg" }; + QString skyboxAmbientUrl { PathUtils::resourcesPath() + "images/Default-Sky-9-ambient.jpg" }; + + _defaultSkyboxTexture = textureCache->getImageTexture(skyboxUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", false } }); + _defaultSkyboxAmbientTexture = textureCache->getImageTexture(skyboxAmbientUrl, NetworkTexture::CUBE_TEXTURE, { { "generateIrradiance", true } }); + + _defaultSkybox->setCubemap(_defaultSkyboxTexture); + _defaultSkybox->setColor({ 1.0, 1.0, 1.0 }); // After all of the constructor is completed, then set firstRun to false. Setting::Handle firstRun{ Settings::firstRun, true }; @@ -4263,23 +4264,19 @@ namespace render { } */ - if (!skyboxTextureLoaded && skyboxTexture && skyboxTexture->isLoaded()) { - skybox->setColor({ 1.0, 1.0, 1.0 }); - skyboxTextureLoaded = true; - auto texture = skyboxTexture->getGPUTexture(); - if (texture) { - skybox->setCubemap(texture); - auto scene = DependencyManager::get()->getStage(); - auto sceneKeyLight = scene->getKeyLight(); - sceneKeyLight->setAmbientSphere(texture->getIrradiance()); - sceneKeyLight->setAmbientMap(texture); - } else { - skybox->setCubemap(nullptr); - } - } - if (skyboxTextureLoaded) { - skybox->render(batch, args->getViewFrustum()); - } + auto scene = DependencyManager::get()->getStage(); + auto sceneKeyLight = scene->getKeyLight(); + scene->setSunModelEnable(false); + sceneKeyLight->setColor(glm::vec3(255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f) * 0.2f); + sceneKeyLight->setIntensity(0.2f); + sceneKeyLight->setAmbientIntensity(3.5f); + sceneKeyLight->setDirection({ 0.0f, 0.0f, -1.0f }); + + auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture(); + sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance()); + sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); + + qApp->getDefaultSkybox()->render(batch, args->getViewFrustum()); } break; diff --git a/interface/src/Application.h b/interface/src/Application.h index c81d56e0aa..8936206790 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -65,6 +65,9 @@ #include "ui/overlays/Overlays.h" #include "UndoStackScriptingInterface.h" +#include +#include + class OffscreenGLCanvas; class GLCanvas; class FaceTracker; @@ -249,6 +252,10 @@ public: float getAvatarSimrate() const { return _avatarSimCounter.rate(); } float getAverageSimsPerSecond() const { return _simCounter.rate(); } + model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; } + gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; } + gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; } + signals: void svoImportRequested(const QString& url); @@ -565,6 +572,10 @@ private: QString _returnFromFullScreenMirrorTo; ConnectionMonitor _connectionMonitor; + + model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ; + gpu::TexturePointer _defaultSkyboxTexture; + gpu::TexturePointer _defaultSkyboxAmbientTexture; }; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 000ca67989..c373da34ba 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -171,7 +171,7 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const } -NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type) { +NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type, const QVariantMap& options = {}) { using Type = NetworkTexture; switch (type) { @@ -188,7 +188,11 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t break; } case Type::CUBE_TEXTURE: { - return model::TextureUsage::createCubeTextureFromImage; + if (options.value("generateIrradiance", true).toBool()) { + return model::TextureUsage::createCubeTextureFromImage; + } else { + return model::TextureUsage::createCubeTextureFromImageWithoutIrradiance; + } break; } case Type::BUMP_TEXTURE: { @@ -225,9 +229,9 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type t } /// Returns a texture version of an image file -gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type) { +gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type, QVariantMap options) { QImage image = QImage(path); - auto loader = getTextureLoaderForType(type); + auto loader = getTextureLoaderForType(type, options); return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString())); } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 0108a3dd6c..66634b6ac0 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -122,7 +122,7 @@ public: const gpu::TexturePointer& getNormalFittingTexture(); /// Returns a texture version of an image file - static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE); + static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE, QVariantMap options = {}); /// Loads a texture from the specified URL. NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE, diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 3e6016d7c0..587aa6e0fa 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -729,3 +729,7 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm gpu::Texture* TextureUsage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { return processCubeTextureColorFromImage(srcImage, srcImageName, false, true, true, true); } + +gpu::Texture* TextureUsage::createCubeTextureFromImageWithoutIrradiance(const QImage& srcImage, const std::string& srcImageName) { + return processCubeTextureColorFromImage(srcImage, srcImageName, false, true, true, false); +} diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index daa4b0d7bb..795b685f27 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -40,6 +40,7 @@ public: static gpu::Texture* createRoughnessTextureFromGlossImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createMetallicTextureFromImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName); + static gpu::Texture* createCubeTextureFromImageWithoutIrradiance(const QImage& image, const std::string& srcImageName); static gpu::Texture* createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName); From c47a5c5aad8e0239f7ff3bd20609d6cb79b24619 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 4 Aug 2016 10:28:06 -0700 Subject: [PATCH 097/279] Add default skybox images --- .../resources/images/Default-Sky-9-ambient.jpg | Bin 0 -> 6223 bytes .../resources/images/Default-Sky-9-cubemap.jpg | Bin 0 -> 403009 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 interface/resources/images/Default-Sky-9-ambient.jpg create mode 100644 interface/resources/images/Default-Sky-9-cubemap.jpg diff --git a/interface/resources/images/Default-Sky-9-ambient.jpg b/interface/resources/images/Default-Sky-9-ambient.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8fb383c5e8d31fdb9aafeadddaa921aa6676c578 GIT binary patch literal 6223 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 Date: Thu, 4 Aug 2016 10:31:06 -0700 Subject: [PATCH 098/279] Add static consts for default skybox values --- interface/src/Application.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4739fedc26..1a11096a7a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4264,13 +4264,18 @@ namespace render { } */ + static const glm::vec3 DEFAULT_SKYBOX_COLOR { 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f }; + static const float DEFAULT_SKYBOX_INTENSITY { 0.2f }; + static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY { 3.5f }; + static const glm::vec3 DEFAULT_SKYBOX_DIRECTION { 0.0f, 0.0f, -1.0f }; + auto scene = DependencyManager::get()->getStage(); auto sceneKeyLight = scene->getKeyLight(); scene->setSunModelEnable(false); - sceneKeyLight->setColor(glm::vec3(255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f) * 0.2f); - sceneKeyLight->setIntensity(0.2f); - sceneKeyLight->setAmbientIntensity(3.5f); - sceneKeyLight->setDirection({ 0.0f, 0.0f, -1.0f }); + sceneKeyLight->setColor(DEFAULT_SKYBOX_COLOR); + sceneKeyLight->setIntensity(DEFAULT_SKYBOX_INTENSITY); + sceneKeyLight->setAmbientIntensity(DEFAULT_SKYBOX_AMBIENT_INTENSITY); + sceneKeyLight->setDirection(DEFAULT_SKYBOX_DIRECTION); auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture(); sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance()); From 9cbfe195cd465d48d2911877ab20c5624e99a1f3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 4 Aug 2016 10:32:05 -0700 Subject: [PATCH 099/279] Adjust commenting out of old stars rendering code --- interface/src/Application.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1a11096a7a..13267f30e7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4252,17 +4252,15 @@ namespace render { // Fall through: if no skybox is available, render the SKY_DOME case model::SunSkyStage::SKY_DOME: { - /* - if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { - PerformanceTimer perfTimer("stars"); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "Application::payloadRender() ... My god, it's full of stars..."); - // should be the first rendering pass - w/o depth buffer / lighting - - static const float alpha = 1.0f; - background->_stars.render(args, alpha); - } - */ +// if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { +// PerformanceTimer perfTimer("stars"); +// PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), +// "Application::payloadRender() ... My god, it's full of stars..."); +// // should be the first rendering pass - w/o depth buffer / lighting +// +// static const float alpha = 1.0f; +// background->_stars.render(args, alpha); +// } static const glm::vec3 DEFAULT_SKYBOX_COLOR { 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f }; static const float DEFAULT_SKYBOX_INTENSITY { 0.2f }; From 36744d72d6c3d7456ded5a7a223725e8eb1e658e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 4 Aug 2016 11:00:21 -0700 Subject: [PATCH 100/279] Fix warning using init list for QVariantMap --- .../model-networking/src/model-networking/TextureCache.cpp | 3 ++- libraries/model-networking/src/model-networking/TextureCache.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c373da34ba..e10be30f54 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -171,7 +171,8 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const } -NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type, const QVariantMap& options = {}) { +NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type, + const QVariantMap& options = QVariantMap()) { using Type = NetworkTexture; switch (type) { diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 66634b6ac0..9c78e7e378 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -122,7 +122,7 @@ public: const gpu::TexturePointer& getNormalFittingTexture(); /// Returns a texture version of an image file - static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE, QVariantMap options = {}); + static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE, QVariantMap options = QVariantMap()); /// Loads a texture from the specified URL. NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE, From eaa77edc25b251a2e0bd708b4df43c6613f37310 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 08:59:57 -0700 Subject: [PATCH 101/279] Replace 'Stars' menu option with 'Default Skybox' --- interface/src/Application.cpp | 47 ++++++++++++++--------------------- interface/src/Menu.cpp | 2 +- interface/src/Menu.h | 2 +- 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 13267f30e7..209ef8d02f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -139,7 +139,6 @@ #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif -#include "Stars.h" #include "ui/AddressBarDialog.h" #include "ui/AvatarInputs.h" #include "ui/DialogsManager.h" @@ -2291,7 +2290,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } case Qt::Key_Asterisk: - Menu::getInstance()->triggerOption(MenuOption::Stars); + Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox); break; case Qt::Key_S: @@ -4216,8 +4215,6 @@ public: typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - Stars _stars; - static render::ItemID _item; // unique WorldBoxRenderData }; @@ -4252,34 +4249,26 @@ namespace render { // Fall through: if no skybox is available, render the SKY_DOME case model::SunSkyStage::SKY_DOME: { -// if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { -// PerformanceTimer perfTimer("stars"); -// PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), -// "Application::payloadRender() ... My god, it's full of stars..."); -// // should be the first rendering pass - w/o depth buffer / lighting -// -// static const float alpha = 1.0f; -// background->_stars.render(args, alpha); -// } + if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) { + static const glm::vec3 DEFAULT_SKYBOX_COLOR { 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f }; + static const float DEFAULT_SKYBOX_INTENSITY { 0.2f }; + static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY { 3.5f }; + static const glm::vec3 DEFAULT_SKYBOX_DIRECTION { 0.0f, 0.0f, -1.0f }; - static const glm::vec3 DEFAULT_SKYBOX_COLOR { 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f }; - static const float DEFAULT_SKYBOX_INTENSITY { 0.2f }; - static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY { 3.5f }; - static const glm::vec3 DEFAULT_SKYBOX_DIRECTION { 0.0f, 0.0f, -1.0f }; + auto scene = DependencyManager::get()->getStage(); + auto sceneKeyLight = scene->getKeyLight(); + scene->setSunModelEnable(false); + sceneKeyLight->setColor(DEFAULT_SKYBOX_COLOR); + sceneKeyLight->setIntensity(DEFAULT_SKYBOX_INTENSITY); + sceneKeyLight->setAmbientIntensity(DEFAULT_SKYBOX_AMBIENT_INTENSITY); + sceneKeyLight->setDirection(DEFAULT_SKYBOX_DIRECTION); - auto scene = DependencyManager::get()->getStage(); - auto sceneKeyLight = scene->getKeyLight(); - scene->setSunModelEnable(false); - sceneKeyLight->setColor(DEFAULT_SKYBOX_COLOR); - sceneKeyLight->setIntensity(DEFAULT_SKYBOX_INTENSITY); - sceneKeyLight->setAmbientIntensity(DEFAULT_SKYBOX_AMBIENT_INTENSITY); - sceneKeyLight->setDirection(DEFAULT_SKYBOX_DIRECTION); + auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture(); + sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance()); + sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); - auto defaultSkyboxAmbientTexture = qApp->getDefaultSkyboxAmbientTexture(); - sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance()); - sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); - - qApp->getDefaultSkybox()->render(batch, args->getViewFrustum()); + qApp->getDefaultSkybox()->render(batch, args->getViewFrustum()); + } } break; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6308ac6c73..a8340e8f47 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -337,7 +337,7 @@ Menu::Menu() { // Developer > Render >>> MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, true); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DefaultSkybox, 0, true); // Developer > Render > Throttle FPS If Not Focus addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ThrottleFPSIfNotFocus, 0, true); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 503cbf51fa..d47b6842a5 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -80,6 +80,7 @@ namespace MenuOption { const QString CrashNewFaultThreaded = "New Fault (threaded)"; const QString DeadlockInterface = "Deadlock Interface"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; + const QString DefaultSkybox = "Default Skybox"; const QString DeleteBookmark = "Delete Bookmark..."; const QString DisableActivityLogger = "Disable Activity Logger"; const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; @@ -175,7 +176,6 @@ namespace MenuOption { const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; const QString SimulateEyeTracking = "Simulate"; const QString SMIEyeTracking = "SMI Eye Tracking"; - const QString Stars = "Stars"; const QString Stats = "Stats"; const QString StopAllScripts = "Stop All Scripts"; const QString SuppressShortTimings = "Suppress Timings Less than 10ms"; From 6854f9fda4c3358ce56eeb7adaca388732b68ba1 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 09:02:23 -0700 Subject: [PATCH 102/279] Remove stars shaders and renderable --- interface/src/Stars.cpp | 216 ----------------------- interface/src/Stars.h | 49 ----- libraries/render-utils/src/stars.slf | 34 ---- libraries/render-utils/src/stars.slv | 36 ---- libraries/render-utils/src/starsGrid.slf | 63 ------- tests/shaders/src/main.cpp | 7 - 6 files changed, 405 deletions(-) delete mode 100644 interface/src/Stars.cpp delete mode 100644 interface/src/Stars.h delete mode 100644 libraries/render-utils/src/stars.slf delete mode 100644 libraries/render-utils/src/stars.slv delete mode 100644 libraries/render-utils/src/starsGrid.slf diff --git a/interface/src/Stars.cpp b/interface/src/Stars.cpp deleted file mode 100644 index 9510710eb3..0000000000 --- a/interface/src/Stars.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// -// Stars.cpp -// interface/src -// -// Created by Tobias Schwinger on 3/22/13. -// Copyright 2013 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 "Stars.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -//static const float TILT = 0.23f; -static const float TILT = 0.0f; -static const unsigned int STARFIELD_NUM_STARS = 50000; -static const unsigned int STARFIELD_SEED = 1; -static const float STAR_COLORIZATION = 0.1f; - -static const float TAU = 6.28318530717958f; -//static const float HALF_TAU = TAU / 2.0f; -//static const float QUARTER_TAU = TAU / 4.0f; -//static const float MILKY_WAY_WIDTH = TAU / 30.0f; // width in radians of one half of the Milky Way -//static const float MILKY_WAY_INCLINATION = 0.0f; // angle of Milky Way from horizontal in degrees -//static const float MILKY_WAY_RATIO = 0.4f; -static const char* UNIFORM_TIME_NAME = "iGlobalTime"; - -// Produce a random float value between 0 and 1 -static float frand() { - return (float)rand() / (float)RAND_MAX; -} - -// http://mathworld.wolfram.com/SpherePointPicking.html -static vec2 randPolar() { - vec2 result(frand(), frand()); - result.x *= TAU; - result.y = powf(result.y, 2.0) / 2.0f; - if (frand() > 0.5f) { - result.y = 0.5f - result.y; - } else { - result.y += 0.5f; - } - result.y = acos((2.0f * result.y) - 1.0f); - return result; -} - - -static vec3 fromPolar(const vec2& polar) { - float sinTheta = sin(polar.x); - float cosTheta = cos(polar.x); - float sinPhi = sin(polar.y); - float cosPhi = cos(polar.y); - return vec3( - cosTheta * sinPhi, - cosPhi, - sinTheta * sinPhi); -} - - -// computeStarColor -// - Generate a star color. -// -// colorization can be a value between 0 and 1 specifying how colorful the resulting star color is. -// -// 0 = completely black & white -// 1 = very colorful -unsigned computeStarColor(float colorization) { - unsigned char red, green, blue; - if (randFloat() < 0.3f) { - // A few stars are colorful - red = 2 + (rand() % 254); - green = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization)); - blue = 2 + round((red * (1 - colorization)) + ((rand() % 254) * colorization)); - } else { - // Most stars are dimmer and white - red = green = blue = 2 + (rand() % 128); - } - return red | (green << 8) | (blue << 16); -} - -struct StarVertex { - vec4 position; - vec4 colorAndSize; -}; - -static const int STARS_VERTICES_SLOT{ 0 }; -static const int STARS_COLOR_SLOT{ 1 }; - -gpu::PipelinePointer Stars::_gridPipeline{}; -gpu::PipelinePointer Stars::_starsPipeline{}; -int32_t Stars::_timeSlot{ -1 }; - -void Stars::init() { - if (!_gridPipeline) { - auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)); - auto ps = gpu::Shader::createPixel(std::string(starsGrid_frag)); - auto program = gpu::Shader::createProgram(vs, ps); - gpu::Shader::makeProgram((*program)); - _timeSlot = program->getBuffers().findLocation(UNIFORM_TIME_NAME); - if (_timeSlot == gpu::Shader::INVALID_LOCATION) { - _timeSlot = program->getUniforms().findLocation(UNIFORM_TIME_NAME); - } - auto state = gpu::StatePointer(new gpu::State()); - // enable decal blend - state->setDepthTest(gpu::State::DepthTest(false)); - state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - _gridPipeline = gpu::Pipeline::create(program, state); - } - - if (!_starsPipeline) { - auto vs = gpu::Shader::createVertex(std::string(stars_vert)); - auto ps = gpu::Shader::createPixel(std::string(stars_frag)); - auto program = gpu::Shader::createProgram(vs, ps); - gpu::Shader::makeProgram((*program)); - auto state = gpu::StatePointer(new gpu::State()); - // enable decal blend - state->setDepthTest(gpu::State::DepthTest(false)); - state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); - state->setAntialiasedLineEnable(true); // line smoothing also smooth points - state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); - _starsPipeline = gpu::Pipeline::create(program, state); - } - - unsigned limit = STARFIELD_NUM_STARS; - std::vector points; - points.resize(limit); - - { // generate stars - QElapsedTimer startTime; - startTime.start(); - - vertexBuffer.reset(new gpu::Buffer); - - srand(STARFIELD_SEED); - for (size_t star = 0; star < limit; ++star) { - points[star].position = vec4(fromPolar(randPolar()), 1); - float size = frand() * 2.5f + 0.5f; - if (frand() < STAR_COLORIZATION) { - vec3 color(frand() / 2.0f + 0.5f, frand() / 2.0f + 0.5f, frand() / 2.0f + 0.5f); - points[star].colorAndSize = vec4(color, size); - } else { - vec3 color(frand() / 2.0f + 0.5f); - points[star].colorAndSize = vec4(color, size); - } - } - - double timeDiff = (double)startTime.nsecsElapsed() / 1000000.0; // ns to ms - qDebug() << "Total time to generate stars: " << timeDiff << " msec"; - } - - gpu::Element positionElement, colorElement; - const size_t VERTEX_STRIDE = sizeof(StarVertex); - - vertexBuffer->append(VERTEX_STRIDE * limit, (const gpu::Byte*)&points[0]); - streamFormat.reset(new gpu::Stream::Format()); // 1 for everyone - streamFormat->setAttribute(gpu::Stream::POSITION, STARS_VERTICES_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::XYZW), 0); - streamFormat->setAttribute(gpu::Stream::COLOR, STARS_COLOR_SLOT, gpu::Element(gpu::VEC4, gpu::FLOAT, gpu::RGBA)); - positionElement = streamFormat->getAttributes().at(gpu::Stream::POSITION)._element; - colorElement = streamFormat->getAttributes().at(gpu::Stream::COLOR)._element; - - size_t offset = offsetof(StarVertex, position); - positionView = gpu::BufferView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, positionElement); - - offset = offsetof(StarVertex, colorAndSize); - colorView = gpu::BufferView(vertexBuffer, offset, vertexBuffer->getSize(), VERTEX_STRIDE, colorElement); -} - -// FIXME star colors -void Stars::render(RenderArgs* renderArgs, float alpha) { - std::call_once(once, [&]{ init(); }); - - - auto modelCache = DependencyManager::get(); - auto textureCache = DependencyManager::get(); - auto geometryCache = DependencyManager::get(); - - - gpu::Batch& batch = *renderArgs->_batch; - batch.setViewTransform(Transform()); - batch.setProjectionTransform(renderArgs->getViewFrustum().getProjection()); - batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->getViewFrustum().getOrientation()) * - quat(vec3(TILT, 0, 0)))); - batch.setResourceTexture(0, textureCache->getWhiteTexture()); - - // Render the world lines - batch.setPipeline(_gridPipeline); - static auto start = usecTimestampNow(); - float msecs = (float)(usecTimestampNow() - start) / (float)USECS_PER_MSEC; - float secs = msecs / (float)MSECS_PER_SECOND; - batch._glUniform1f(_timeSlot, secs); - geometryCache->renderCube(batch); - - // Render the stars - batch.setPipeline(_starsPipeline); - batch.setInputFormat(streamFormat); - batch.setInputBuffer(STARS_VERTICES_SLOT, positionView); - batch.setInputBuffer(STARS_COLOR_SLOT, colorView); - batch.draw(gpu::Primitive::POINTS, STARFIELD_NUM_STARS); -} diff --git a/interface/src/Stars.h b/interface/src/Stars.h deleted file mode 100644 index f07caff770..0000000000 --- a/interface/src/Stars.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// Stars.h -// interface/src -// -// Created by Tobias Schwinger on 3/22/13. -// Copyright 2013 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_Stars_h -#define hifi_Stars_h - -#include - -class RenderArgs; - -// Starfield rendering component. -class Stars { -public: - Stars() = default; - ~Stars() = default; - - Stars(Stars const&) = delete; - Stars& operator=(Stars const&) = delete; - - // Renders the starfield from a local viewer's perspective. - // The parameters specifiy the field of view. - void render(RenderArgs* args, float alpha); - -private: - // Pipelines - static gpu::PipelinePointer _gridPipeline; - static gpu::PipelinePointer _starsPipeline; - static int32_t _timeSlot; - - // Buffers - gpu::BufferPointer vertexBuffer; - gpu::Stream::FormatPointer streamFormat; - gpu::BufferView positionView; - gpu::BufferView colorView; - std::once_flag once; - - void init(); -}; - - -#endif // hifi_Stars_h diff --git a/libraries/render-utils/src/stars.slf b/libraries/render-utils/src/stars.slf deleted file mode 100644 index 14191f2a6a..0000000000 --- a/libraries/render-utils/src/stars.slf +++ /dev/null @@ -1,34 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// fragment shader -// -// Created by Bradley Austin Davis on 6/10/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -in vec4 varColor; -in float varSize; - -out vec4 outFragColor; - -const float EDGE_SIZE = 0.25; -const float ALPHA_BOUNDARY = 1.0 - EDGE_SIZE; - -void main(void) { - vec2 coord = gl_PointCoord * vec2(2.0) - vec2(1.0); - coord = coord * coord; - - float l = coord.x + coord.y; - if (l > 1.0) { - discard; - } - - outFragColor = varColor; - if (l >= ALPHA_BOUNDARY) { - outFragColor.a = smoothstep(1.0, ALPHA_BOUNDARY, l); - } -} diff --git a/libraries/render-utils/src/stars.slv b/libraries/render-utils/src/stars.slv deleted file mode 100644 index d00bb8b7dd..0000000000 --- a/libraries/render-utils/src/stars.slv +++ /dev/null @@ -1,36 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// standardTransformPNTC.slv -// vertex shader -// -// Created by Sam Gateau on 6/10/2015. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -<@include gpu/Inputs.slh@> -<@include gpu/Color.slh@> -<@include gpu/Transform.slh@> -<$declareStandardTransform()$> - -// TODO we need to get the viewport resolution and FOV passed to us so we can modify the point size -// and effectively producing a points that take up a constant angular size regardless of the display resolution -// or projection matrix - -out vec4 varColor; -out float varSize; - -void main(void) { - varColor = colorToLinearRGBA(inColor); - - // standard transform - TransformCamera cam = getTransformCamera(); - TransformObject obj = getTransformObject(); - <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> - varSize = inColor.a; - gl_PointSize = varSize; -} \ No newline at end of file diff --git a/libraries/render-utils/src/starsGrid.slf b/libraries/render-utils/src/starsGrid.slf deleted file mode 100644 index ad9a45a4f5..0000000000 --- a/libraries/render-utils/src/starsGrid.slf +++ /dev/null @@ -1,63 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// stars.frag -// fragment shader -// -// Created by Bradley Austin Davis on 2015/06/19 - -in vec2 varTexcoord; -in vec3 varNomral; -in vec3 varPosition; - -uniform float iGlobalTime; - -const float PI = 3.14159; -const float TAU = 3.14159 * 2.0; -const int latitudeCount = 5; -const float latitudeDist = PI / 2.0 / float(latitudeCount); -const int meridianCount = 4; -const float merdianDist = PI / float(meridianCount); - -out vec4 outFragColor; - -float clampLine(float val, float target) { - return clamp((1.0 - abs((val - target)) - 0.998) * 500.0, 0.0, 1.0); -} - -float latitude(vec2 pos, float angle) { - float result = clampLine(pos.y, angle); - if (angle != 0.0) { - result += clampLine(pos.y, -angle); - } - return result; -} - -float meridian(vec2 pos, float angle) { - return clampLine(pos.x, angle) + clampLine(pos.x + PI, angle); -} - -vec2 toPolar(in vec3 dir) { - vec2 polar = vec2(atan(dir.z, dir.x), asin(dir.y)); - return polar; -} - -void mainVR( out vec4 fragColor, in vec2 fragCoord, in vec3 fragRayOri, in vec3 fragRayDir ) -{ - vec2 polar = toPolar(fragRayDir); - //polar.x += mod(iGlobalTime / 12.0, PI / 4.0) - PI / 4.0; - float c = 0.0; - for (int i = 0; i < latitudeCount - 1; ++i) { - c += latitude(polar, float(i) * latitudeDist); - } - for (int i = 0; i < meridianCount; ++i) { - c += meridian(polar, float(i) * merdianDist); - } - const vec3 col_lines = vec3(102.0 / 255.0, 136.0 / 255.0, 221.0 / 255.0); - fragColor = vec4(c * col_lines, 0.2); -} - -void main(void) { - mainVR(outFragColor, gl_FragCoord.xy, vec3(0.0), normalize(varPosition)); -} - diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index 2ef7bbdd02..9c4a835966 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -75,10 +75,6 @@ #include #include -#include -#include -#include - #include #include #include @@ -168,9 +164,6 @@ void QTestWindow::draw() { testShaderBuild(deferred_light_limited_vert, spot_light_frag); testShaderBuild(standardTransformPNTC_vert, standardDrawTexture_frag); testShaderBuild(standardTransformPNTC_vert, DrawTextureOpaque_frag); - - testShaderBuild(standardTransformPNTC_vert, starsGrid_frag); - testShaderBuild(stars_vert, stars_frag); testShaderBuild(model_vert, model_frag); testShaderBuild(model_normal_map_vert, model_normal_map_frag); From 8ee3f680c91b1b3e4324bc92b1a59ac18fbe7971 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 8 Aug 2016 14:08:49 -0700 Subject: [PATCH 103/279] Change default skybox ambient intensity from 3.5 to 2.0 --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 209ef8d02f..dfebb6bcb2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4252,7 +4252,7 @@ namespace render { if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) { static const glm::vec3 DEFAULT_SKYBOX_COLOR { 255.0f / 255.0f, 220.0f / 255.0f, 194.0f / 255.0f }; static const float DEFAULT_SKYBOX_INTENSITY { 0.2f }; - static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY { 3.5f }; + static const float DEFAULT_SKYBOX_AMBIENT_INTENSITY { 2.0f }; static const glm::vec3 DEFAULT_SKYBOX_DIRECTION { 0.0f, 0.0f, -1.0f }; auto scene = DependencyManager::get()->getStage(); From 3df8aa0dba370f9c0f11c3ad6099e91dcc8c6956 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Mon, 8 Aug 2016 17:01:32 -0700 Subject: [PATCH 104/279] Suppress repeated ice server connection messages --- domain-server/src/DomainServer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 6c4b12d4c0..d5f23f7c4e 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1233,7 +1233,10 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() { callbackParameters.errorCallbackReceiver = this; callbackParameters.errorCallbackMethod = "handleFailedICEServerAddressUpdate"; - qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" << _iceServerSocket.getAddress().toString(); + static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex + ("Updating ice-server address in High Fidelity Metaverse API to [^ \n]+"); + qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" + << _iceServerSocket.getAddress().toString(); static const QString DOMAIN_ICE_ADDRESS_UPDATE = "/api/v1/domains/%1/ice_server_address"; From ae9fb3768c6154f419e739136875af6245698687 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 8 Aug 2016 17:45:25 -0700 Subject: [PATCH 105/279] CR notes --- libraries/render-utils/src/GeometryCache.cpp | 37 +++++++++---------- .../render-utils/src/MeshPartPayload.cpp | 4 ++ libraries/render-utils/src/MeshPartPayload.h | 3 +- libraries/render-utils/src/Model.cpp | 1 - 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index dcd36946cb..f906a871fe 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -406,29 +406,28 @@ GeometryCache::GeometryCache() : _nextID(0) { buildShapes(); GeometryCache::_simpleOpaquePipeline = - std::make_shared(getSimplePipeline(false, false, true, false), nullptr, - [](const render::ShapePipeline&, gpu::Batch& batch) { - // Set the defaults needed for a simple program - batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, - DependencyManager::get()->getWhiteTexture()); - batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, - DependencyManager::get()->getNormalFittingTexture()); - } - ); + std::make_shared(getSimplePipeline(false, false, true, false), nullptr, + [](const render::ShapePipeline&, gpu::Batch& batch) { + // Set the defaults needed for a simple program + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, + DependencyManager::get()->getWhiteTexture()); + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, + DependencyManager::get()->getNormalFittingTexture()); + } + ); GeometryCache::_simpleTransparentPipeline = std::make_shared(getSimplePipeline(false, true, true, false), nullptr, - [](const render::ShapePipeline&, gpu::Batch& batch) { - // Set the defaults needed for a simple program - batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, - DependencyManager::get()->getWhiteTexture()); - batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, - DependencyManager::get()->getNormalFittingTexture()); - } - ); + [](const render::ShapePipeline&, gpu::Batch& batch) { + // Set the defaults needed for a simple program + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::ALBEDO, + DependencyManager::get()->getWhiteTexture()); + batch.setResourceTexture(render::ShapePipeline::Slot::MAP::NORMAL_FITTING, + DependencyManager::get()->getNormalFittingTexture()); + } + ); GeometryCache::_simpleWirePipeline = std::make_shared(getSimplePipeline(false, false, true, true), nullptr, - [](const render::ShapePipeline&, gpu::Batch& batch) {} - ); + [](const render::ShapePipeline&, gpu::Batch& batch) {}); } GeometryCache::~GeometryCache() { diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index c2c27fb298..2fa31022ec 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -516,6 +516,10 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: batch.setModelTransform(transform); } +void ModelMeshPartPayload::startFade() { + _fadeStartTime = usecTimestampNow(); + _hasStartedFade = true; +} void ModelMeshPartPayload::render(RenderArgs* args) const { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index c2305f3741..8fdafee9b0 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -84,9 +84,8 @@ public: void updateTransformForSkinnedMesh(const Transform& transform, const Transform& offsetTransform, const QVector& clusterMatrices); // Entity fade in - void startFade() { _fadeStartTime = usecTimestampNow(); } + void startFade(); bool hasStartedFade() { return _hasStartedFade; } - void setHasStartedFade(bool hasStartedFade) { _hasStartedFade = hasStartedFade; } bool isStillFading() const { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } // Render Item interface diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 9d158810e7..2c7e9485fb 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -214,7 +214,6 @@ void Model::updateRenderItems() { pendingChanges.updateItem(itemID, [modelTransform, modelMeshOffset, deleteGeometryCounter](ModelMeshPartPayload& data) { if (!data.hasStartedFade() && data._model && data._model->isLoaded() && data._model->getGeometry()->areTexturesLoaded()) { data.startFade(); - data.setHasStartedFade(true); } // Ensure the model geometry was not reset between frames if (data._model && data._model->isLoaded() && deleteGeometryCounter == data._model->_deleteGeometryCounter) { From 25b60d0c62026d5fcf8f97f33f76ca40aaa7302a Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 9 Aug 2016 09:46:58 -0700 Subject: [PATCH 106/279] CR feedback --- interface/resources/qml/LoginDialogSave.qml | 197 ------------------ interface/src/ui/LoginDialog.cpp | 19 +- interface/src/ui/LoginDialog.h | 7 +- .../src/steamworks-wrapper/SteamClient.cpp | 3 +- 4 files changed, 7 insertions(+), 219 deletions(-) delete mode 100644 interface/resources/qml/LoginDialogSave.qml diff --git a/interface/resources/qml/LoginDialogSave.qml b/interface/resources/qml/LoginDialogSave.qml deleted file mode 100644 index 46246fc1a5..0000000000 --- a/interface/resources/qml/LoginDialogSave.qml +++ /dev/null @@ -1,197 +0,0 @@ -Window { - id: root - HifiConstants { id: hifi } - - width: 550 - height: 200 - - anchors.centerIn: parent - resizable: true - - property bool required: false - - Component { - id: welcomeBody - - Column { - anchors.centerIn: parent - - OverlayTitle { - anchors.horizontalCenter: parent.horizontalCenter - - text: "Welcomeback Atlante45!" - color: hifi.colors.baseGrayHighlight - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } - - VerticalSpacer {} - - HorizontalRule {} - - MenuItem { - id: details - anchors.horizontalCenter: parent.horizontalCenter - - text: "You are now signed into High Fidelity" - color: hifi.colors.baseGrayHighlight - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } - - VerticalSpacer {} - - Button { - anchors.horizontalCenter: parent.horizontalCenter - - text: "Close" - } - } - } - - Component { - id: signInBody - - Column { - anchors.centerIn: parent - - OverlayTitle { - anchors.horizontalCenter: parent.horizontalCenter - - text: required ? "Sign In Required" : "Sign In" - color: hifi.colors.baseGrayHighlight - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } - - VerticalSpacer {} - - HorizontalRule {} - - MenuItem { - id: details - anchors.horizontalCenter: parent.horizontalCenter - - text: required ? "This domain's owner requires that you sign in:" - : "Sign in to access your user account:" - color: hifi.colors.baseGrayHighlight - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } - - VerticalSpacer {} - - Row { - anchors.horizontalCenter: parent.horizontalCenter - - Button { - anchors.verticalCenter: parent.verticalCenter - width: undefined // invalidate so that the image's size sets the width - height: undefined // invalidate so that the image's size sets the height - - style: Original.ButtonStyle { - background: Image { - id: buttonImage - source: "../images/steam-sign-in.png" - } - } - - onClicked: body.sourceComponent = completeProfileBody - } - - HorizontalSpacer {} - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: "Cancel" - - onClicked: required = !required - } - } - } - } - - Component { - id: completeProfileBody - - Column { - anchors.centerIn: parent - - Row { - anchors.horizontalCenter: parent.horizontalCenter - - HiFiGlyphs { - anchors.verticalCenter: parent.verticalCenter - - text: hifi.glyphs.avatar - } - - OverlayTitle { - anchors.verticalCenter: parent.verticalCenter - - text: "Complete Your Profile" - color: hifi.colors.baseGrayHighlight - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } - } - - VerticalSpacer {} - - HorizontalRule {} - - VerticalSpacer {} - - Row { - anchors.horizontalCenter: parent.horizontalCenter - - Button { - anchors.verticalCenter: parent.verticalCenter - - width: 200 - - text: "Create your profile" - color: hifi.buttons.blue - - onClicked: body.sourceComponent = welcomeBody - } - - HorizontalSpacer {} - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: "Cancel" - - onClicked: body.sourceComponent = signInBody - - } - } - - VerticalSpacer {} - - ShortcutText { - text: "Already have a High Fidelity profile? Link to an existing profile here." - - color: hifi.colors.blueAccent - font.underline: true - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } - } - } - - Rectangle { - anchors.fill: root - color: hifi.colors.faintGray - radius: hifi.dimensions.borderRadius - - Loader { - id: body - anchors.centerIn: parent - sourceComponent: signInBody - } - } -} diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 78aacb1216..08e2b6479d 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -54,11 +54,11 @@ void LoginDialog::toggleAction() { } } -bool LoginDialog::isSteamRunning() { +bool LoginDialog::isSteamRunning() const { return SteamClient::isRunning(); } -void LoginDialog::login(const QString& username, const QString& password) { +void LoginDialog::login(const QString& username, const QString& password) const { qDebug() << "Attempting to login " << username; DependencyManager::get()->requestAccessToken(username, password); } @@ -131,25 +131,12 @@ void LoginDialog::createAccountFromStream(QString username) { } -void LoginDialog::openUrl(const QString& url) { +void LoginDialog::openUrl(const QString& url) const { auto offscreenUi = DependencyManager::get(); auto browser = offscreenUi->load("Browser.qml"); browser->setProperty("url", url); } -void LoginDialog::sendRecoveryEmail(const QString& email) { - const QString PASSWORD_RESET_PATH = "/users/password"; - - QJsonObject payload; - payload.insert("user_email", QJsonValue::fromVariant(QVariant(email))); - - - auto accountManager = DependencyManager::get(); - accountManager->sendRequest(PASSWORD_RESET_PATH, AccountManagerAuth::None, - QNetworkAccessManager::PostOperation, JSONCallbackParameters(), - QJsonDocument(payload).toJson()); -} - void LoginDialog::linkCompleted(QNetworkReply& reply) { emit handleLinkCompleted(); } diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h index ef2c937201..8b6dc40302 100644 --- a/interface/src/ui/LoginDialog.h +++ b/interface/src/ui/LoginDialog.h @@ -45,15 +45,14 @@ public slots: void createFailed(QNetworkReply& reply); protected slots: - Q_INVOKABLE bool isSteamRunning(); + Q_INVOKABLE bool isSteamRunning() const; - Q_INVOKABLE void login(const QString& username, const QString& password); + Q_INVOKABLE void login(const QString& username, const QString& password) const; Q_INVOKABLE void loginThroughSteam(); Q_INVOKABLE void linkSteam(); Q_INVOKABLE void createAccountFromStream(QString username = QString()); - Q_INVOKABLE void openUrl(const QString& url); - Q_INVOKABLE void sendRecoveryEmail(const QString& email); + Q_INVOKABLE void openUrl(const QString& url) const; }; diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp index f6cf13effd..2e8f6bd7b3 100644 --- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp +++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp @@ -301,8 +301,7 @@ void SteamClient::joinLobby(QString lobbyIdStr) { if (!initialized) { if (SteamAPI_IsSteamRunning()) { init(); - } - else { + } else { qWarning() << "Steam is not running"; return; } From 51fb52977e039af704dfc74b7faf653ed80a6d09 Mon Sep 17 00:00:00 2001 From: Zander Otavka Date: Tue, 9 Aug 2016 11:46:08 -0700 Subject: [PATCH 107/279] Generalize atp handling in network access managers --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 18 +++++++++- .../src/{QmlAtpReply.cpp => AtpReply.cpp} | 18 +++++----- .../src/{QmlAtpReply.h => AtpReply.h} | 14 ++++---- .../networking/src/NetworkAccessManager.cpp | 13 +++++++- .../networking/src/NetworkAccessManager.h | 9 +++-- .../src/OAuthNetworkAccessManager.h | 5 +-- .../src/QmlNetworkAccessManager.cpp | 29 ---------------- .../networking/src/QmlNetworkAccessManager.h | 33 ------------------- 8 files changed, 55 insertions(+), 84 deletions(-) rename libraries/networking/src/{QmlAtpReply.cpp => AtpReply.cpp} (86%) rename libraries/networking/src/{QmlAtpReply.h => AtpReply.h} (76%) delete mode 100644 libraries/networking/src/QmlNetworkAccessManager.cpp delete mode 100644 libraries/networking/src/QmlNetworkAccessManager.h diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index ebccc8a1fc..d813e002c7 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include "OffscreenGLCanvas.h" #include "GLEscrow.h" @@ -56,6 +56,22 @@ private: friend class OffscreenQmlSurface; }; +class QmlNetworkAccessManager : public NetworkAccessManager { +public: + friend class QmlNetworkAccessManagerFactory; +protected: + QmlNetworkAccessManager(QObject* parent) : NetworkAccessManager(parent) { } +}; + +class QmlNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory { +public: + QNetworkAccessManager* create(QObject* parent); +}; + +QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) { + return new QmlNetworkAccessManager(parent); +} + Q_DECLARE_LOGGING_CATEGORY(offscreenFocus) Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") diff --git a/libraries/networking/src/QmlAtpReply.cpp b/libraries/networking/src/AtpReply.cpp similarity index 86% rename from libraries/networking/src/QmlAtpReply.cpp rename to libraries/networking/src/AtpReply.cpp index a2e537ba1f..4440995ee0 100644 --- a/libraries/networking/src/QmlAtpReply.cpp +++ b/libraries/networking/src/AtpReply.cpp @@ -1,5 +1,5 @@ // -// QmlAtpReply.cpp +// AtpReply.cpp // libraries/networking/src // // Created by Zander Otavka on 8/4/16. @@ -10,30 +10,30 @@ // #include "ResourceManager.h" -#include "QmlAtpReply.h" +#include "AtpReply.h" -QmlAtpReply::QmlAtpReply(const QUrl& url, QObject* parent) : +AtpReply::AtpReply(const QUrl& url, QObject* parent) : _resourceRequest(ResourceManager::createResourceRequest(parent, url)) { setOperation(QNetworkAccessManager::GetOperation); - connect(_resourceRequest, &AssetResourceRequest::progress, this, &QmlAtpReply::downloadProgress); - connect(_resourceRequest, &AssetResourceRequest::finished, this, &QmlAtpReply::handleRequestFinish); + connect(_resourceRequest, &AssetResourceRequest::progress, this, &AtpReply::downloadProgress); + connect(_resourceRequest, &AssetResourceRequest::finished, this, &AtpReply::handleRequestFinish); _resourceRequest->send(); } -QmlAtpReply::~QmlAtpReply() { +AtpReply::~AtpReply() { if (_resourceRequest) { _resourceRequest->deleteLater(); _resourceRequest = nullptr; } } -qint64 QmlAtpReply::bytesAvailable() const { +qint64 AtpReply::bytesAvailable() const { return _content.size() - _readOffset + QIODevice::bytesAvailable(); } -qint64 QmlAtpReply::readData(char* data, qint64 maxSize) { +qint64 AtpReply::readData(char* data, qint64 maxSize) { if (_readOffset < _content.size()) { qint64 readSize = qMin(maxSize, _content.size() - _readOffset); memcpy(data, _content.constData() + _readOffset, readSize); @@ -44,7 +44,7 @@ qint64 QmlAtpReply::readData(char* data, qint64 maxSize) { } } -void QmlAtpReply::handleRequestFinish() { +void AtpReply::handleRequestFinish() { Q_ASSERT(_resourceRequest->getState() == ResourceRequest::State::Finished); switch (_resourceRequest->getResult()) { diff --git a/libraries/networking/src/QmlAtpReply.h b/libraries/networking/src/AtpReply.h similarity index 76% rename from libraries/networking/src/QmlAtpReply.h rename to libraries/networking/src/AtpReply.h index a8f6dfde14..6ed5dd8fb8 100644 --- a/libraries/networking/src/QmlAtpReply.h +++ b/libraries/networking/src/AtpReply.h @@ -1,5 +1,5 @@ // -// QmlAtpReply.h +// AtpReply.h // libraries/networking/src // // Created by Zander Otavka on 8/4/16. @@ -9,19 +9,19 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_QmlAtpReply_h -#define hifi_QmlAtpReply_h +#ifndef hifi_AtpReply_h +#define hifi_AtpReply_h #include #include #include "AssetResourceRequest.h" -class QmlAtpReply : public QNetworkReply { +class AtpReply : public QNetworkReply { Q_OBJECT public: - QmlAtpReply(const QUrl& url, QObject* parent = Q_NULLPTR); - ~QmlAtpReply(); + AtpReply(const QUrl& url, QObject* parent = Q_NULLPTR); + ~AtpReply(); qint64 bytesAvailable() const override; void abort() override { } bool isSequential() const override { return true; } @@ -37,4 +37,4 @@ private: qint64 _readOffset { 0 }; }; -#endif // hifi_QmlAtpReply_h +#endif // hifi_AtpReply_h diff --git a/libraries/networking/src/NetworkAccessManager.cpp b/libraries/networking/src/NetworkAccessManager.cpp index 97171d5ad7..78a05677fd 100644 --- a/libraries/networking/src/NetworkAccessManager.cpp +++ b/libraries/networking/src/NetworkAccessManager.cpp @@ -1,6 +1,6 @@ // // NetworkAccessManager.cpp -// +// libraries/networking/src // // Created by Clement on 7/1/14. // Copyright 2014 High Fidelity, Inc. @@ -11,6 +11,7 @@ #include +#include "AtpReply.h" #include "NetworkAccessManager.h" QThreadStorage networkAccessManagers; @@ -23,3 +24,13 @@ QNetworkAccessManager& NetworkAccessManager::getInstance() { return *networkAccessManagers.localData(); } + +QNetworkReply* NetworkAccessManager::createRequest(Operation operation, const QNetworkRequest& request, QIODevice* device) { + if (request.url().scheme() == "atp" && operation == GetOperation) { + return new AtpReply(request.url()); + //auto url = request.url().toString(); + //return QNetworkAccessManager::createRequest(operation, request, device); + } else { + return QNetworkAccessManager::createRequest(operation, request, device); + } +} diff --git a/libraries/networking/src/NetworkAccessManager.h b/libraries/networking/src/NetworkAccessManager.h index c4b435adb6..1679ed081c 100644 --- a/libraries/networking/src/NetworkAccessManager.h +++ b/libraries/networking/src/NetworkAccessManager.h @@ -1,6 +1,6 @@ // // NetworkAccessManager.h -// +// libraries/networking/src // // Created by Clement on 7/1/14. // Copyright 2014 High Fidelity, Inc. @@ -13,12 +13,17 @@ #define hifi_NetworkAccessManager_h #include +#include +#include /// Wrapper around QNetworkAccessManager to restrict at one instance by thread -class NetworkAccessManager : public QObject { +class NetworkAccessManager : public QNetworkAccessManager { Q_OBJECT public: static QNetworkAccessManager& getInstance(); +protected: + NetworkAccessManager(QObject* parent = Q_NULLPTR) : QNetworkAccessManager(parent) {} + virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* device = Q_NULLPTR) override; }; #endif // hifi_NetworkAccessManager_h \ No newline at end of file diff --git a/libraries/networking/src/OAuthNetworkAccessManager.h b/libraries/networking/src/OAuthNetworkAccessManager.h index acfd52e18f..434d9b7c75 100644 --- a/libraries/networking/src/OAuthNetworkAccessManager.h +++ b/libraries/networking/src/OAuthNetworkAccessManager.h @@ -12,12 +12,13 @@ #ifndef hifi_OAuthNetworkAccessManager_h #define hifi_OAuthNetworkAccessManager_h -#include +#include "NetworkAccessManager.h" -class OAuthNetworkAccessManager : public QNetworkAccessManager { +class OAuthNetworkAccessManager : public NetworkAccessManager { public: static OAuthNetworkAccessManager* getInstance(); protected: + OAuthNetworkAccessManager(QObject* parent = Q_NULLPTR) : NetworkAccessManager(parent) { } virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest& req, QIODevice* outgoingData = 0); }; diff --git a/libraries/networking/src/QmlNetworkAccessManager.cpp b/libraries/networking/src/QmlNetworkAccessManager.cpp deleted file mode 100644 index 575bc02f8c..0000000000 --- a/libraries/networking/src/QmlNetworkAccessManager.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// -// QmlNetworkAccessManager.cpp -// libraries/networking/src -// -// Created by Zander Otavka on 8/4/16. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include "QmlAtpReply.h" -#include "QmlNetworkAccessManager.h" - -QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) { - return new QmlNetworkAccessManager(parent); -} - -QNetworkReply* QmlNetworkAccessManager::createRequest(Operation operation, const QNetworkRequest& request, QIODevice* device) { - if (request.url().scheme() == "atp" && operation == GetOperation) { - return new QmlAtpReply(request.url()); - //auto url = request.url().toString(); - //return QNetworkAccessManager::createRequest(operation, request, device); - } else { - return QNetworkAccessManager::createRequest(operation, request, device); - } -} diff --git a/libraries/networking/src/QmlNetworkAccessManager.h b/libraries/networking/src/QmlNetworkAccessManager.h deleted file mode 100644 index 059d0ebba0..0000000000 --- a/libraries/networking/src/QmlNetworkAccessManager.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// QmlNetworkAccessManager.h -// libraries/networking/src -// -// Created by Zander Otavka on 8/4/16. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_QmlNetworkAccessManager_h -#define hifi_QmlNetworkAccessManager_h - -#include -#include -#include - -class QmlNetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory { -public: - QNetworkAccessManager* create(QObject* parent); -}; - - -class QmlNetworkAccessManager : public QNetworkAccessManager { - Q_OBJECT -public: - QmlNetworkAccessManager(QObject* parent = Q_NULLPTR) : QNetworkAccessManager(parent) { } -protected: - QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, QIODevice* device = Q_NULLPTR); -}; - -#endif // hifi_QmlNetworkAccessManager_h From 36d9f921011cc5e3ea4c5d4efedae4eaadbd4524 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 9 Aug 2016 13:10:02 -0700 Subject: [PATCH 108/279] performance optimization to minimize calling usecTimestampNow --- .../entities-renderer/src/RenderableEntityItem.h | 9 +++++---- .../src/RenderableLightEntityItem.cpp | 4 +++- .../src/RenderablePolyLineEntityItem.cpp | 6 ++++-- .../src/RenderableShapeEntityItem.cpp | 12 +++++++----- .../src/RenderableTextEntityItem.cpp | 4 ++-- .../src/RenderableWebEntityItem.cpp | 4 ++-- libraries/entities/src/EntityItem.cpp | 2 +- libraries/entities/src/EntityItem.h | 3 ++- .../procedural/src/procedural/Procedural.cpp | 1 + libraries/procedural/src/procedural/Procedural.h | 3 +++ libraries/render-utils/src/MeshPartPayload.cpp | 15 ++++++++++----- libraries/render-utils/src/MeshPartPayload.h | 4 +++- 12 files changed, 43 insertions(+), 24 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 09d6d88c6a..22b6264520 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -96,16 +96,17 @@ public: \ virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override { _renderHelper.removeFromScene(self, scene, pendingChanges); } \ virtual void locationChanged(bool tellPhysics = true) override { EntityItem::locationChanged(tellPhysics); _renderHelper.notifyChanged(); } \ virtual void dimensionsChanged() override { EntityItem::dimensionsChanged(); _renderHelper.notifyChanged(); } \ - void checkTransparency() { \ + void checkFading() { \ bool transparent = isTransparent(); \ - if (transparent != prevIsTransparent) { \ + if (transparent != _prevIsTransparent) { \ _renderHelper.notifyChanged(); \ - prevIsTransparent = transparent; \ + _isFading = false; \ + _prevIsTransparent = transparent; \ } \ } \ private: \ SimpleRenderableEntityItem _renderHelper; \ - bool prevIsTransparent { isTransparent() }; + bool _prevIsTransparent { isTransparent() }; #endif // hifi_RenderableEntityItem_h diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index fccd52d58c..b7f32cca65 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -28,6 +28,8 @@ EntityItemPointer RenderableLightEntityItem::factory(const EntityItemID& entityI void RenderableLightEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableLightEntityItem::render"); assert(getType() == EntityTypes::Light); + checkFading(); + glm::vec3 position = getPosition(); glm::vec3 dimensions = getDimensions(); glm::quat rotation = getRotation(); @@ -35,7 +37,7 @@ void RenderableLightEntityItem::render(RenderArgs* args) { glm::vec3 color = toGlm(getXColor()); - float intensity = getIntensity() * Interpolate::calculateFadeRatio(_fadeStartTime); + float intensity = getIntensity() * (_isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f); float falloffRadius = getFalloffRadius(); float exponent = getExponent(); float cutoff = glm::radians(getCutoff()); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index ef4c9d6b5d..dc2545b956 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -167,7 +167,7 @@ void RenderablePolyLineEntityItem::update(const quint64& now) { } void RenderablePolyLineEntityItem::render(RenderArgs* args) { - checkTransparency(); + checkFading(); QWriteLocker lock(&_quadReadWriteLock); if (_points.size() < 2 || _normals.size () < 2 || _strokeWidths.size() < 2) { @@ -206,7 +206,9 @@ void RenderablePolyLineEntityItem::render(RenderArgs* args) { batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); - batch._glColor4f(1.0f, 1.0f, 1.0f, Interpolate::calculateFadeRatio(_fadeStartTime)); + if (_isFading) { + batch._glColor4f(1.0f, 1.0f, 1.0f, Interpolate::calculateFadeRatio(_fadeStartTime)); + } batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0); }; diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index f557625db2..73c4d99b5e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -72,8 +72,10 @@ void RenderableShapeEntityItem::setUserData(const QString& value) { } bool RenderableShapeEntityItem::isTransparent() { - if (_procedural && _procedural->ready()) { - return Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f; + if (_procedural && _procedural->isFading()) { + float isFading = Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) < 1.0f; + _procedural->setIsFading(isFading); + return isFading; } else { return getLocalRenderAlpha() < 1.0f || EntityItem::isTransparent(); } @@ -83,7 +85,7 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); //Q_ASSERT(getType() == EntityTypes::Shape); Q_ASSERT(args->_batch); - checkTransparency(); + checkFading(); if (!_procedural) { _procedural.reset(new Procedural(getUserData())); @@ -110,12 +112,12 @@ void RenderableShapeEntityItem::render(RenderArgs* args) { if (_procedural->ready()) { _procedural->prepare(batch, getPosition(), getDimensions(), getOrientation()); auto outColor = _procedural->getColor(color); - outColor.a *= Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()); + outColor.a *= _procedural->isFading() ? Interpolate::calculateFadeRatio(_procedural->getFadeStartTime()) : 1.0f; batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); DependencyManager::get()->renderShape(batch, MAPPING[_shape]); } else { // FIXME, support instanced multi-shape rendering using multidraw indirect - color.a *= Interpolate::calculateFadeRatio(_fadeStartTime); + color.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; auto geometryCache = DependencyManager::get(); auto pipeline = color.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline(); geometryCache->renderSolidShapeInstance(batch, MAPPING[_shape], color, pipeline); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index ccdaa39bdd..20adff83df 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -27,10 +27,10 @@ EntityItemPointer RenderableTextEntityItem::factory(const EntityItemID& entityID void RenderableTextEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderableTextEntityItem::render"); Q_ASSERT(getType() == EntityTypes::Text); - checkTransparency(); + checkFading(); static const float SLIGHTLY_BEHIND = -0.005f; - float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime); + float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; bool transparent = fadeRatio < 1.0f; glm::vec4 textColor = glm::vec4(toGlm(getTextColorX()), fadeRatio); glm::vec4 backgroundColor = glm::vec4(toGlm(getBackgroundColorX()), fadeRatio); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index d6c1c1f761..c712ee506e 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -164,7 +164,7 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { } void RenderableWebEntityItem::render(RenderArgs* args) { - checkTransparency(); + checkFading(); #ifdef WANT_EXTRA_DEBUGGING { @@ -210,7 +210,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) { batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture); } - float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime); + float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); DependencyManager::get()->bindSimpleSRGBTexturedUnlitNoTexAlphaProgram(batch); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index f774d52274..b9f384f013 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2212,4 +2212,4 @@ void EntityItem::globalizeProperties(EntityItemProperties& properties, const QSt } QUuid empty; properties.setParentID(empty); -} +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 198928da4e..f12075d191 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -436,7 +436,7 @@ public: QUuid getOwningAvatarID() const { return _owningAvatarID; } void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } - virtual bool isTransparent() { return Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f; } + virtual bool isTransparent() { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; } protected: @@ -568,6 +568,7 @@ protected: quint64 _lastUpdatedAccelerationTimestamp { 0 }; quint64 _fadeStartTime { usecTimestampNow() }; + bool _isFading { true }; }; #endif // hifi_EntityItem_h diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index 7f8ab2db41..cd9edb6621 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -208,6 +208,7 @@ bool Procedural::ready() { if (!_hasStartedFade) { _hasStartedFade = true; + _isFading = true; } return true; } diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index dea55f197b..c2939e4a01 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -43,6 +43,8 @@ public: glm::vec4 getColor(const glm::vec4& entityColor); quint64 getFadeStartTime() { return _fadeStartTime; } + bool isFading() { return _isFading; } + void setIsFading(bool isFading) { _isFading = isFading; } uint8_t _version { 1 }; @@ -110,6 +112,7 @@ private: quint64 _fadeStartTime; bool _hasStartedFade { false }; + bool _isFading { false }; }; #endif diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 2fa31022ec..38d181e748 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -391,7 +391,7 @@ ItemKey ModelMeshPartPayload::getKey() const { } } - if (Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f) { + if (!_hasFinishedFade) { builder.withTransparent(); } @@ -446,7 +446,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { } ShapeKey::Builder builder; - if (isTranslucent || Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f) { + if (isTranslucent || !_hasFinishedFade) { builder.withTranslucent(); } if (hasTangents) { @@ -487,7 +487,7 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } - float fadeRatio = Interpolate::calculateFadeRatio(_fadeStartTime); + float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; if (!_hasColorAttrib || fadeRatio < 1.0f) { batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); } @@ -519,6 +519,8 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: void ModelMeshPartPayload::startFade() { _fadeStartTime = usecTimestampNow(); _hasStartedFade = true; + _prevHasStartedFade = false; + _hasFinishedFade = false; } void ModelMeshPartPayload::render(RenderArgs* args) const { @@ -530,10 +532,13 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // When an individual mesh parts like this finishes its fade, we will mark the Model as // having render items that need updating - if (_wasFading && !isStillFading()) { + bool nextIsFading = _isFading ? isStillFading() : false; + if (_isFading != nextIsFading || _prevHasStartedFade != _hasStartedFade) { + _isFading = nextIsFading || _prevHasStartedFade != _hasStartedFade; + _hasFinishedFade = _prevHasStartedFade == _hasStartedFade && !_isFading; + _prevHasStartedFade = _hasStartedFade; _model->setRenderItemsNeedUpdate(); } - _wasFading = isStillFading(); gpu::Batch& batch = *(args->_batch); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 8fdafee9b0..67fb660f8b 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -110,7 +110,9 @@ public: private: quint64 _fadeStartTime { 0 }; bool _hasStartedFade { false }; - mutable bool _wasFading { false }; + mutable bool _prevHasStartedFade{ false }; + mutable bool _hasFinishedFade { false }; + mutable bool _isFading { false }; }; namespace render { From 5a4d15dd5bf06190148635a8a8a56b499e0be9ff Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 9 Aug 2016 14:02:42 -0700 Subject: [PATCH 109/279] fix warning --- libraries/gpu/src/gpu/Texture.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 25e4fa549c..f1a8960fa1 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -700,8 +700,6 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< return false; } - const float UCHAR_TO_FLOAT = 1.0f / float(std::numeric_limits::max()); - // for each face of cube texture for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) { From 956078bc91ef652ad1b38f66a3d767f87155de35 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 9 Aug 2016 15:17:36 -0700 Subject: [PATCH 110/279] teleport works with xbox controller --- scripts/system/controllers/teleport.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index fae9b98b96..5ec429fae0 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -232,14 +232,11 @@ function Teleporter() { }; this.rightRay = function() { + var pose = Controller.getPoseValue(Controller.Standard.RightHand); + var rightPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition(); + var rightRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) : MyAvatar.headOrientation; - var rightPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.RightHand).translation), MyAvatar.position); - - var rightControllerRotation = Controller.getPoseValue(Controller.Standard.RightHand).rotation; - - var rightRotation = Quat.multiply(MyAvatar.orientation, rightControllerRotation); - - var rightFinal = Quat.multiply(rightRotation, Quat.angleAxis(90, { + var rightFinal = Quat.multiply(rightRotation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 @@ -247,7 +244,7 @@ function Teleporter() { var rightPickRay = { origin: rightPosition, - direction: Quat.getUp(rightRotation), + direction: Quat.getUp(pose.valid ? rightRotation : rightFinal), }; this.rightPickRay = rightPickRay; @@ -288,11 +285,11 @@ function Teleporter() { this.leftRay = function() { - var leftPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.LeftHand).translation), MyAvatar.position); + var pose = Controller.getPoseValue(Controller.Standard.LeftHand); + var leftPosition = pose.valid ? Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position) : MyAvatar.getHeadPosition(); + var leftRotation = pose.valid ? Quat.multiply(MyAvatar.orientation, pose.rotation) : MyAvatar.headOrientation; - var leftRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(Controller.Standard.LeftHand).rotation) - - var leftFinal = Quat.multiply(leftRotation, Quat.angleAxis(90, { + var leftFinal = Quat.multiply(leftRotation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 @@ -300,7 +297,7 @@ function Teleporter() { var leftPickRay = { origin: leftPosition, - direction: Quat.getUp(leftRotation), + direction: Quat.getUp(pose.valid ? leftRotation : leftFinal), }; this.leftPickRay = leftPickRay; From 6965fb05206dfb8463ba435446f1fe96eefec461 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 9 Aug 2016 14:15:06 -0700 Subject: [PATCH 111/279] refactor system html to split js/css --- scripts/system/html/{ => css}/edit-style.css | 46 +- scripts/system/html/entityList.html | 314 +--- scripts/system/html/entityProperties.html | 1322 +---------------- scripts/system/html/entityProperties.js | 1317 ++++++++++++++++ scripts/system/html/gridControls.html | 142 +- scripts/system/html/{ => js}/colpick.js | 0 scripts/system/html/js/entityList.js | 310 ++++ scripts/system/html/js/entityProperties.js | 1317 ++++++++++++++++ .../system/html/{ => js}/eventBridgeLoader.js | 0 scripts/system/html/js/gridControls.js | 138 ++ .../system/html/{ => js}/jquery-2.1.4.min.js | 0 scripts/system/html/{ => js}/list.min.js | 0 scripts/system/html/{ => js}/spinButtons.js | 0 13 files changed, 3121 insertions(+), 1785 deletions(-) rename scripts/system/html/{ => css}/edit-style.css (94%) create mode 100644 scripts/system/html/entityProperties.js rename scripts/system/html/{ => js}/colpick.js (100%) create mode 100644 scripts/system/html/js/entityList.js create mode 100644 scripts/system/html/js/entityProperties.js rename scripts/system/html/{ => js}/eventBridgeLoader.js (100%) create mode 100644 scripts/system/html/js/gridControls.js rename scripts/system/html/{ => js}/jquery-2.1.4.min.js (100%) rename scripts/system/html/{ => js}/list.min.js (100%) rename scripts/system/html/{ => js}/spinButtons.js (100%) diff --git a/scripts/system/html/edit-style.css b/scripts/system/html/css/edit-style.css similarity index 94% rename from scripts/system/html/edit-style.css rename to scripts/system/html/css/edit-style.css index 19d1cd95a9..0b58cf22ac 100644 --- a/scripts/system/html/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -10,51 +10,51 @@ @font-face { font-family: Raleway-Regular; - src: url(../../../resources/fonts/Raleway-Regular.ttf), /* Windows production */ - url(../../../fonts/Raleway-Regular.ttf), /* OSX production */ - url(../../../interface/resources/fonts/Raleway-Regular.ttf); /* Development, running script in /HiFi/examples */ + src: url(../../../../resources/fonts/Raleway-Regular.ttf), /* Windows production */ + url(../../../../fonts/Raleway-Regular.ttf), /* OSX production */ + url(../../../../interface/resources/fonts/Raleway-Regular.ttf); /* Development, running script in /HiFi/examples */ } @font-face { font-family: Raleway-Light; - src: url(../../../resources/fonts/Raleway-Light.ttf), - url(../../../fonts/Raleway-Light.ttf), - url(../../../interface/resources/fonts/Raleway-Light.ttf); + src: url(../../../../resources/fonts/Raleway-Light.ttf), + url(../../../../fonts/Raleway-Light.ttf), + url(../../../../interface/resources/fonts/Raleway-Light.ttf); } @font-face { font-family: Raleway-Bold; - src: url(../../../resources/fonts/Raleway-Bold.ttf), - url(../../../fonts/Raleway-Bold.ttf), - url(../../../interface/resources/fonts/Raleway-Bold.ttf); + src: url(../../../../resources/fonts/Raleway-Bold.ttf), + url(../../../../fonts/Raleway-Bold.ttf), + url(../../../../interface/resources/fonts/Raleway-Bold.ttf); } @font-face { font-family: Raleway-SemiBold; - src: url(../../../resources/fonts/Raleway-SemiBold.ttf), - url(../../../fonts/Raleway-SemiBold.ttf), - url(../../../interface/resources/fonts/Raleway-SemiBold.ttf); + src: url(../../../../resources/fonts/Raleway-SemiBold.ttf), + url(../../../../fonts/Raleway-SemiBold.ttf), + url(../../../../interface/resources/fonts/Raleway-SemiBold.ttf); } @font-face { font-family: FiraSans-SemiBold; - src: url(../../../resources/fonts/FiraSans-SemiBold.ttf), - url(../../../fonts/FiraSans-SemiBold.ttf), - url(../../../interface/resources/fonts/FiraSans-SemiBold.ttf); + src: url(../../../../resources/fonts/FiraSans-SemiBold.ttf), + url(../../../../fonts/FiraSans-SemiBold.ttf), + url(../../../../interface/resources/fonts/FiraSans-SemiBold.ttf); } @font-face { font-family: AnonymousPro-Regular; - src: url(../../../resources/fonts/AnonymousPro-Regular.ttf), - url(../../../fonts/AnonymousPro-Regular.ttf), - url(../../../interface/resources/fonts/AnonymousPro-Regular.ttf); + src: url(../../../../resources/fonts/AnonymousPro-Regular.ttf), + url(../../../../fonts/AnonymousPro-Regular.ttf), + url(../../../../interface/resources/fonts/AnonymousPro-Regular.ttf); } @font-face { font-family: HiFi-Glyphs; - src: url(../../../resources/fonts/hifi-glyphs.ttf), - url(../../../fonts/hifi-glyphs.ttf), - url(../../../interface/resources/fonts/hifi-glyphs.ttf); + src: url(../../../../resources/fonts/hifi-glyphs.ttf), + url(../../../../fonts/hifi-glyphs.ttf), + url(../../../../interface/resources/fonts/hifi-glyphs.ttf); } * { @@ -1077,10 +1077,6 @@ input#dimension-rescale-button { input#reset-to-natural-dimensions { margin-right: 0; } -input#preview-camera-button { - margin-left: 1px; - margin-right: 0; -} #animation-fps { margin-top: 48px; diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index dbc224e9fb..2088898613 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -10,314 +10,12 @@ - - + + - - - + + +

@@ -378,4 +76,4 @@
- \ No newline at end of file + diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 424795981d..01826b8e10 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -11,1325 +11,15 @@ Properties - + - - + + - - - + + + -
diff --git a/scripts/system/html/entityProperties.js b/scripts/system/html/entityProperties.js new file mode 100644 index 0000000000..490579e909 --- /dev/null +++ b/scripts/system/html/entityProperties.js @@ -0,0 +1,1317 @@ +// entityProperties.js +// +// Created by Ryan Huffman on 13 Nov 2014 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +var PI = 3.14159265358979; +var DEGREES_TO_RADIANS = PI / 180.0; +var RADIANS_TO_DEGREES = 180.0 / PI; +var ICON_FOR_TYPE = { + Box: "V", + Sphere: "n", + Shape: "n", + ParticleEffect: "", + Model: "", + Web: "q", + Text: "l", + Light: "p", + Zone: "o", + PolyVox: "", + Multiple: "" +} + +var colorPickers = []; + +debugPrint = function(message) { + EventBridge.emitWebEvent( + JSON.stringify({ + type:"print", + message: message + }) + ); +}; + +function enableChildren(el, selector) { + els = el.querySelectorAll(selector); + for (var i = 0; i < els.length; i++) { + els[i].removeAttribute('disabled'); + } +} +function disableChildren(el, selector) { + els = el.querySelectorAll(selector); + for (var i = 0; i < els.length; i++) { + els[i].setAttribute('disabled', 'disabled'); + } +} + +function enableProperties() { + enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); + enableChildren(document, ".colpick"); +} + +function disableProperties() { + disableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); + disableChildren(document, ".colpick"); + for (var i = 0; i < colorPickers.length; i++) { + colorPickers[i].colpickHide(); + } +} + +function showElements(els, show) { + for (var i = 0; i < els.length; i++) { + els[i].style.display = (show) ? 'table' : 'none'; + } +} + +function createEmitCheckedPropertyUpdateFunction(propertyName) { + return function() { + EventBridge.emitWebEvent( + '{ "type":"update", "properties":{"' + propertyName + '":' + this.checked + '}}' + ); + }; +} + +function createEmitCheckedToStringPropertyUpdateFunction(checkboxElement, name, propertyName) { + var newString = ""; + if (checkboxElement.checked) { + newString += name + ""; + } else { + + } + +} + +function createEmitGroupCheckedPropertyUpdateFunction(group, propertyName) { + return function () { + var properties = {}; + properties[group] = {}; + properties[group][propertyName] = this.checked; + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties + }) + ); + }; +} + +function createEmitNumberPropertyUpdateFunction(propertyName, decimals) { + decimals = decimals == undefined ? 4 : decimals; + return function() { + var value = parseFloat(this.value).toFixed(decimals); + + EventBridge.emitWebEvent( + '{ "type":"update", "properties":{"' + propertyName + '":' + value + '}}' + ); + }; +} +function createEmitGroupNumberPropertyUpdateFunction(group, propertyName) { + return function() { + var properties = {}; + properties[group] = {}; + properties[group][propertyName] = this.value; + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties, + }) + ); + }; +} + + +function createEmitTextPropertyUpdateFunction(propertyName) { + return function() { + var properties = {}; + properties[propertyName] = this.value; + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties, + }) + ); + }; +} + +function createEmitGroupTextPropertyUpdateFunction(group,propertyName) { + return function() { + var properties = {}; + properties[group] = {}; + properties[group][propertyName] = this.value; + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties, + }) + ); + }; +} + +function createEmitVec3PropertyUpdateFunction(property, elX, elY, elZ) { + return function() { + var data = { + type: "update", + properties: { + } + }; + data.properties[property] = { + x: elX.value, + y: elY.value, + z: elZ.value, + }; + EventBridge.emitWebEvent(JSON.stringify(data)); + } +}; + +function createEmitGroupVec3PropertyUpdateFunction(group, property, elX, elY, elZ) { + return function() { + var data = { + type: "update", + properties: { + } + }; + data.properties[group] = { }; + data.properties[group][property] = { + x: elX.value, + y: elY.value, + z: elZ ? elZ.value : 0, + }; + EventBridge.emitWebEvent(JSON.stringify(data)); + } +}; + +function createEmitVec3PropertyUpdateFunctionWithMultiplier(property, elX, elY, elZ, multiplier) { + return function() { + var data = { + type: "update", + properties: { + } + }; + data.properties[property] = { + x: elX.value * multiplier, + y: elY.value * multiplier, + z: elZ.value * multiplier, + }; + EventBridge.emitWebEvent(JSON.stringify(data)); + } +}; + +function createEmitColorPropertyUpdateFunction(property, elRed, elGreen, elBlue) { + return function() { + emitColorPropertyUpdate(property, elRed.value, elGreen.value, elBlue.value); + } +}; + +function emitColorPropertyUpdate(property, red, green, blue, group) { + var data = { + type: "update", + properties: { + } + }; + if (group) { + data.properties[group] = { }; + data.properties[group][property] = { + red: red, + green: green, + blue: blue, + }; + } else { + data.properties[property] = { + red: red, + green: green, + blue: blue, + }; + } + EventBridge.emitWebEvent(JSON.stringify(data)); +}; + + +function createEmitGroupColorPropertyUpdateFunction(group, property, elRed, elGreen, elBlue) { + return function() { + var data = { + type: "update", + properties: { + } + }; + data.properties[group] = { }; + + data.properties[group][property] = { + red: elRed.value, + green: elGreen.value, + blue: elBlue.value, + }; + EventBridge.emitWebEvent(JSON.stringify(data)); + } +}; + +function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString) { + if (subPropertyElement.checked) { + if (propertyValue.indexOf(subPropertyString)) { + propertyValue += subPropertyString + ','; + } + } else { + // We've unchecked, so remove + propertyValue = propertyValue.replace(subPropertyString + ",", ""); + } + + var _properties ={} + _properties[propertyName] = propertyValue; + + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: _properties + }) + ); + +} + +function userDataChanger(groupName, keyName, checkBoxElement, userDataElement, defaultValue) { + var properties = {}; + var parsedData = {}; + try { + parsedData = JSON.parse(userDataElement.value); + } catch(e) {} + + if (!(groupName in parsedData)) { + parsedData[groupName] = {} + } + delete parsedData[groupName][keyName]; + if (checkBoxElement.checked !== defaultValue) { + parsedData[groupName][keyName] = checkBoxElement.checked; + } + + if (Object.keys(parsedData[groupName]).length == 0) { + delete parsedData[groupName]; + } + if (Object.keys(parsedData).length > 0) { + properties['userData'] = JSON.stringify(parsedData); + } else { + properties['userData'] = ''; + } + + userDataElement.value = properties['userData']; + + EventBridge.emitWebEvent( + JSON.stringify({ + type: "update", + properties: properties, + }) + ); +}; + +function setTextareaScrolling(element) { + var isScrolling = element.scrollHeight > element.offsetHeight; + element.setAttribute("scrolling", isScrolling ? "true" : "false"); +} + +function loaded() { + openEventBridge(function() { + var allSections = []; + var elID = document.getElementById("property-id"); + var elType = document.getElementById("property-type"); + var elTypeIcon = document.getElementById("type-icon"); + var elName = document.getElementById("property-name"); + var elLocked = document.getElementById("property-locked"); + var elVisible = document.getElementById("property-visible"); + var elPositionX = document.getElementById("property-pos-x"); + var elPositionY = document.getElementById("property-pos-y"); + var elPositionZ = document.getElementById("property-pos-z"); + var elMoveSelectionToGrid = document.getElementById("move-selection-to-grid"); + var elMoveAllToGrid = document.getElementById("move-all-to-grid"); + + var elDimensionsX = document.getElementById("property-dim-x"); + var elDimensionsY = document.getElementById("property-dim-y"); + var elDimensionsZ = document.getElementById("property-dim-z"); + var elResetToNaturalDimensions = document.getElementById("reset-to-natural-dimensions"); + var elRescaleDimensionsPct = document.getElementById("dimension-rescale-pct"); + var elRescaleDimensionsButton = document.getElementById("dimension-rescale-button"); + + var elParentID = document.getElementById("property-parent-id"); + var elParentJointIndex = document.getElementById("property-parent-joint-index"); + + var elRegistrationX = document.getElementById("property-reg-x"); + var elRegistrationY = document.getElementById("property-reg-y"); + var elRegistrationZ = document.getElementById("property-reg-z"); + + var elRotationX = document.getElementById("property-rot-x"); + var elRotationY = document.getElementById("property-rot-y"); + var elRotationZ = document.getElementById("property-rot-z"); + + var elLinearVelocityX = document.getElementById("property-lvel-x"); + var elLinearVelocityY = document.getElementById("property-lvel-y"); + var elLinearVelocityZ = document.getElementById("property-lvel-z"); + var elLinearDamping = document.getElementById("property-ldamping"); + + var elAngularVelocityX = document.getElementById("property-avel-x"); + var elAngularVelocityY = document.getElementById("property-avel-y"); + var elAngularVelocityZ = document.getElementById("property-avel-z"); + var elAngularDamping = document.getElementById("property-adamping"); + + var elRestitution = document.getElementById("property-restitution"); + var elFriction = document.getElementById("property-friction"); + + var elGravityX = document.getElementById("property-grav-x"); + var elGravityY = document.getElementById("property-grav-y"); + var elGravityZ = document.getElementById("property-grav-z"); + + var elAccelerationX = document.getElementById("property-lacc-x"); + var elAccelerationY = document.getElementById("property-lacc-y"); + var elAccelerationZ = document.getElementById("property-lacc-z"); + + var elDensity = document.getElementById("property-density"); + var elCollisionless = document.getElementById("property-collisionless"); + var elDynamic = document.getElementById("property-dynamic" ); + var elCollideStatic = document.getElementById("property-collide-static"); + var elCollideDynamic = document.getElementById("property-collide-dynamic"); + var elCollideKinematic = document.getElementById("property-collide-kinematic"); + var elCollideMyAvatar = document.getElementById("property-collide-myAvatar"); + var elCollideOtherAvatar = document.getElementById("property-collide-otherAvatar"); + var elCollisionSoundURL = document.getElementById("property-collision-sound-url"); + + var elGrabbable = document.getElementById("property-grabbable"); + var elWantsTrigger = document.getElementById("property-wants-trigger"); + var elIgnoreIK = document.getElementById("property-ignore-ik"); + + var elLifetime = document.getElementById("property-lifetime"); + var elScriptURL = document.getElementById("property-script-url"); + /* + FIXME: See FIXME for property-script-url. + var elScriptTimestamp = document.getElementById("property-script-timestamp"); + */ + var elReloadScriptButton = document.getElementById("reload-script-button"); + var elUserData = document.getElementById("property-user-data"); + + var elColorSections = document.querySelectorAll(".color-section"); + var elColor = document.getElementById("property-color"); + var elColorRed = document.getElementById("property-color-red"); + var elColorGreen = document.getElementById("property-color-green"); + var elColorBlue = document.getElementById("property-color-blue"); + + var elShapeSections = document.querySelectorAll(".shape-section"); + allSections.push(elShapeSections); + var elShape = document.getElementById("property-shape"); + + var elLightSections = document.querySelectorAll(".light-section"); + allSections.push(elLightSections); + var elLightSpotLight = document.getElementById("property-light-spot-light"); + var elLightColor = document.getElementById("property-light-color"); + var elLightColorRed = document.getElementById("property-light-color-red"); + var elLightColorGreen = document.getElementById("property-light-color-green"); + var elLightColorBlue = document.getElementById("property-light-color-blue"); + + var elLightIntensity = document.getElementById("property-light-intensity"); + var elLightFalloffRadius = document.getElementById("property-light-falloff-radius"); + var elLightExponent = document.getElementById("property-light-exponent"); + var elLightCutoff = document.getElementById("property-light-cutoff"); + + var elModelSections = document.querySelectorAll(".model-section"); + allSections.push(elModelSections); + var elModelURL = document.getElementById("property-model-url"); + var elShapeType = document.getElementById("property-shape-type"); + var elCompoundShapeURL = document.getElementById("property-compound-shape-url"); + var elModelAnimationURL = document.getElementById("property-model-animation-url"); + var elModelAnimationPlaying = document.getElementById("property-model-animation-playing"); + var elModelAnimationFPS = document.getElementById("property-model-animation-fps"); + var elModelAnimationFrame = document.getElementById("property-model-animation-frame"); + var elModelAnimationFirstFrame = document.getElementById("property-model-animation-first-frame"); + var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame"); + var elModelAnimationLoop = document.getElementById("property-model-animation-loop"); + var elModelAnimationHold = document.getElementById("property-model-animation-hold"); + var elModelTextures = document.getElementById("property-model-textures"); + var elModelOriginalTextures = document.getElementById("property-model-original-textures"); + + var elWebSections = document.querySelectorAll(".web-section"); + allSections.push(elWebSections); + var elWebSourceURL = document.getElementById("property-web-source-url"); + + var elDescription = document.getElementById("property-description"); + + var elHyperlinkHref = document.getElementById("property-hyperlink-href"); + + var elHyperlinkSections = document.querySelectorAll(".hyperlink-section"); + + + var elTextSections = document.querySelectorAll(".text-section"); + allSections.push(elTextSections); + var elTextText = document.getElementById("property-text-text"); + var elTextLineHeight = document.getElementById("property-text-line-height"); + var elTextTextColor = document.getElementById("property-text-text-color"); + var elTextFaceCamera = document.getElementById("property-text-face-camera"); + var elTextTextColorRed = document.getElementById("property-text-text-color-red"); + var elTextTextColorGreen = document.getElementById("property-text-text-color-green"); + var elTextTextColorBlue = document.getElementById("property-text-text-color-blue"); + var elTextBackgroundColor = document.getElementById("property-text-background-color"); + var elTextBackgroundColorRed = document.getElementById("property-text-background-color-red"); + var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green"); + var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue"); + + var elZoneSections = document.querySelectorAll(".zone-section"); + allSections.push(elZoneSections); + var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled"); + + var elZoneKeyLightColor = document.getElementById("property-zone-key-light-color"); + var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red"); + var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green"); + var elZoneKeyLightColorBlue = document.getElementById("property-zone-key-light-color-blue"); + var elZoneKeyLightIntensity = document.getElementById("property-zone-key-intensity"); + var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity"); + var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x"); + var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y"); + var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z"); + var elZoneKeyLightAmbientURL = document.getElementById("property-zone-key-ambient-url"); + + var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude"); + var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude"); + var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude"); + var elZoneStageAutomaticHourDay = document.getElementById("property-zone-stage-automatic-hour-day"); + var elZoneStageDay = document.getElementById("property-zone-stage-day"); + var elZoneStageHour = document.getElementById("property-zone-stage-hour"); + + var elZoneBackgroundMode = document.getElementById("property-zone-background-mode"); + + var elZoneSkyboxColor = document.getElementById("property-zone-skybox-color"); + var elZoneSkyboxColorRed = document.getElementById("property-zone-skybox-color-red"); + var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green"); + var elZoneSkyboxColorBlue = document.getElementById("property-zone-skybox-color-blue"); + var elZoneSkyboxURL = document.getElementById("property-zone-skybox-url"); + + var elZoneFlyingAllowed = document.getElementById("property-zone-flying-allowed"); + var elZoneGhostingAllowed = document.getElementById("property-zone-ghosting-allowed"); + + var elPolyVoxSections = document.querySelectorAll(".poly-vox-section"); + allSections.push(elPolyVoxSections); + var elVoxelVolumeSizeX = document.getElementById("property-voxel-volume-size-x"); + var elVoxelVolumeSizeY = document.getElementById("property-voxel-volume-size-y"); + var elVoxelVolumeSizeZ = document.getElementById("property-voxel-volume-size-z"); + var elVoxelSurfaceStyle = document.getElementById("property-voxel-surface-style"); + var elXTextureURL = document.getElementById("property-x-texture-url"); + var elYTextureURL = document.getElementById("property-y-texture-url"); + var elZTextureURL = document.getElementById("property-z-texture-url"); + + var elPreviewCameraButton = document.getElementById("preview-camera-button"); + + if (window.EventBridge !== undefined) { + var properties; + EventBridge.scriptEventReceived.connect(function(data) { + data = JSON.parse(data); + if (data.type == "update") { + if (data.selections.length == 0) { + elTypeIcon.style.display = "none"; + elType.innerHTML = "No selection"; + elID.innerHTML = ""; + disableProperties(); + } else if (data.selections.length > 1) { + var selections = data.selections; + + var ids = []; + var types = {}; + var numTypes = 0; + + for (var i = 0; i < selections.length; i++) { + ids.push(selections[i].id); + var type = selections[i].properties.type; + if (types[type] === undefined) { + types[type] = 0; + numTypes += 1; + } + types[type]++; + } + + var type; + if (numTypes === 1) { + type = selections[0].properties.type; + } else { + type = "Multiple"; + } + elType.innerHTML = type + " (" + data.selections.length + ")"; + elTypeIcon.innerHTML = ICON_FOR_TYPE[type]; + elTypeIcon.style.display = "inline-block"; + + elID.innerHTML = ids.join("
"); + + disableProperties(); + } else { + + + properties = data.selections[0].properties; + + elID.innerHTML = properties.id; + + elType.innerHTML = properties.type; + elTypeIcon.innerHTML = ICON_FOR_TYPE[properties.type]; + elTypeIcon.style.display = "inline-block"; + + elLocked.checked = properties.locked; + + if (properties.locked) { + disableProperties(); + elLocked.removeAttribute('disabled'); + } else { + enableProperties(); + } + + elName.value = properties.name; + + elVisible.checked = properties.visible; + + elPositionX.value = properties.position.x.toFixed(4); + elPositionY.value = properties.position.y.toFixed(4); + elPositionZ.value = properties.position.z.toFixed(4); + + elDimensionsX.value = properties.dimensions.x.toFixed(4); + elDimensionsY.value = properties.dimensions.y.toFixed(4); + elDimensionsZ.value = properties.dimensions.z.toFixed(4); + + elParentID.value = properties.parentID; + elParentJointIndex.value = properties.parentJointIndex; + + elRegistrationX.value = properties.registrationPoint.x.toFixed(4); + elRegistrationY.value = properties.registrationPoint.y.toFixed(4); + elRegistrationZ.value = properties.registrationPoint.z.toFixed(4); + + elRotationX.value = properties.rotation.x.toFixed(4); + elRotationY.value = properties.rotation.y.toFixed(4); + elRotationZ.value = properties.rotation.z.toFixed(4); + + elLinearVelocityX.value = properties.velocity.x.toFixed(4); + elLinearVelocityY.value = properties.velocity.y.toFixed(4); + elLinearVelocityZ.value = properties.velocity.z.toFixed(4); + elLinearDamping.value = properties.damping.toFixed(2); + + elAngularVelocityX.value = (properties.angularVelocity.x * RADIANS_TO_DEGREES).toFixed(4); + elAngularVelocityY.value = (properties.angularVelocity.y * RADIANS_TO_DEGREES).toFixed(4); + elAngularVelocityZ.value = (properties.angularVelocity.z * RADIANS_TO_DEGREES).toFixed(4); + elAngularDamping.value = properties.angularDamping.toFixed(4); + + elRestitution.value = properties.restitution.toFixed(4); + elFriction.value = properties.friction.toFixed(4); + + elGravityX.value = properties.gravity.x.toFixed(4); + elGravityY.value = properties.gravity.y.toFixed(4); + elGravityZ.value = properties.gravity.z.toFixed(4); + + elAccelerationX.value = properties.acceleration.x.toFixed(4); + elAccelerationY.value = properties.acceleration.y.toFixed(4); + elAccelerationZ.value = properties.acceleration.z.toFixed(4); + + elDensity.value = properties.density.toFixed(4); + elCollisionless.checked = properties.collisionless; + elDynamic.checked = properties.dynamic; + + elCollideStatic.checked = properties.collidesWith.indexOf("static") > -1; + elCollideKinematic.checked = properties.collidesWith.indexOf("kinematic") > -1; + elCollideDynamic.checked = properties.collidesWith.indexOf("dynamic") > -1; + elCollideMyAvatar.checked = properties.collidesWith.indexOf("myAvatar") > -1; + elCollideOtherAvatar.checked = properties.collidesWith.indexOf("otherAvatar") > -1; + + elGrabbable.checked = properties.dynamic; + elWantsTrigger.checked = false; + elIgnoreIK.checked = false; + var parsedUserData = {} + try { + parsedUserData = JSON.parse(properties.userData); + + if ("grabbableKey" in parsedUserData) { + if ("grabbable" in parsedUserData["grabbableKey"]) { + elGrabbable.checked = parsedUserData["grabbableKey"].grabbable; + } + if ("wantsTrigger" in parsedUserData["grabbableKey"]) { + elWantsTrigger.checked = parsedUserData["grabbableKey"].wantsTrigger; + } + if ("ignoreIK" in parsedUserData["grabbableKey"]) { + elIgnoreIK.checked = parsedUserData["grabbableKey"].ignoreIK; + } + } + } catch(e) {} + + elCollisionSoundURL.value = properties.collisionSoundURL; + elLifetime.value = properties.lifetime; + elScriptURL.value = properties.script; + /* + FIXME: See FIXME for property-script-url. + elScriptTimestamp.value = properties.scriptTimestamp; + */ + elUserData.value = properties.userData; + setTextareaScrolling(elUserData); + + elHyperlinkHref.value = properties.href; + elDescription.value = properties.description; + + for (var i = 0; i < allSections.length; i++) { + for (var j = 0; j < allSections[i].length; j++) { + allSections[i][j].style.display = 'none'; + } + } + + for (var i = 0; i < elHyperlinkSections.length; i++) { + elHyperlinkSections[i].style.display = 'table'; + } + + if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere") { + for (var i = 0; i < elShapeSections.length; i++) { + elShapeSections[i].style.display = 'table'; + } + elShape.value = properties.shape; + setDropdownText(elShape); + + } else { + for (var i = 0; i < elShapeSections.length; i++) { + elShapeSections[i].style.display = 'none'; + } + } + + if (properties.type == "Shape" || properties.type == "Box" || properties.type == "Sphere" || properties.type == "ParticleEffect") { + for (var i = 0; i < elColorSections.length; i++) { + elColorSections[i].style.display = 'table'; + } + elColorRed.value = properties.color.red; + elColorGreen.value = properties.color.green; + elColorBlue.value = properties.color.blue; + elColor.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; + } else { + for (var i = 0; i < elColorSections.length; i++) { + elColorSections[i].style.display = 'none'; + } + } + + if (properties.type == "Model") { + for (var i = 0; i < elModelSections.length; i++) { + elModelSections[i].style.display = 'table'; + } + + elModelURL.value = properties.modelURL; + elShapeType.value = properties.shapeType; + setDropdownText(elShapeType); + elCompoundShapeURL.value = properties.compoundShapeURL; + elModelAnimationURL.value = properties.animation.url; + elModelAnimationPlaying.checked = properties.animation.running; + elModelAnimationFPS.value = properties.animation.fps; + elModelAnimationFrame.value = properties.animation.currentFrame; + elModelAnimationFirstFrame.value = properties.animation.firstFrame; + elModelAnimationLastFrame.value = properties.animation.lastFrame; + elModelAnimationLoop.checked = properties.animation.loop; + elModelAnimationHold.checked = properties.animation.hold; + elModelTextures.value = properties.textures; + setTextareaScrolling(elModelTextures); + elModelOriginalTextures.value = properties.originalTextures; + setTextareaScrolling(elModelOriginalTextures); + } else if (properties.type == "Web") { + for (var i = 0; i < elWebSections.length; i++) { + elWebSections[i].style.display = 'table'; + } + for (var i = 0; i < elHyperlinkSections.length; i++) { + elHyperlinkSections[i].style.display = 'none'; + } + + elWebSourceURL.value = properties.sourceUrl; + } else if (properties.type == "Text") { + for (var i = 0; i < elTextSections.length; i++) { + elTextSections[i].style.display = 'table'; + } + + elTextText.value = properties.text; + elTextLineHeight.value = properties.lineHeight.toFixed(4); + elTextFaceCamera = properties.faceCamera; + elTextTextColor.style.backgroundColor = "rgb(" + properties.textColor.red + "," + properties.textColor.green + "," + properties.textColor.blue + ")"; + elTextTextColorRed.value = properties.textColor.red; + elTextTextColorGreen.value = properties.textColor.green; + elTextTextColorBlue.value = properties.textColor.blue; + elTextBackgroundColorRed.value = properties.backgroundColor.red; + elTextBackgroundColorGreen.value = properties.backgroundColor.green; + elTextBackgroundColorBlue.value = properties.backgroundColor.blue; + } else if (properties.type == "Light") { + for (var i = 0; i < elLightSections.length; i++) { + elLightSections[i].style.display = 'table'; + } + + elLightSpotLight.checked = properties.isSpotlight; + + elLightColor.style.backgroundColor = "rgb(" + properties.color.red + "," + properties.color.green + "," + properties.color.blue + ")"; + elLightColorRed.value = properties.color.red; + elLightColorGreen.value = properties.color.green; + elLightColorBlue.value = properties.color.blue; + + elLightIntensity.value = properties.intensity.toFixed(1); + elLightFalloffRadius.value = properties.falloffRadius.toFixed(1); + elLightExponent.value = properties.exponent.toFixed(2); + elLightCutoff.value = properties.cutoff.toFixed(2); + } else if (properties.type == "Zone") { + for (var i = 0; i < elZoneSections.length; i++) { + elZoneSections[i].style.display = 'table'; + } + + elZoneStageSunModelEnabled.checked = properties.stage.sunModelEnabled; + elZoneKeyLightColor.style.backgroundColor = "rgb(" + properties.keyLight.color.red + "," + properties.keyLight.color.green + "," + properties.keyLight.color.blue + ")"; + elZoneKeyLightColorRed.value = properties.keyLight.color.red; + elZoneKeyLightColorGreen.value = properties.keyLight.color.green; + elZoneKeyLightColorBlue.value = properties.keyLight.color.blue; + elZoneKeyLightIntensity.value = properties.keyLight.intensity.toFixed(2); + elZoneKeyLightAmbientIntensity.value = properties.keyLight.ambientIntensity.toFixed(2); + elZoneKeyLightDirectionX.value = properties.keyLight.direction.x.toFixed(2); + elZoneKeyLightDirectionY.value = properties.keyLight.direction.y.toFixed(2); + elZoneKeyLightAmbientURL.value = properties.keyLight.ambientURL; + + + elZoneStageLatitude.value = properties.stage.latitude.toFixed(2); + elZoneStageLongitude.value = properties.stage.longitude.toFixed(2); + elZoneStageAltitude.value = properties.stage.altitude.toFixed(2); + elZoneStageAutomaticHourDay.checked = properties.stage.automaticHourDay; + elZoneStageDay.value = properties.stage.day; + elZoneStageHour.value = properties.stage.hour; + elShapeType.value = properties.shapeType; + elCompoundShapeURL.value = properties.compoundShapeURL; + + elZoneBackgroundMode.value = properties.backgroundMode; + setDropdownText(elZoneBackgroundMode); + + elZoneSkyboxColor.style.backgroundColor = "rgb(" + properties.skybox.color.red + "," + properties.skybox.color.green + "," + properties.skybox.color.blue + ")"; + elZoneSkyboxColorRed.value = properties.skybox.color.red; + elZoneSkyboxColorGreen.value = properties.skybox.color.green; + elZoneSkyboxColorBlue.value = properties.skybox.color.blue; + elZoneSkyboxURL.value = properties.skybox.url; + + elZoneFlyingAllowed.checked = properties.flyingAllowed; + elZoneGhostingAllowed.checked = properties.ghostingAllowed; + + showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox'); + } else if (properties.type == "PolyVox") { + for (var i = 0; i < elPolyVoxSections.length; i++) { + elPolyVoxSections[i].style.display = 'table'; + } + + elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); + elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); + elVoxelVolumeSizeZ.value = properties.voxelVolumeSize.z.toFixed(2); + elVoxelSurfaceStyle.value = properties.voxelSurfaceStyle; + setDropdownText(elVoxelSurfaceStyle); + elXTextureURL.value = properties.xTextureURL; + elYTextureURL.value = properties.yTextureURL; + elZTextureURL.value = properties.zTextureURL; + } + + var activeElement = document.activeElement; + + if(typeof activeElement.select!=="undefined"){ + activeElement.select(); + } + } + } + }); + } + + elLocked.addEventListener('change', createEmitCheckedPropertyUpdateFunction('locked')); + elName.addEventListener('change', createEmitTextPropertyUpdateFunction('name')); + elHyperlinkHref.addEventListener('change', createEmitTextPropertyUpdateFunction('href')); + elDescription.addEventListener('change', createEmitTextPropertyUpdateFunction('description')); + elVisible.addEventListener('change', createEmitCheckedPropertyUpdateFunction('visible')); + + var positionChangeFunction = createEmitVec3PropertyUpdateFunction( + 'position', elPositionX, elPositionY, elPositionZ); + elPositionX.addEventListener('change', positionChangeFunction); + elPositionY.addEventListener('change', positionChangeFunction); + elPositionZ.addEventListener('change', positionChangeFunction); + + var dimensionsChangeFunction = createEmitVec3PropertyUpdateFunction( + 'dimensions', elDimensionsX, elDimensionsY, elDimensionsZ); + elDimensionsX.addEventListener('change', dimensionsChangeFunction); + elDimensionsY.addEventListener('change', dimensionsChangeFunction); + elDimensionsZ.addEventListener('change', dimensionsChangeFunction); + + elParentID.addEventListener('change', createEmitTextPropertyUpdateFunction('parentID')); + elParentJointIndex.addEventListener('change', createEmitNumberPropertyUpdateFunction('parentJointIndex')); + + var registrationChangeFunction = createEmitVec3PropertyUpdateFunction( + 'registrationPoint', elRegistrationX, elRegistrationY, elRegistrationZ); + elRegistrationX.addEventListener('change', registrationChangeFunction); + elRegistrationY.addEventListener('change', registrationChangeFunction); + elRegistrationZ.addEventListener('change', registrationChangeFunction); + + var rotationChangeFunction = createEmitVec3PropertyUpdateFunction( + 'rotation', elRotationX, elRotationY, elRotationZ); + elRotationX.addEventListener('change', rotationChangeFunction); + elRotationY.addEventListener('change', rotationChangeFunction); + elRotationZ.addEventListener('change', rotationChangeFunction); + + var velocityChangeFunction = createEmitVec3PropertyUpdateFunction( + 'velocity', elLinearVelocityX, elLinearVelocityY, elLinearVelocityZ); + elLinearVelocityX.addEventListener('change', velocityChangeFunction); + elLinearVelocityY.addEventListener('change', velocityChangeFunction); + elLinearVelocityZ.addEventListener('change', velocityChangeFunction); + elLinearDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('damping')); + + var angularVelocityChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier( + 'angularVelocity', elAngularVelocityX, elAngularVelocityY, elAngularVelocityZ, DEGREES_TO_RADIANS); + elAngularVelocityX.addEventListener('change', angularVelocityChangeFunction); + elAngularVelocityY.addEventListener('change', angularVelocityChangeFunction); + elAngularVelocityZ.addEventListener('change', angularVelocityChangeFunction); + elAngularDamping.addEventListener('change', createEmitNumberPropertyUpdateFunction('angularDamping')); + + elRestitution.addEventListener('change', createEmitNumberPropertyUpdateFunction('restitution')); + elFriction.addEventListener('change', createEmitNumberPropertyUpdateFunction('friction')); + + var gravityChangeFunction = createEmitVec3PropertyUpdateFunction( + 'gravity', elGravityX, elGravityY, elGravityZ); + elGravityX.addEventListener('change', gravityChangeFunction); + elGravityY.addEventListener('change', gravityChangeFunction); + elGravityZ.addEventListener('change', gravityChangeFunction); + + var accelerationChangeFunction = createEmitVec3PropertyUpdateFunction( + 'acceleration', elAccelerationX, elAccelerationY, elAccelerationZ); + elAccelerationX.addEventListener('change', accelerationChangeFunction); + elAccelerationY.addEventListener('change', accelerationChangeFunction); + elAccelerationZ.addEventListener('change', accelerationChangeFunction); + + elDensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('density')); + elCollisionless.addEventListener('change', createEmitCheckedPropertyUpdateFunction('collisionless')); + elDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('dynamic')); + + elCollideDynamic.addEventListener('change', function() { + updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideDynamic, 'dynamic'); + }); + + elCollideKinematic.addEventListener('change', function() { + updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideKinematic, 'kinematic'); + }); + + elCollideStatic.addEventListener('change', function() { + updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideStatic, 'static'); + }); + elCollideMyAvatar.addEventListener('change', function() { + updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideMyAvatar, 'myAvatar'); + }); + elCollideOtherAvatar.addEventListener('change', function() { + updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideOtherAvatar, 'otherAvatar'); + }); + + elGrabbable.addEventListener('change', function() { + userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, properties.dynamic); + }); + elWantsTrigger.addEventListener('change', function() { + userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false); + }); + elIgnoreIK.addEventListener('change', function() { + userDataChanger("grabbableKey", "ignoreIK", elIgnoreIK, elUserData, false); + }); + + elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL')); + + elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime')); + elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script')); + /* + FIXME: See FIXME for property-script-url. + elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp')); + */ + elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); + + var colorChangeFunction = createEmitColorPropertyUpdateFunction( + 'color', elColorRed, elColorGreen, elColorBlue); + elColorRed.addEventListener('change', colorChangeFunction); + elColorGreen.addEventListener('change', colorChangeFunction); + elColorBlue.addEventListener('change', colorChangeFunction); + colorPickers.push($('#property-color').colpick({ + colorScheme: 'dark', + layout: 'hex', + color: '000000', + onShow: function (colpick) { + $('#property-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); + emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); + } + })); + + elLightSpotLight.addEventListener('change', createEmitCheckedPropertyUpdateFunction('isSpotlight')); + + var lightColorChangeFunction = createEmitColorPropertyUpdateFunction( + 'color', elLightColorRed, elLightColorGreen, elLightColorBlue); + elLightColorRed.addEventListener('change', lightColorChangeFunction); + elLightColorGreen.addEventListener('change', lightColorChangeFunction); + elLightColorBlue.addEventListener('change', lightColorChangeFunction); + colorPickers.push($('#property-light-color').colpick({ + colorScheme: 'dark', + layout: 'hex', + color: '000000', + onShow: function (colpick) { + $('#property-light-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-light-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#' + hex); + $(el).colpickHide(); + emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b); + } + })); + + elLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('intensity', 1)); + elLightFalloffRadius.addEventListener('change', createEmitNumberPropertyUpdateFunction('falloffRadius', 1)); + elLightExponent.addEventListener('change', createEmitNumberPropertyUpdateFunction('exponent', 2)); + elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff', 2)); + + elShape.addEventListener('change', createEmitTextPropertyUpdateFunction('shape')); + + elWebSourceURL.addEventListener('change', createEmitTextPropertyUpdateFunction('sourceUrl')); + + elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL')); + elShapeType.addEventListener('change', createEmitTextPropertyUpdateFunction('shapeType')); + elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL')); + + elModelAnimationURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('animation', 'url')); + elModelAnimationPlaying.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation','running')); + elModelAnimationFPS.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation','fps')); + elModelAnimationFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'currentFrame')); + elModelAnimationFirstFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'firstFrame')); + elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame')); + elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop')); + elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold')); + + elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures')); + + elTextText.addEventListener('change', createEmitTextPropertyUpdateFunction('text')); + elTextFaceCamera.addEventListener('change', createEmitCheckedPropertyUpdateFunction('faceCamera')); + elTextLineHeight.addEventListener('change', createEmitNumberPropertyUpdateFunction('lineHeight')); + var textTextColorChangeFunction = createEmitColorPropertyUpdateFunction( + 'textColor', elTextTextColorRed, elTextTextColorGreen, elTextTextColorBlue); + elTextTextColorRed.addEventListener('change', textTextColorChangeFunction); + elTextTextColorGreen.addEventListener('change', textTextColorChangeFunction); + elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction); + colorPickers.push($('#property-text-text-color').colpick({ + colorScheme:'dark', + layout:'hex', + color: '000000', + onShow: function (colpick) { + $('#property-text-text-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-text-text-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#'+hex); + $(el).colpickHide(); + $(el).attr('active', 'false'); + emitColorPropertyUpdate('textColor', rgb.r, rgb.g, rgb.b); + } + })); + + var textBackgroundColorChangeFunction = createEmitColorPropertyUpdateFunction( + 'backgroundColor', elTextBackgroundColorRed, elTextBackgroundColorGreen, elTextBackgroundColorBlue); + elTextBackgroundColorRed.addEventListener('change', textBackgroundColorChangeFunction); + elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction); + elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction); + colorPickers.push($('#property-text-background-color').colpick({ + colorScheme:'dark', + layout:'hex', + color:'000000', + onShow: function (colpick) { + $('#property-text-background-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-text-background-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#'+hex); + $(el).colpickHide(); + emitColorPropertyUpdate('backgroundColor', rgb.r, rgb.g, rgb.b); + } + })); + + elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled')); + colorPickers.push($('#property-zone-key-light-color').colpick({ + colorScheme:'dark', + layout:'hex', + color:'000000', + onShow: function (colpick) { + $('#property-zone-key-light-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-zone-key-light-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#'+hex); + $(el).colpickHide(); + emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'keyLight'); + } + })); + var zoneKeyLightColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('keyLight','color', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue); + elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction); + elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction); + elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction); + elZoneKeyLightIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','intensity')); + elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('keyLight','ambientIntensity')); + elZoneKeyLightAmbientURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('keyLight','ambientURL')); + var zoneKeyLightDirectionChangeFunction = createEmitGroupVec3PropertyUpdateFunction('keyLight','direction', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY); + elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction); + elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction); + + elZoneStageLatitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','latitude')); + elZoneStageLongitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','longitude')); + elZoneStageAltitude.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','altitude')); + elZoneStageAutomaticHourDay.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','automaticHourDay')); + elZoneStageDay.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','day')); + elZoneStageHour.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('stage','hour')); + + + elZoneBackgroundMode.addEventListener('change', createEmitTextPropertyUpdateFunction('backgroundMode')); + var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox','color', + elZoneSkyboxColorRed, elZoneSkyboxColorGreen, elZoneSkyboxColorBlue); + elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction); + elZoneSkyboxColorGreen.addEventListener('change', zoneSkyboxColorChangeFunction); + elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction); + colorPickers.push($('#property-zone-skybox-color').colpick({ + colorScheme:'dark', + layout:'hex', + color:'000000', + onShow: function (colpick) { + $('#property-zone-skybox-color').attr('active', 'true'); + }, + onHide: function (colpick) { + $('#property-zone-skybox-color').attr('active', 'false'); + }, + onSubmit: function (hsb, hex, rgb, el) { + $(el).css('background-color', '#'+hex); + $(el).colpickHide(); + emitColorPropertyUpdate('color', rgb.r, rgb.g, rgb.b, 'skybox'); + } + })); + + elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox','url')); + + elZoneFlyingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('flyingAllowed')); + elZoneGhostingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('ghostingAllowed')); + + var voxelVolumeSizeChangeFunction = createEmitVec3PropertyUpdateFunction( + 'voxelVolumeSize', elVoxelVolumeSizeX, elVoxelVolumeSizeY, elVoxelVolumeSizeZ); + elVoxelVolumeSizeX.addEventListener('change', voxelVolumeSizeChangeFunction); + elVoxelVolumeSizeY.addEventListener('change', voxelVolumeSizeChangeFunction); + elVoxelVolumeSizeZ.addEventListener('change', voxelVolumeSizeChangeFunction); + elVoxelSurfaceStyle.addEventListener('change', createEmitTextPropertyUpdateFunction('voxelSurfaceStyle')); + elXTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('xTextureURL')); + elYTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('yTextureURL')); + elZTextureURL.addEventListener('change', createEmitTextPropertyUpdateFunction('zTextureURL')); + + elMoveSelectionToGrid.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "moveSelectionToGrid", + })); + }); + elMoveAllToGrid.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "moveAllToGrid", + })); + }); + elResetToNaturalDimensions.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "resetToNaturalDimensions", + })); + }); + elRescaleDimensionsButton.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "rescaleDimensions", + percentage: parseInt(elRescaleDimensionsPct.value), + })); + }); + /* + FIXME: See FIXME for property-script-url. + elReloadScriptButton.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "reloadScript" + })); + }); + */ + elPreviewCameraButton.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "previewCamera" + })); + }); + + window.onblur = function() { + // Fake a change event + var ev = document.createEvent("HTMLEvents"); + ev.initEvent("change", true, true); + document.activeElement.dispatchEvent(ev); + } + + // For input and textarea elements, select all of the text on focus + // WebKit-based browsers, such as is used with QWebView, have a quirk + // where the mouseup event comes after the focus event, causing the + // text to be deselected immediately after selecting all of the text. + // To make this work we block the first mouseup event after the elements + // received focus. If we block all mouseup events the user will not + // be able to click within the selected text. + // We also check to see if the value has changed to make sure we aren't + // blocking a mouse-up event when clicking on an input spinner. + var els = document.querySelectorAll("input, textarea"); + for (var i = 0; i < els.length; i++) { + var clicked = false; + var originalText; + els[i].onfocus = function(e) { + originalText = this.value; + this.select(); + clicked = false; + }; + els[i].onmouseup = function(e) { + if (!clicked && originalText == this.value) { + e.preventDefault(); + } + clicked = true; + }; + } + }); + + // Collapsible sections + var elCollapsible = document.getElementsByClassName("section-header"); + + var toggleCollapsedEvent = function (event) { + var element = event.target; + if (element.nodeName !== "DIV") { + element = element.parentNode; + } + var isCollapsed = element.getAttribute("collapsed") !== "true"; + element.setAttribute("collapsed", isCollapsed ? "true" : "false"); + element.getElementsByTagName("span")[0].textContent = isCollapsed ? "L" : "M"; + }; + + for (var i = 0, length = elCollapsible.length; i < length; i++) { + var element = elCollapsible[i]; + element.addEventListener("click", toggleCollapsedEvent, true); + }; + + + // Textarea scrollbars + var elTextareas = document.getElementsByTagName("TEXTAREA"); + + var textareaOnChangeEvent = function (event) { + setTextareaScrolling(event.target); + } + + for (var i = 0, length = elTextareas.length; i < length; i++) { + var element = elTextareas[i]; + setTextareaScrolling(element); + element.addEventListener("input", textareaOnChangeEvent, false); + element.addEventListener("change", textareaOnChangeEvent, false); + /* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize + event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */ + element.addEventListener("mouseup", textareaOnChangeEvent, false); + }; + + // Dropdowns + // For each dropdown the following replacement is created in place of the oriringal dropdown... + // Structure created: + //
+ //
display textcarat
+ //
+ //