From fc47e6799059af018053a71dd13cac172c5e7297 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 16 Feb 2018 13:56:59 -0800 Subject: [PATCH 01/85] Merging with master --- libraries/render-utils/src/AntialiasingEffect.cpp | 6 +++--- libraries/render-utils/src/VelocityBufferPass.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index 7bc5328100..eb1b98aa1f 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -202,7 +202,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { if (!_antialiasingPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(taa_frag)); + auto ps = taa_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -233,7 +233,7 @@ const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { if (!_blendPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(fxaa_blend_frag)); + auto ps = fxaa_blend_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -254,7 +254,7 @@ const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { const gpu::PipelinePointer& Antialiasing::getDebugBlendPipeline() { if (!_debugBlendPipeline) { auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(taa_blend_frag)); + auto ps = taa_blend_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; diff --git a/libraries/render-utils/src/VelocityBufferPass.cpp b/libraries/render-utils/src/VelocityBufferPass.cpp index 26dc1c6848..4f25604798 100644 --- a/libraries/render-utils/src/VelocityBufferPass.cpp +++ b/libraries/render-utils/src/VelocityBufferPass.cpp @@ -147,7 +147,7 @@ void VelocityBufferPass::run(const render::RenderContextPointer& renderContext, const gpu::PipelinePointer& VelocityBufferPass::getCameraMotionPipeline() { if (!_cameraMotionPipeline) { auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(velocityBuffer_cameraMotion_frag)); + auto ps = velocityBuffer_cameraMotion_frag::getShader(); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; From 27ba9172b8e9d19f31caddbc935181f8f9072518 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 16 Feb 2018 17:20:20 -0800 Subject: [PATCH 02/85] identifying the bug with defered transform in hmd and trying to apply fix for taa --- libraries/render-utils/src/DeferredFrameTransform.cpp | 2 ++ libraries/render-utils/src/DeferredFrameTransform.h | 2 ++ libraries/render-utils/src/DeferredTransform.slh | 9 +++++++++ .../render-utils/src/velocityBuffer_cameraMotion.slf | 5 +++-- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/DeferredFrameTransform.cpp b/libraries/render-utils/src/DeferredFrameTransform.cpp index cee5786847..781161a8af 100644 --- a/libraries/render-utils/src/DeferredFrameTransform.cpp +++ b/libraries/render-utils/src/DeferredFrameTransform.cpp @@ -48,6 +48,7 @@ void DeferredFrameTransform::update(RenderArgs* args) { frameTransformBuffer.projection[0] = frameTransformBuffer.projectionMono; frameTransformBuffer.stereoInfo = glm::vec4(0.0f, (float)args->_viewport.z, 0.0f, 0.0f); frameTransformBuffer.invpixelInfo = glm::vec4(1.0f / args->_viewport.z, 1.0f / args->_viewport.w, 0.0f, 0.0f); + frameTransformBuffer.invProjection[0] = glm::inverse(frameTransformBuffer.projection[0]); } else { mat4 projMats[2]; @@ -59,6 +60,7 @@ void DeferredFrameTransform::update(RenderArgs* args) { // Compose the mono Eye space to Stereo clip space Projection Matrix auto sideViewMat = projMats[i] * eyeViews[i]; frameTransformBuffer.projection[i] = sideViewMat; + frameTransformBuffer.invProjection[i] = glm::inverse(sideViewMat); } frameTransformBuffer.stereoInfo = glm::vec4(1.0f, (float)(args->_viewport.z >> 1), 0.0f, 1.0f); diff --git a/libraries/render-utils/src/DeferredFrameTransform.h b/libraries/render-utils/src/DeferredFrameTransform.h index a90effe053..0b5cb6a989 100644 --- a/libraries/render-utils/src/DeferredFrameTransform.h +++ b/libraries/render-utils/src/DeferredFrameTransform.h @@ -45,6 +45,8 @@ protected: glm::vec4 stereoInfo{ 0.0 }; // Mono proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space glm::mat4 projection[2]; + // Inverse proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space + glm::mat4 invProjection[2]; // THe mono projection for sure glm::mat4 projectionMono; // Inv View matrix from eye space (mono) to world space diff --git a/libraries/render-utils/src/DeferredTransform.slh b/libraries/render-utils/src/DeferredTransform.slh index 25d352387f..f1765978eb 100644 --- a/libraries/render-utils/src/DeferredTransform.slh +++ b/libraries/render-utils/src/DeferredTransform.slh @@ -31,6 +31,7 @@ struct DeferredFrameTransform { vec4 _depthInfo; vec4 _stereoInfo; mat4 _projection[2]; + mat4 _invProjection[2]; mat4 _projectionMono; mat4 _viewInverse; mat4 _view; @@ -128,6 +129,14 @@ vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) { return vec3(Xe, Ye, Zeye); } +vec3 evalEyePositionFromZdb(int side, float Zdb, vec2 texcoord) { + // compute the view space position using the depth + vec3 clipPos; + clipPos.xyz = vec3(texcoord.xy, Zdb) * 2.0 - 1.0; + vec4 eyePos = frameTransform._invProjection[side] * vec4(clipPos.xyz, 1.0); + return eyePos.xyz / eyePos.w; +} + ivec2 getPixelPosTexcoordPosAndSide(in vec2 glFragCoord, out ivec2 pixelPos, out vec2 texcoordPos, out ivec4 stereoSide) { ivec2 fragPos = ivec2(glFragCoord.xy); diff --git a/libraries/render-utils/src/velocityBuffer_cameraMotion.slf b/libraries/render-utils/src/velocityBuffer_cameraMotion.slf index 7488f42535..00d5cdfb3d 100644 --- a/libraries/render-utils/src/velocityBuffer_cameraMotion.slf +++ b/libraries/render-utils/src/velocityBuffer_cameraMotion.slf @@ -26,14 +26,15 @@ void main(void) { ivec2 framePixelPos = getPixelPosTexcoordPosAndSide(gl_FragCoord.xy, pixelPos, texcoordPos, stereoSide); float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x; - float Zeye = evalZeyeFromZdb(Zdb); + // float Zeye = evalZeyeFromZdb(Zdb); /* if (Zeye <= -getPosLinearDepthFar()) { outFragColor = vec4(0.5, 0.5, 0.0, 0.0); return; }*/ // The position of the pixel fragment in Eye space then in world space - vec3 eyePos = evalEyePositionFromZeye(stereoSide.x, Zeye, texcoordPos); + //vec3 eyePos = evalEyePositionFromZeye(stereoSide.x, Zeye, texcoordPos); + vec3 eyePos = evalEyePositionFromZdb(stereoSide.x, Zdb, texcoordPos); vec3 worldPos = (frameTransform._viewInverse * cameraCorrection._correction * vec4(eyePos, 1.0)).xyz; vec3 prevEyePos = (cameraCorrection._prevCorrectionInverse * frameTransform._prevView * vec4(worldPos, 1.0)).xyz; From 6d02aa064a9b531c6d537f414d5fa9d425db5575 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 13 Mar 2018 10:53:21 -0700 Subject: [PATCH 03/85] REmove any change --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 14db81d01e..df91e0ca7b 100644 --- a/.gitignore +++ b/.gitignore @@ -78,10 +78,8 @@ TAGS node_modules npm-debug.log - # ignore qmlc files generated from qml as cache *.qmlc - # Android studio files *___jb_old___ From c237e34b7119467c17a0bea75228ecef45d0abe9 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 28 Mar 2018 17:47:20 -0700 Subject: [PATCH 04/85] Adding script tools --- .../utilities/render/inspectEngine.js | 39 +++++++++++++++++++ .../utilities/render/inspectEngine.qml | 19 +++++++++ 2 files changed, 58 insertions(+) create mode 100644 scripts/developer/utilities/render/inspectEngine.js create mode 100644 scripts/developer/utilities/render/inspectEngine.qml diff --git a/scripts/developer/utilities/render/inspectEngine.js b/scripts/developer/utilities/render/inspectEngine.js new file mode 100644 index 0000000000..7abce8b477 --- /dev/null +++ b/scripts/developer/utilities/render/inspectEngine.js @@ -0,0 +1,39 @@ +(function() { // BEGIN LOCAL_SCOPE + +function traverse(root, functor, depth) { + var subs = root.findChildren(/.*/) + depth++; + for (var i = 0; i Date: Wed, 28 Mar 2018 18:27:21 -0700 Subject: [PATCH 05/85] i dont't know --- .../utilities/render/inspectEngine.js | 22 ++++++++++++++----- .../utilities/render/inspectEngine.qml | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/scripts/developer/utilities/render/inspectEngine.js b/scripts/developer/utilities/render/inspectEngine.js index 7abce8b477..82231b991e 100644 --- a/scripts/developer/utilities/render/inspectEngine.js +++ b/scripts/developer/utilities/render/inspectEngine.js @@ -1,17 +1,27 @@ (function() { // BEGIN LOCAL_SCOPE -function traverse(root, functor, depth) { +function task_traverse(root, functor, depth) { var subs = root.findChildren(/.*/) depth++; for (var i = 0; i Date: Wed, 28 Nov 2018 22:37:11 -0800 Subject: [PATCH 06/85] REmove cruft --- .../utilities/render/inspectEngine.js | 49 ------------------- .../utilities/render/inspectEngine.qml | 20 -------- 2 files changed, 69 deletions(-) delete mode 100644 scripts/developer/utilities/render/inspectEngine.js delete mode 100644 scripts/developer/utilities/render/inspectEngine.qml diff --git a/scripts/developer/utilities/render/inspectEngine.js b/scripts/developer/utilities/render/inspectEngine.js deleted file mode 100644 index 82231b991e..0000000000 --- a/scripts/developer/utilities/render/inspectEngine.js +++ /dev/null @@ -1,49 +0,0 @@ -(function() { // BEGIN LOCAL_SCOPE - -function task_traverse(root, functor, depth) { - var subs = root.findChildren(/.*/) - depth++; - for (var i = 0; i Date: Fri, 30 Nov 2018 00:42:49 -0800 Subject: [PATCH 07/85] exploring better stereo drawcall techniques --- .../hmd/DebugHmdDisplayPlugin.cpp | 5 +++ .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 8 +++- .../gpu-gl-common/src/gpu/gl/GLBackend.h | 10 ++++- .../src/gpu/gl/GLBackendTransform.cpp | 42 ++++++++++++++++++- libraries/gpu/src/gpu/Transform.slh | 9 +++- libraries/shaders/headers/450/header.glsl | 1 + tools/shadergen.py | 4 ++ 7 files changed, 73 insertions(+), 6 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp index 40063652c8..3de3e590b9 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp @@ -26,6 +26,8 @@ bool DebugHmdDisplayPlugin::isSupported() const { void DebugHmdDisplayPlugin::resetSensors() { _currentRenderFrameInfo.renderPose = glm::mat4(); // identity + _currentRenderFrameInfo.renderPose = glm::translate(glm::mat4(), glm::vec3(0.0f, 1.76f, 0.0f)); + } bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) { @@ -35,6 +37,8 @@ bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) { // FIXME simulate head movement //_currentRenderFrameInfo.renderPose = ; //_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose; + _currentRenderFrameInfo.renderPose = glm::translate(glm::mat4(), glm::vec3(0.0f, 1.76f, 0.0f)); + _currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose; withNonPresentThreadLock([&] { _frameInfos[frameIndex] = _currentRenderFrameInfo; @@ -71,6 +75,7 @@ bool DebugHmdDisplayPlugin::internalActivate() { _eyeOffsets[1][3] = vec4{ 0.0327499993, 0.0, -0.0149999997, 1.0 }; _renderTargetSize = { 3024, 1680 }; _cullingProjection = _eyeProjections[0]; + // This must come after the initialization, so that the values calculated // above are available during the customizeContext call (when not running // in threaded present mode) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index c1ce05c18b..00373eb196 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -410,20 +410,24 @@ void GLBackend::render(const Batch& batch) { renderPassTransfer(batch); } -#ifdef GPU_STEREO_DRAWCALL_INSTANCED +//#ifdef GPU_STEREO_DRAWCALL_INSTANCED +#ifdef GPU_STEREO_VIEWPORT_CLIPPED if (_stereo.isStereo()) { glEnable(GL_CLIP_DISTANCE0); } #endif +//#endif { PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render"); renderPassDraw(batch); } -#ifdef GPU_STEREO_DRAWCALL_INSTANCED +//#ifdef GPU_STEREO_DRAWCALL_INSTANCED +#ifdef GPU_STEREO_VIEWPORT_CLIPPED if (_stereo.isStereo()) { glDisable(GL_CLIP_DISTANCE0); } #endif +//#endif // Restore the saved stereo state for the next batch _stereo._enable = savedStereo; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 37dde5b08e..8a459c86ca 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -36,7 +36,8 @@ #define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE #else //#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER -#define GPU_STEREO_TECHNIQUE_INSTANCED +//#define GPU_STEREO_TECHNIQUE_INSTANCED +#define GPU_STEREO_TECHNIQUE_INSTANCED_MULTIVIEWPORT #endif // Let these be configured by the one define picked above @@ -51,6 +52,13 @@ #ifdef GPU_STEREO_TECHNIQUE_INSTANCED #define GPU_STEREO_DRAWCALL_INSTANCED +#define GPU_STEREO_VIEWPORT_CLIPPED +#define GPU_STEREO_CAMERA_BUFFER +#endif + +#ifdef GPU_STEREO_TECHNIQUE_INSTANCED_MULTIVIEWPORT +#define GPU_STEREO_DRAWCALL_INSTANCED +#define GPU_STEREO_MULTI_VIEWPORT #define GPU_STEREO_CAMERA_BUFFER #endif diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp index 2c2a4e254c..caa194764d 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp @@ -39,6 +39,45 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) #ifdef GPU_STEREO_DRAWCALL_INSTANCED { + #ifdef GPU_STEREO_MULTI_VIEWPORT + ivec4& vp = _transform._viewport; + auto sideWidth = vp.z / 2; + + vec4 leftRight[3]; + + // Mono + leftRight[0] = vp; + + // Left side + leftRight[1] = vp; + leftRight[1].x = 0; + leftRight[1].z = sideWidth; + + // right side + leftRight[2] = vp; + leftRight[2].x = sideWidth; + leftRight[2].z = sideWidth; + + glViewportArrayv(0, 3, (float*)leftRight); + + // Where we assign the GL viewport + if (_stereo.isStereo()) { + + // ivec4 leftRight[3]; + // leftRight[0] = vp; + vp.z /= 2; + /* leftRight[1] = vp; // left side + leftRight[2] = vp; // right side + leftRight[2].x += vp.z; + glViewportArrayv(0, 3, (float*) leftRight); +*/ + if (_stereo._pass) { + vp.x += vp.z; + } + } else { + // glViewport(vp.x, vp.y, vp.z, vp.w); + } + #else ivec4& vp = _transform._viewport; glViewport(vp.x, vp.y, vp.z, vp.w); @@ -49,6 +88,7 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) vp.x += vp.z; } } + #endif } #else if (!_inRenderTransferPass && !isStereo()) { @@ -123,7 +163,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo if (_invalidView || _invalidProj || _invalidViewport) { size_t offset = _cameraUboSize * _cameras.size(); - Vec2 finalJitter = _projectionJitter / Vec2(framebufferSize); + Vec2 finalJitter = _projectionJitter / Vec2(framebufferSize); _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); if (stereo.isStereo()) { diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index 43205ba4c2..998204ce92 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -168,10 +168,15 @@ TransformObject getTransformObject() { vec2 eyeOffsetScale = vec2(-0.5, +0.5); uint eyeIndex = uint(_stereoSide); #ifndef GPU_GLES +#ifdef GPU_GL450 + gl_ViewportIndex = _stereoSide + 1; + // gl_ViewportIndex = 2 - _stereoSide; +#else gl_ClipDistance[0] = dot(<$clipPos$>, eyeClipEdge[eyeIndex]); #endif - float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w; - <$clipPos$>.x = newClipPosX; +#endif + // float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w; + // <$clipPos$>.x = newClipPosX; #endif #else diff --git a/libraries/shaders/headers/450/header.glsl b/libraries/shaders/headers/450/header.glsl index 6ce61b4378..a33b7634b1 100644 --- a/libraries/shaders/headers/450/header.glsl +++ b/libraries/shaders/headers/450/header.glsl @@ -1,4 +1,5 @@ #version 450 core +#extension GL_ARB_shader_viewport_layer_array : require #define GPU_GL450 #define GPU_SSBO_TRANSFORM_OBJECT #define BITFIELD int diff --git a/tools/shadergen.py b/tools/shadergen.py index ffbe1662ec..120fcc6bb7 100644 --- a/tools/shadergen.py +++ b/tools/shadergen.py @@ -190,6 +190,10 @@ def processCommand(line): if (dialect == '310es'): spirvCrossDialect = '320es' spirvCrossArgs = [spirvCrossExec, '--output', glslFile, spirvFile, '--version', spirvCrossDialect] if (dialect == '410'): spirvCrossArgs.append('--no-420pack-extension') + if (dialect == '450'): + spirvCrossArgs.append('--extension') + spirvCrossArgs.append('GL_ARB_shader_viewport_layer_array') + executeSubprocess(spirvCrossArgs) else: # This logic is necessary because cmake will agressively keep re-executing the shadergen From 9125f4ff41f43c5a1aaa0911a701cd5083a87a19 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 1 Mar 2019 17:45:33 -0800 Subject: [PATCH 08/85] Trying to hack the stereo for layered --- libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp | 3 +++ libraries/gpu/src/gpu/Transform.slh | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp index 130932238d..698a70af5a 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp @@ -47,6 +47,9 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) // Mono leftRight[0] = vp; + // adding this here as im doing Layered, force the first viewport here to be half of it + leftRight[0].x = 0; + leftRight[0].z = sideWidth; // Left side leftRight[1] = vp; diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index 9298ddcba4..484ad7ebd2 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -169,8 +169,10 @@ TransformObject getTransformObject() { uint eyeIndex = uint(_stereoSide); #if !defined(GPU_GLES) || (defined(HAVE_EXT_clip_cull_distance) && !defined(VULKAN)) #ifdef GPU_GL450 - gl_ViewportIndex = _stereoSide + 1; + /* gl_ViewportIndex = _stereoSide + 1; // gl_ViewportIndex = 2 - _stereoSide; + */// THIs is the layered version + gl_Layer = _stereoSide; #else gl_ClipDistance[0] = dot(<$clipPos$>, eyeClipEdge[eyeIndex]); #endif From fe23ef1485e4a152e339d6f40b53deab374bdb31 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Sun, 3 Mar 2019 23:35:40 -0800 Subject: [PATCH 09/85] Adding the prop library --- .../developer/utilities/lib/prop/Global.qml | 44 +++++++ .../utilities/lib/prop/PropColor.qml | 0 .../developer/utilities/lib/prop/PropEnum.qml | 110 ++++++++++++++++++ .../developer/utilities/lib/prop/PropItem.qml | 62 ++++++++++ .../utilities/lib/prop/PropLabel.qml | 25 ++++ .../utilities/lib/prop/PropScalar.qml | 71 +++++++++++ .../utilities/lib/prop/PropSplitter.qml | 21 ++++ .../developer/utilities/lib/prop/PropText.qml | 24 ++++ scripts/developer/utilities/lib/prop/qmldir | 8 ++ scripts/developer/utilities/render/luci.qml | 59 ++++++++++ scripts/developer/utilities/render/luci2.js | 13 +++ 11 files changed, 437 insertions(+) create mode 100644 scripts/developer/utilities/lib/prop/Global.qml create mode 100644 scripts/developer/utilities/lib/prop/PropColor.qml create mode 100644 scripts/developer/utilities/lib/prop/PropEnum.qml create mode 100644 scripts/developer/utilities/lib/prop/PropItem.qml create mode 100644 scripts/developer/utilities/lib/prop/PropLabel.qml create mode 100644 scripts/developer/utilities/lib/prop/PropScalar.qml create mode 100644 scripts/developer/utilities/lib/prop/PropSplitter.qml create mode 100644 scripts/developer/utilities/lib/prop/PropText.qml create mode 100644 scripts/developer/utilities/lib/prop/qmldir create mode 100644 scripts/developer/utilities/render/luci.qml create mode 100644 scripts/developer/utilities/render/luci2.js diff --git a/scripts/developer/utilities/lib/prop/Global.qml b/scripts/developer/utilities/lib/prop/Global.qml new file mode 100644 index 0000000000..be189e3c96 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/Global.qml @@ -0,0 +1,44 @@ +// +// Prop/Global.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls + + +Item { + HifiConstants { id: hifi } + id: root + + property real lineHeight: 32 + property real slimHeight: 24 + + property var color: hifi.colors.baseGray + property var colorBackHighlight: hifi.colors.baseGrayHighlight + property var colorBorderLight: hifi.colors.lightGray + property var colorBorderHighight: hifi.colors.blueHighlight + + property real fontSize: 12 + property var fontFamily: "Raleway" + property var fontWeight: Font.DemiBold + property var fontColor: hifi.colors.faintGray + + property var splitterRightWidthScale: 0.44 + property real splitterWidth: 4 + + property var labelTextAlign: Text.AlignRight + property var labelTextElide: Text.ElideMiddle + + property var valueAreaWidthScale: 0.3 * (1.0 - splitterRightWidthScale) + property var valueTextAlign: Text.AlignHCenter + property real valueBorderWidth: 1 + property real valueBorderRadius: 2 +} \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/PropColor.qml b/scripts/developer/utilities/lib/prop/PropColor.qml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/developer/utilities/lib/prop/PropEnum.qml b/scripts/developer/utilities/lib/prop/PropEnum.qml new file mode 100644 index 0000000000..fe6200d971 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/PropEnum.qml @@ -0,0 +1,110 @@ +// +// PropEnum.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +PropItem { + Global { id: global } + id: root + + property alias valueVar : valueCombo.currentIndex + property alias enums : valueCombo.model + + ComboBox { + id: valueCombo + + flat: true + + anchors.left: root.splitter.right + anchors.right: parent.right + anchors.verticalCenter: root.verticalCenter + height: global.slimHeight + + currentIndex: root.valueVarGetter() + onCurrentIndexChanged: { root.valueVarSetter(currentIndex); } + + delegate: ItemDelegate { + width: valueCombo.width + height: valueCombo.height + contentItem: PropText { + text: modelData + horizontalAlignment: global.valueTextAlign + } + background: Rectangle { + color:highlighted?global.colorBackHighlight:global.color; + } + highlighted: valueCombo.highlightedIndex === index + } + + indicator: Canvas { + id: canvas + x: valueCombo.width - width - valueCombo.rightPadding + y: valueCombo.topPadding + (valueCombo.availableHeight - height) / 2 + width: 12 + height: 8 + contextType: "2d" + + Connections { + target: valueCombo + onPressedChanged: canvas.requestPaint() + } + + onPaint: { + context.reset(); + context.moveTo(0, 0); + context.lineTo(width, 0); + context.lineTo(width / 2, height); + context.closePath(); + context.fillStyle = (valueCombo.pressed) ? global.colorBorderHighight : global.colorBorderLight; + context.fill(); + } + } + + contentItem: PropText { + leftPadding: 0 + rightPadding: valueCombo.indicator.width + valueCombo.spacing + + text: valueCombo.displayText + horizontalAlignment: global.valueTextAlign + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: global.color + border.color: valueCombo.popup.visible ? global.colorBorderHighight : global.colorBorderLight + border.width: global.valueBorderWidth + radius: global.valueBorderRadius + } + + popup: Popup { + y: valueCombo.height - 1 + width: valueCombo.width + implicitHeight: contentItem.implicitHeight + 2 + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: valueCombo.popup.visible ? valueCombo.delegateModel : null + currentIndex: valueCombo.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: global.color + border.color: global.colorBorderHighight + radius: global.valueBorderRadius + } + } + } +} diff --git a/scripts/developer/utilities/lib/prop/PropItem.qml b/scripts/developer/utilities/lib/prop/PropItem.qml new file mode 100644 index 0000000000..ee1e99a772 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/PropItem.qml @@ -0,0 +1,62 @@ +// +// PropItem.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +Item { + Global { id: global } + + id: root + + // Prop item is designed to author an object[property]: + property var object: NULL + property string property: "" + + // value is accessed through the "valueVarSetter" and "valueVarGetter" + // By default, these just go get or set the value from the object[property] + // + function defaultGet() { return root.object[root.property]; } + function defaultSet(value) { root.object[root.property] = value; } + property var valueVarSetter: defaultSet + property var valueVarGetter: defaultGet + + // PropItem is stretching horizontally accross its parent + // Fixed height + anchors.left: parent.left + anchors.right: parent.right + height: global.lineHeight + + + // LabelControl And SplitterControl are on the left side of the PropItem + property bool showLabel: true + property alias labelControl: labelControl + property alias label: labelControl.text + + property var labelAreaWidth: root.width * global.splitterRightWidthScale - global.splitterWidth + + PropText { + id: labelControl + text: root.label + enabled: root.showLabel + + anchors.left: root.left + anchors.verticalCenter: root.verticalCenter + width: labelAreaWidth + } + + property alias splitter: splitterControl + PropSplitter { + id: splitterControl + + anchors.left: labelControl.right + size: global.splitterWidth + } + +} diff --git a/scripts/developer/utilities/lib/prop/PropLabel.qml b/scripts/developer/utilities/lib/prop/PropLabel.qml new file mode 100644 index 0000000000..9dbeffe0ec --- /dev/null +++ b/scripts/developer/utilities/lib/prop/PropLabel.qml @@ -0,0 +1,25 @@ +// +// PropLabel.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +Label { + Global { id: global } + + color: global.fontColor + font.pixelSize: global.fontSize + font.family: global.fontFamily + font.weight: global.fontWeight + verticalAlignment: Text.AlignVCenter + horizontalAlignment: global.labelTextAlign + elide: global.labelTextElide +} + diff --git a/scripts/developer/utilities/lib/prop/PropScalar.qml b/scripts/developer/utilities/lib/prop/PropScalar.qml new file mode 100644 index 0000000000..29b42e2801 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/PropScalar.qml @@ -0,0 +1,71 @@ +// +// PropItem.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +import controlsUit 1.0 as HifiControls + +PropItem { + Global { id: global } + id: root + + // Scalar Prop + property bool integral: false + property var numDigits: 2 + + + property alias valueVar : sliderControl.value + property alias min: sliderControl.minimumValue + property alias max: sliderControl.maximumValue + + + + property bool showValue: true + + + signal valueChanged(real value) + + Component.onCompleted: { + valueVar = root.valueVarGetter(); + } + + PropLabel { + id: valueLabel + enabled: root.showValue + + anchors.left: root.splitter.right + anchors.verticalCenter: root.verticalCenter + width: root.width * global.valueAreaWidthScale + horizontalAlignment: global.valueTextAlign + height: global.slimHeight + + text: sliderControl.value.toFixed(root.integral ? 0 : root.numDigits) + + background: Rectangle { + color: global.color + border.color: global.colorBorderLight + border.width: global.valueBorderWidth + radius: global.valueBorderRadius + } + } + + HifiControls.Slider { + id: sliderControl + stepSize: root.integral ? 1.0 : 0.0 + anchors.left: valueLabel.right + anchors.right: root.right + anchors.rightMargin: 0 + anchors.verticalCenter: root.verticalCenter + + onValueChanged: { root.valueVarSetter(value) } + } + + +} diff --git a/scripts/developer/utilities/lib/prop/PropSplitter.qml b/scripts/developer/utilities/lib/prop/PropSplitter.qml new file mode 100644 index 0000000000..25f668a6eb --- /dev/null +++ b/scripts/developer/utilities/lib/prop/PropSplitter.qml @@ -0,0 +1,21 @@ +// +// PropSplitter.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +Item { + id: root + property real size + + width: size // Must be non-zero + height: size + + anchors.verticalCenter: parent.verticalCenter +} diff --git a/scripts/developer/utilities/lib/prop/PropText.qml b/scripts/developer/utilities/lib/prop/PropText.qml new file mode 100644 index 0000000000..b1669f3836 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/PropText.qml @@ -0,0 +1,24 @@ +// +// Prop/Text.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +Text { + Global { id: global } + + color: global.fontColor + font.pixelSize: global.fontSize + font.family: global.fontFamily + font.weight: global.fontWeight + verticalAlignment: Text.AlignVCenter + horizontalAlignment: global.labelTextAlign + elide: global.labelTextElide +} + diff --git a/scripts/developer/utilities/lib/prop/qmldir b/scripts/developer/utilities/lib/prop/qmldir new file mode 100644 index 0000000000..44e4889ab6 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/qmldir @@ -0,0 +1,8 @@ +PropGlobal 1.0 PropGlobal.qml +PropText 1.0 PropText.qml +PropLabel 1.0 PropLabel.qml +PropSplitter 1.0 PropSplitter.qml +PropItem 1.0 PropItem.qml +PropScalar 1.0 PropScalar.qml +PropEnum 1.0 PropEnum.qml +PropColor 1.0 PropColor.qml diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml new file mode 100644 index 0000000000..959a24e9be --- /dev/null +++ b/scripts/developer/utilities/render/luci.qml @@ -0,0 +1,59 @@ +// +// luci.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 + +import controlsUit 1.0 as HifiControls + +import "../lib/prop" as Prop + +Rectangle { + Prop.Global { id: prop;} + id: render; + anchors.fill: parent + color: prop.color; + + property var mainViewTask: Render.getConfig("RenderMainView") + + Column { + anchors.left: parent.left + anchors.right: parent.right + Repeater { + model: [ "Tone mapping exposure:ToneMapping:exposure:5.0:-5.0", + "Tone:ToneMapping:exposure:5.0:-5.0" + ] + Prop.PropScalar { + label: qsTr(modelData.split(":")[0]) + integral: false + object: render.mainViewTask.getConfig(modelData.split(":")[1]) + property: modelData.split(":")[2] + max: modelData.split(":")[3] + min: modelData.split(":")[4] + + anchors.left: parent.left + anchors.right: parent.right + } + } + Prop.PropEnum { + label: "Tone Curve" + object: render.mainViewTask.getConfig("ToneMapping") + property: "curve" + enums: [ + "RGB", + "SRGB", + "Reinhard", + "Filmic", + ] + anchors.left: parent.left + anchors.right: parent.right + } + } +} \ No newline at end of file diff --git a/scripts/developer/utilities/render/luci2.js b/scripts/developer/utilities/render/luci2.js new file mode 100644 index 0000000000..4aea49a059 --- /dev/null +++ b/scripts/developer/utilities/render/luci2.js @@ -0,0 +1,13 @@ +function openEngineTaskView() { + // Set up the qml ui + var qml = Script.resolvePath('luci.qml'); + var window = new OverlayWindow({ + title: 'luci qml', + source: qml, + width: 300, + height: 400 + }); + window.setPosition(200, 50); + //window.closed.connect(function() { Script.stop(); }); + } + openEngineTaskView(); \ No newline at end of file From 3d2614498be2de804e86d73527be5e26e88d3b7b Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 4 Mar 2019 18:07:02 -0800 Subject: [PATCH 10/85] ANd fuck --- .../developer/utilities/lib/prop/PropBool.qml | 35 +++++++ .../utilities/lib/prop/PropGroup.qml | 60 ++++++++++++ .../utilities/lib/prop/style/Global.qml | 44 +++++++++ .../utilities/lib/prop/style/PiComboBox.qml | 98 +++++++++++++++++++ .../utilities/lib/prop/style/PiLabel.qml | 25 +++++ .../utilities/lib/prop/style/PiSplitter.qml | 21 ++++ .../utilities/lib/prop/style/PiText.qml | 24 +++++ 7 files changed, 307 insertions(+) create mode 100644 scripts/developer/utilities/lib/prop/PropBool.qml create mode 100644 scripts/developer/utilities/lib/prop/PropGroup.qml create mode 100644 scripts/developer/utilities/lib/prop/style/Global.qml create mode 100644 scripts/developer/utilities/lib/prop/style/PiComboBox.qml create mode 100644 scripts/developer/utilities/lib/prop/style/PiLabel.qml create mode 100644 scripts/developer/utilities/lib/prop/style/PiSplitter.qml create mode 100644 scripts/developer/utilities/lib/prop/style/PiText.qml diff --git a/scripts/developer/utilities/lib/prop/PropBool.qml b/scripts/developer/utilities/lib/prop/PropBool.qml new file mode 100644 index 0000000000..e355398375 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/PropBool.qml @@ -0,0 +1,35 @@ +// +// PropBool.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import controlsUit 1.0 as HifiControls + +PropItem { + Global { id: global } + id: root + + property alias valueVar : checkboxControl.checked + + Component.onCompleted: { + valueVar = root.valueVarGetter(); + } + + HifiControls.CheckBox { + id: checkboxControl + + anchors.left: root.splitter.right + anchors.verticalCenter: root.verticalCenter + width: root.width * global.valueAreaWidthScale + height: global.slimHeight + + checked: root.valueVar + onCheckedChanged: { root.valueVarSetter(checked); } + } +} \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml new file mode 100644 index 0000000000..39294743b6 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -0,0 +1,60 @@ +// +// PropGroup.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +Item { + Global { id: global } + id: root + + // Prop Group is designed to author an array of ProItems, they are defined with an array of the tuplets describing each individual item: + // [ ..., PropItemInfo, ...] + // PropItemInfo { + // "type": "PropXXXX", "object": object, "property": "propName" + // } + // + property var propItems: [] + + + property var label: "group" + + Column { + id: column + anchors.left: parent.left + anchors.right: parent.right + + PropLabel { + anchors.left: parent.left + anchors.right: parent.right + text: root.label + } + + + Component.onCompleted: { + var component = Qt.createComponent("PropBool.qml"); + component.label = "Test"; + for (var i=0; i Date: Mon, 4 Mar 2019 23:44:14 -0800 Subject: [PATCH 11/85] Fixing the bins and exploring the REpeater --- .../developer/utilities/lib/prop/Global.qml | 44 ---------- .../developer/utilities/lib/prop/PropBool.qml | 1 - .../developer/utilities/lib/prop/PropEnum.qml | 83 ++----------------- .../utilities/lib/prop/PropGroup.qml | 42 ++++++---- .../developer/utilities/lib/prop/PropItem.qml | 3 +- .../utilities/lib/prop/PropLabel.qml | 25 ------ .../utilities/lib/prop/PropSplitter.qml | 21 ----- .../developer/utilities/lib/prop/PropText.qml | 24 ------ scripts/developer/utilities/lib/prop/qmldir | 11 ++- .../utilities/lib/prop/style/Global.qml | 2 + .../utilities/lib/prop/style/PiComboBox.qml | 2 +- scripts/developer/utilities/render/luci.qml | 17 ++++ 12 files changed, 58 insertions(+), 217 deletions(-) delete mode 100644 scripts/developer/utilities/lib/prop/Global.qml delete mode 100644 scripts/developer/utilities/lib/prop/PropLabel.qml delete mode 100644 scripts/developer/utilities/lib/prop/PropSplitter.qml delete mode 100644 scripts/developer/utilities/lib/prop/PropText.qml diff --git a/scripts/developer/utilities/lib/prop/Global.qml b/scripts/developer/utilities/lib/prop/Global.qml deleted file mode 100644 index be189e3c96..0000000000 --- a/scripts/developer/utilities/lib/prop/Global.qml +++ /dev/null @@ -1,44 +0,0 @@ -// -// Prop/Global.qml -// -// Created by Sam Gateau on 3/2/2019 -// Copyright 2019 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.7 - -import stylesUit 1.0 -import controlsUit 1.0 as HifiControls - - -Item { - HifiConstants { id: hifi } - id: root - - property real lineHeight: 32 - property real slimHeight: 24 - - property var color: hifi.colors.baseGray - property var colorBackHighlight: hifi.colors.baseGrayHighlight - property var colorBorderLight: hifi.colors.lightGray - property var colorBorderHighight: hifi.colors.blueHighlight - - property real fontSize: 12 - property var fontFamily: "Raleway" - property var fontWeight: Font.DemiBold - property var fontColor: hifi.colors.faintGray - - property var splitterRightWidthScale: 0.44 - property real splitterWidth: 4 - - property var labelTextAlign: Text.AlignRight - property var labelTextElide: Text.ElideMiddle - - property var valueAreaWidthScale: 0.3 * (1.0 - splitterRightWidthScale) - property var valueTextAlign: Text.AlignHCenter - property real valueBorderWidth: 1 - property real valueBorderRadius: 2 -} \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/PropBool.qml b/scripts/developer/utilities/lib/prop/PropBool.qml index e355398375..d8e4bad589 100644 --- a/scripts/developer/utilities/lib/prop/PropBool.qml +++ b/scripts/developer/utilities/lib/prop/PropBool.qml @@ -29,7 +29,6 @@ PropItem { width: root.width * global.valueAreaWidthScale height: global.slimHeight - checked: root.valueVar onCheckedChanged: { root.valueVarSetter(checked); } } } \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/PropEnum.qml b/scripts/developer/utilities/lib/prop/PropEnum.qml index fe6200d971..9446a267b3 100644 --- a/scripts/developer/utilities/lib/prop/PropEnum.qml +++ b/scripts/developer/utilities/lib/prop/PropEnum.qml @@ -18,7 +18,11 @@ PropItem { property alias valueVar : valueCombo.currentIndex property alias enums : valueCombo.model - ComboBox { + Component.onCompleted: { + valueVar = root.valueVarGetter(); + } + + PropComboBox { id: valueCombo flat: true @@ -28,83 +32,6 @@ PropItem { anchors.verticalCenter: root.verticalCenter height: global.slimHeight - currentIndex: root.valueVarGetter() onCurrentIndexChanged: { root.valueVarSetter(currentIndex); } - - delegate: ItemDelegate { - width: valueCombo.width - height: valueCombo.height - contentItem: PropText { - text: modelData - horizontalAlignment: global.valueTextAlign - } - background: Rectangle { - color:highlighted?global.colorBackHighlight:global.color; - } - highlighted: valueCombo.highlightedIndex === index - } - - indicator: Canvas { - id: canvas - x: valueCombo.width - width - valueCombo.rightPadding - y: valueCombo.topPadding + (valueCombo.availableHeight - height) / 2 - width: 12 - height: 8 - contextType: "2d" - - Connections { - target: valueCombo - onPressedChanged: canvas.requestPaint() - } - - onPaint: { - context.reset(); - context.moveTo(0, 0); - context.lineTo(width, 0); - context.lineTo(width / 2, height); - context.closePath(); - context.fillStyle = (valueCombo.pressed) ? global.colorBorderHighight : global.colorBorderLight; - context.fill(); - } - } - - contentItem: PropText { - leftPadding: 0 - rightPadding: valueCombo.indicator.width + valueCombo.spacing - - text: valueCombo.displayText - horizontalAlignment: global.valueTextAlign - } - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: global.color - border.color: valueCombo.popup.visible ? global.colorBorderHighight : global.colorBorderLight - border.width: global.valueBorderWidth - radius: global.valueBorderRadius - } - - popup: Popup { - y: valueCombo.height - 1 - width: valueCombo.width - implicitHeight: contentItem.implicitHeight + 2 - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: valueCombo.popup.visible ? valueCombo.delegateModel : null - currentIndex: valueCombo.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { } - } - - background: Rectangle { - color: global.color - border.color: global.colorBorderHighight - radius: global.valueBorderRadius - } - } } } diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 39294743b6..dd579af7eb 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -25,6 +25,19 @@ Item { property var label: "group" + /* Component.onCompleted: { + var component1 = Qt.createComponent("PropBool.qml"); + component1.label = "Test"; + for (var i=0; i Date: Tue, 5 Mar 2019 14:27:32 -0800 Subject: [PATCH 12/85] Dynamic creation of the propItem in the propGRoup! --- .../developer/utilities/lib/prop/PropEnum.qml | 3 +- .../utilities/lib/prop/PropGroup.qml | 62 ++++++++++++------- scripts/developer/utilities/render/luci.qml | 12 +++- 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/scripts/developer/utilities/lib/prop/PropEnum.qml b/scripts/developer/utilities/lib/prop/PropEnum.qml index 9446a267b3..ff5dfd8161 100644 --- a/scripts/developer/utilities/lib/prop/PropEnum.qml +++ b/scripts/developer/utilities/lib/prop/PropEnum.qml @@ -19,7 +19,7 @@ PropItem { property alias enums : valueCombo.model Component.onCompleted: { - valueVar = root.valueVarGetter(); + // valueVar = root.valueVarGetter(); } PropComboBox { @@ -32,6 +32,7 @@ PropItem { anchors.verticalCenter: root.verticalCenter height: global.slimHeight + currentIndex: root.valueVarGetter() onCurrentIndexChanged: { root.valueVarSetter(currentIndex); } } } diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index dd579af7eb..1dfb957536 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -17,7 +17,7 @@ Item { // Prop Group is designed to author an array of ProItems, they are defined with an array of the tuplets describing each individual item: // [ ..., PropItemInfo, ...] // PropItemInfo { - // "type": "PropXXXX", "object": object, "property": "propName" + // type: "PropXXXX", object: JSobject, property: "propName" // } // property var propItems: [] @@ -25,18 +25,6 @@ Item { property var label: "group" - /* Component.onCompleted: { - var component1 = Qt.createComponent("PropBool.qml"); - component1.label = "Test"; - for (var i=0; i Date: Tue, 5 Mar 2019 20:22:19 -0800 Subject: [PATCH 13/85] This is working !!!! --- .../utilities/lib/jet/qml/TaskPropView.qml | 40 +++++++++++++++++++ .../developer/utilities/lib/jet/qml/qmldir | 1 + .../utilities/lib/prop/PropGroup.qml | 7 +++- scripts/developer/utilities/render/luci.qml | 11 ++++- 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 scripts/developer/utilities/lib/jet/qml/TaskPropView.qml diff --git a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml new file mode 100644 index 0000000000..350103021a --- /dev/null +++ b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml @@ -0,0 +1,40 @@ +// +// jet/TaskListView.qml +// +// Created by Sam Gateau, 2018/05/09 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 1.4 as Original +import QtQuick.Controls.Styles 1.4 + +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls + +import "../../prop" as Prop + +import "../jet.js" as Jet + +Prop.PropGroup { + + id: root; + + property var rootConfig : Render + property var jobPath: "" + property alias jobName: root.label + + Component.onCompleted: { + var props = Jet.job_propKeys(rootConfig.getConfig(jobPath)); + //console.log(JSON.stringify(props)); + for (var p in props) { + root.propItems.push({"type": "PropBool", "object": rootConfig.getConfig(jobPath), "property":props[p] }) + } + root.updatePropItems(); + } + + +} \ No newline at end of file diff --git a/scripts/developer/utilities/lib/jet/qml/qmldir b/scripts/developer/utilities/lib/jet/qml/qmldir index e16820914b..2914c27c23 100644 --- a/scripts/developer/utilities/lib/jet/qml/qmldir +++ b/scripts/developer/utilities/lib/jet/qml/qmldir @@ -1,3 +1,4 @@ TaskList 1.0 TaskList.qml TaskViewList 1.0 TaskViewList.qml TaskTimeFrameView 1.0 TaskTimeFrameView.qml +TaskPropView 1.0 TaskPropView.qml \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 1dfb957536..6f5607def4 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -42,8 +42,8 @@ Item { } height: column.height - Component.onCompleted: { - for (var i = 0; i < root.propItems.length; i++) { + function updatePropItems() { + for (var i = 0; i < root.propItems.length; i++) { var proItem = root.propItems[i]; switch(proItem.type) { case 'PropBool': { @@ -77,4 +77,7 @@ Item { } } } + Component.onCompleted: { + updatePropItems(); + } } diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml index d009d52f55..d5156c3cf7 100644 --- a/scripts/developer/utilities/render/luci.qml +++ b/scripts/developer/utilities/render/luci.qml @@ -14,6 +14,7 @@ import QtQuick.Layouts 1.3 import controlsUit 1.0 as HifiControls import "../lib/prop" as Prop +import "../lib/jet/qml" as Jet Rectangle { Prop.Global { id: prop;} @@ -62,7 +63,7 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right }*/ - Prop.PropGroup { + /* Prop.PropGroup { label: "My group" propItems: [ {"type": "PropBool", "object": render.mainViewTask.getConfig("LightingModel"), "property": "enableBackground"}, @@ -75,6 +76,14 @@ Rectangle { "Filmic", ]}, ] + anchors.left: parent.left + anchors.right: parent.right + }*/ + + Jet.TaskPropView { + jobPath: "RenderMainView.LightingModel" + label: "Le tone mapping Job" + anchors.left: parent.left anchors.right: parent.right } From f7896b64079b90bfed7a9474f8260307053a7f92 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 6 Mar 2019 22:24:43 -0800 Subject: [PATCH 14/85] Better propGroup --- .../utilities/lib/jet/qml/TaskPropView.qml | 6 +- .../utilities/lib/prop/PropGroup.qml | 126 ++++++++++++------ scripts/developer/utilities/render/luci.qml | 11 +- 3 files changed, 98 insertions(+), 45 deletions(-) diff --git a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml index 350103021a..2188df7cc0 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml @@ -25,13 +25,13 @@ Prop.PropGroup { property var rootConfig : Render property var jobPath: "" - property alias jobName: root.label + property alias label: root.label Component.onCompleted: { var props = Jet.job_propKeys(rootConfig.getConfig(jobPath)); - //console.log(JSON.stringify(props)); + console.log(JSON.stringify(props)); for (var p in props) { - root.propItems.push({"type": "PropBool", "object": rootConfig.getConfig(jobPath), "property":props[p] }) + root.propItems.push({"object": rootConfig.getConfig(jobPath), "property":props[p] }) } root.updatePropItems(); } diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 6f5607def4..7b901b079d 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -25,56 +25,102 @@ Item { property var label: "group" - - Column { - id: column - anchors.left: parent.left - anchors.right: parent.right + Item { + id: header + height: global.slimHeight + anchors.left: parent.left + anchors.right: parent.right PropLabel { - anchors.left: parent.left - anchors.right: parent.right + id: labelControl + anchors.left: header.left + width: 0.8 * header.width + anchors.verticalCenter: header.verticalCenter text: root.label horizontalAlignment: Text.AlignHCenter } - - + Rectangle { + id: headerRect + color: global.color + border.color: global.colorBorderLight + border.width: global.valueBorderWidth + radius: global.valueBorderRadius + + anchors.left: labelControl.right + anchors.right: header.right + anchors.verticalCenter: header.verticalCenter + height: parent.height + } } - height: column.height + + Column { + id: column + anchors.top: header.bottom + anchors.left: parent.left + anchors.right: parent.right + clip: true + + // Where the propItems are added + } + height: header.height + column.height function updatePropItems() { for (var i = 0; i < root.propItems.length; i++) { var proItem = root.propItems[i]; - switch(proItem.type) { - case 'PropBool': { - var component = Qt.createComponent("PropBool.qml"); - component.createObject(column, { - "label": proItem.property, - "object": proItem.object, - "property": proItem.property - }) - } break; - case 'PropScalar': { - var component = Qt.createComponent("PropScalar.qml"); - component.createObject(column, { - "label": proItem.property, - "object": proItem.object, - "property": proItem.property, - "min": (proItem["min"] !== undefined ? proItem.min : 0.0), - "max": (proItem["max"] !== undefined ? proItem.max : 1.0), - "integer": (proItem["integral"] !== undefined ? proItem.integral : false), - }) - } break; - case 'PropEnum': { - var component = Qt.createComponent("PropEnum.qml"); - component.createObject(column, { - "label": proItem.property, - "object": proItem.object, - "property": proItem.property, - "enums": (proItem["enums"] !== undefined ? proItem.enums : ["Undefined Enums !!!"]), - }) - } break; - } + // valid object + if (proItem['object'] !== undefined && proItem['object'] !== null ) { + // valid property + if (proItem['property'] !== undefined && proItem.object[proItem.property] !== undefined) { + // check type + if (proItem['type'] === undefined) { + proItem['type'] = typeof(proItem.object[proItem.property]) + } + switch(proItem.type) { + case 'boolean': + case 'PropBool': { + var component = Qt.createComponent("PropBool.qml"); + component.createObject(column, { + "label": proItem.property, + "object": proItem.object, + "property": proItem.property + }) + } break; + case 'number': + case 'PropScalar': { + var component = Qt.createComponent("PropScalar.qml"); + component.createObject(column, { + "label": proItem.property, + "object": proItem.object, + "property": proItem.property, + "min": (proItem["min"] !== undefined ? proItem.min : 0.0), + "max": (proItem["max"] !== undefined ? proItem.max : 1.0), + "integer": (proItem["integral"] !== undefined ? proItem.integral : false), + }) + } break; + case 'PropEnum': { + var component = Qt.createComponent("PropEnum.qml"); + component.createObject(column, { + "label": proItem.property, + "object": proItem.object, + "property": proItem.property, + "enums": (proItem["enums"] !== undefined ? proItem.enums : ["Undefined Enums !!!"]), + }) + } break; + case 'object': { + var component = Qt.createComponent("PropItem.qml"); + component.createObject(column, { + "label": proItem.property, + "object": proItem.object, + "property": proItem.property, + }) + } break; + } + } else { + console.log('Invalid property: ' + JSON.stringify(proItem)); + } + } else { + console.log('Invalid object: ' + JSON.stringify(proItem)); + } } } Component.onCompleted: { diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml index d5156c3cf7..091a287e02 100644 --- a/scripts/developer/utilities/render/luci.qml +++ b/scripts/developer/utilities/render/luci.qml @@ -81,8 +81,15 @@ Rectangle { }*/ Jet.TaskPropView { - jobPath: "RenderMainView.LightingModel" - label: "Le tone mapping Job" + jobPath: "RenderMainView.ToneMapping" + label: "Le ToneMapping Job" + + anchors.left: parent.left + anchors.right: parent.right + } + Jet.TaskPropView { + jobPath: "RenderMainView.Antialiasing" + label: "Le Antialiasing Job" anchors.left: parent.left anchors.right: parent.right From 5b8116bec4046af2ed36e9b3a1749c0dbbd2a047 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 7 Mar 2019 17:52:11 -0800 Subject: [PATCH 15/85] make the group foldable --- scripts/developer/utilities/lib/prop/PropGroup.qml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 7b901b079d..0b8d5621f4 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -25,6 +25,8 @@ Item { property var label: "group" + property var isUnfold: false + Item { id: header height: global.slimHeight @@ -39,6 +41,7 @@ Item { text: root.label horizontalAlignment: Text.AlignHCenter } + Rectangle { id: headerRect color: global.color @@ -50,11 +53,20 @@ Item { anchors.right: header.right anchors.verticalCenter: header.verticalCenter height: parent.height + + MouseArea{ + id: mousearea + anchors.fill: parent + onDoubleClicked: { + root.isUnfold = !root.isUnfold + } + } } } Column { id: column + visible: root.isUnfold anchors.top: header.bottom anchors.left: parent.left anchors.right: parent.right @@ -62,7 +74,7 @@ Item { // Where the propItems are added } - height: header.height + column.height + height: header.height + isUnfold * column.height function updatePropItems() { for (var i = 0; i < root.propItems.length; i++) { From a303f803ebe1933805fac15e986dd5d23042e887 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 8 Mar 2019 18:14:38 -0800 Subject: [PATCH 16/85] BReaking group --- scripts/developer/utilities/lib/prop/PropGroup.qml | 4 ++-- scripts/developer/utilities/render/luci.qml | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 0b8d5621f4..eaba9d299f 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -36,7 +36,7 @@ Item { PropLabel { id: labelControl anchors.left: header.left - width: 0.8 * header.width + width: 0.9 * header.width anchors.verticalCenter: header.verticalCenter text: root.label horizontalAlignment: Text.AlignHCenter @@ -57,7 +57,7 @@ Item { MouseArea{ id: mousearea anchors.fill: parent - onDoubleClicked: { + onClicked: { root.isUnfold = !root.isUnfold } } diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml index 091a287e02..3f6b5b5c01 100644 --- a/scripts/developer/utilities/render/luci.qml +++ b/scripts/developer/utilities/render/luci.qml @@ -16,14 +16,17 @@ import controlsUit 1.0 as HifiControls import "../lib/prop" as Prop import "../lib/jet/qml" as Jet -Rectangle { + Original.ScrollView { Prop.Global { id: prop;} id: render; - anchors.fill: parent - color: prop.color; + anchors.fill: parent + + // color: prop.color; + + property var mainViewTask: Render.getConfig("RenderMainView") - + Column { anchors.left: parent.left anchors.right: parent.right From 530b871eef09bc74257cbaf9772cf7e60dfee1ea Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 11 Mar 2019 00:08:31 -0700 Subject: [PATCH 17/85] Getting the group styled and scrollview working all together --- .../developer/utilities/lib/prop/PropEnum.qml | 2 +- .../utilities/lib/prop/PropGroup.qml | 93 +++++++++------ .../utilities/lib/prop/PropScalar.qml | 1 - scripts/developer/utilities/lib/prop/qmldir | 1 + .../utilities/lib/prop/style/Global.qml | 5 +- .../utilities/lib/prop/style/PiCanvasIcon.qml | 50 ++++++++ .../utilities/lib/prop/style/PiComboBox.qml | 22 +--- scripts/developer/utilities/render/luci.qml | 112 ++++++------------ 8 files changed, 159 insertions(+), 127 deletions(-) create mode 100644 scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml diff --git a/scripts/developer/utilities/lib/prop/PropEnum.qml b/scripts/developer/utilities/lib/prop/PropEnum.qml index ff5dfd8161..87c2845c90 100644 --- a/scripts/developer/utilities/lib/prop/PropEnum.qml +++ b/scripts/developer/utilities/lib/prop/PropEnum.qml @@ -28,7 +28,7 @@ PropItem { flat: true anchors.left: root.splitter.right - anchors.right: parent.right + anchors.right: root.right anchors.verticalCenter: root.verticalCenter height: global.slimHeight diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index eaba9d299f..4465ec420e 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -25,56 +25,83 @@ Item { property var label: "group" - property var isUnfold: false - + property alias isUnfold: headerRect.icon + Item { id: header height: global.slimHeight anchors.left: parent.left anchors.right: parent.right - + + Item { + id: folder + anchors.left: header.left + width: headerRect.width * 2 + anchors.verticalCenter: header.verticalCenter + height: parent.height + + PropCanvasIcon { + id: headerRect + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + + MouseArea{ + id: mousearea + anchors.fill: parent + onClicked: { + root.isUnfold = !root.isUnfold + } + } + } + } + PropLabel { id: labelControl - anchors.left: header.left - width: 0.9 * header.width + anchors.left: folder.right + anchors.right: header.right anchors.verticalCenter: header.verticalCenter text: root.label horizontalAlignment: Text.AlignHCenter } - Rectangle { - id: headerRect - color: global.color - border.color: global.colorBorderLight - border.width: global.valueBorderWidth - radius: global.valueBorderRadius - - anchors.left: labelControl.right - anchors.right: header.right - anchors.verticalCenter: header.verticalCenter - height: parent.height + /* Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + anchors.bottom: parent.bottom + color: global.colorBorderHighight + + visible: root.isUnfold + }*/ + } - MouseArea{ - id: mousearea - anchors.fill: parent - onClicked: { - root.isUnfold = !root.isUnfold - } - } + Rectangle { + visible: root.isUnfold + + color: "transparent" + border.color: global.colorBorderLight + border.width: global.valueBorderWidth + radius: global.valueBorderRadius + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: header.bottom + anchors.bottom: root.bottom + + Column { + id: column + // anchors.top: header.bottom + anchors.left: parent.left + anchors.right: parent.right + clip: true + + // Where the propItems are added } } - - Column { - id: column - visible: root.isUnfold - anchors.top: header.bottom - anchors.left: parent.left - anchors.right: parent.right - clip: true - // Where the propItems are added - } height: header.height + isUnfold * column.height + anchors.leftMargin: global.horizontalMargin + anchors.rightMargin: global.horizontalMargin function updatePropItems() { for (var i = 0; i < root.propItems.length; i++) { diff --git a/scripts/developer/utilities/lib/prop/PropScalar.qml b/scripts/developer/utilities/lib/prop/PropScalar.qml index 29b42e2801..684dd4fed4 100644 --- a/scripts/developer/utilities/lib/prop/PropScalar.qml +++ b/scripts/developer/utilities/lib/prop/PropScalar.qml @@ -61,7 +61,6 @@ PropItem { stepSize: root.integral ? 1.0 : 0.0 anchors.left: valueLabel.right anchors.right: root.right - anchors.rightMargin: 0 anchors.verticalCenter: root.verticalCenter onValueChanged: { root.valueVarSetter(value) } diff --git a/scripts/developer/utilities/lib/prop/qmldir b/scripts/developer/utilities/lib/prop/qmldir index 3e52395dab..2cd06d58ac 100644 --- a/scripts/developer/utilities/lib/prop/qmldir +++ b/scripts/developer/utilities/lib/prop/qmldir @@ -4,6 +4,7 @@ PropText 1.0 style/PiText.qml PropLabel 1.0 style/PiLabel.qml PropSplitter 1.0 style/PiSplitter.qml PropComboBox 1.0 style/PiComboBox.qml +PropCanvasIcon 1.0 style/PiCanvasIcon.qml PropItem 1.0 PropItem.qml PropScalar 1.0 PropScalar.qml diff --git a/scripts/developer/utilities/lib/prop/style/Global.qml b/scripts/developer/utilities/lib/prop/style/Global.qml index e772477611..6066a4f99b 100644 --- a/scripts/developer/utilities/lib/prop/style/Global.qml +++ b/scripts/developer/utilities/lib/prop/style/Global.qml @@ -21,7 +21,7 @@ Item { property real lineHeight: 32 property real slimHeight: 24 - property real horizontalMargin: 2 + property real horizontalMargin: 4 property var color: hifi.colors.baseGray property var colorBackHighlight: hifi.colors.baseGrayHighlight @@ -35,6 +35,9 @@ Item { property var splitterRightWidthScale: 0.45 property real splitterWidth: 8 + + property real iconWidth: fontSize + property real iconHeight: fontSize property var labelTextAlign: Text.AlignRight property var labelTextElide: Text.ElideMiddle diff --git a/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml b/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml new file mode 100644 index 0000000000..6a805ea4c6 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml @@ -0,0 +1,50 @@ +// +// Prop/style/PiFoldCanvas.qml +// +// Created by Sam Gateau on 3/9/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + + import QtQuick 2.7 +import QtQuick.Controls 2.2 + +Canvas { + Global { id: global } + + width: global.iconWidth + height: global.iconHeight + + property var icon: 0 + onIconChanged: function () { requestPaint() } + property var fillColor: global.colorBorderHighight + + contextType: "2d" + onPaint: { + context.reset(); + switch (icon) { + case 0: + case 1: + case 2: + default: { + if ((icon % 3) == 0) { + context.moveTo(width * 0.25, 0); + context.lineTo(width * 0.25, height); + context.lineTo(width, height / 2); + } else if ((icon % 3) == 1) { + context.moveTo(0, height * 0.25); + context.lineTo(width, height * 0.25); + context.lineTo(width / 2, height); + } else { + context.moveTo(0, height * 0.75); + context.lineTo(width, height* 0.75); + context.lineTo(width / 2, 0); + } + context.closePath(); + context.fillStyle = fillColor; + context.fill(); + }} + } +} \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/style/PiComboBox.qml b/scripts/developer/utilities/lib/prop/style/PiComboBox.qml index 9d002ed2b9..d9e029b702 100644 --- a/scripts/developer/utilities/lib/prop/style/PiComboBox.qml +++ b/scripts/developer/utilities/lib/prop/style/PiComboBox.qml @@ -33,28 +33,16 @@ ComboBox { highlighted: valueCombo.highlightedIndex === index } - indicator: Canvas { + indicator: PiCanvasIcon { id: canvas x: valueCombo.width - width - valueCombo.rightPadding y: valueCombo.topPadding + (valueCombo.availableHeight - height) / 2 - width: 12 - height: 8 - contextType: "2d" - Connections { + icon: 1 + /*Connections { target: valueCombo - onPressedChanged: canvas.requestPaint() - } - - onPaint: { - context.reset(); - context.moveTo(0, 0); - context.lineTo(width, 0); - context.lineTo(width / 2, height); - context.closePath(); - context.fillStyle = (valueCombo.pressed) ? global.colorBorderHighight : global.colorBorderLight; - context.fill(); - } + onPressedChanged: { canvas.icon = control.down + 1 } + }*/ } contentItem: PiText { diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml index 3f6b5b5c01..89794b037e 100644 --- a/scripts/developer/utilities/render/luci.qml +++ b/scripts/developer/utilities/render/luci.qml @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html // import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import controlsUit 1.0 as HifiControls @@ -16,86 +16,50 @@ import controlsUit 1.0 as HifiControls import "../lib/prop" as Prop import "../lib/jet/qml" as Jet - Original.ScrollView { - Prop.Global { id: prop;} - id: render; +Rectangle { anchors.fill: parent - - // color: prop.color; - - - + id: render; property var mainViewTask: Render.getConfig("RenderMainView") - Column { - anchors.left: parent.left - anchors.right: parent.right - /* Repeater { - model: [ "Tone mapping exposure:ToneMapping:exposure:5.0:-5.0", - "Tone:ToneMapping:exposure:5.0:-5.0" + Prop.Global { id: global;} + color: global.color + + ScrollView { + id: control + anchors.fill: parent + clip: true + + Column { + width: render.width + + Prop.PropEnum { + label: "Tone Curve" + object: render.mainViewTask.getConfig("ToneMapping") + property: "curve" + enums: [ + "RGB", + "SRGB", + "Reinhard", + "Filmic", ] - Prop.PropScalar { - label: qsTr(modelData.split(":")[0]) - integral: false - object: render.mainViewTask.getConfig(modelData.split(":")[1]) - property: modelData.split(":")[2] - max: modelData.split(":")[3] - min: modelData.split(":")[4] + anchors.left: parent.left + anchors.right: parent.right + } + + Jet.TaskPropView { + jobPath: "RenderMainView.ToneMapping" + label: "Le ToneMapping Job" - anchors.left: parent.left - anchors.right: parent.right + anchors.left: parent.left + anchors.right: parent.right } - } - Prop.PropEnum { - label: "Tone Curve" - object: render.mainViewTask.getConfig("ToneMapping") - property: "curve" - enums: [ - "RGB", - "SRGB", - "Reinhard", - "Filmic", - ] - anchors.left: parent.left - anchors.right: parent.right - } - Prop.PropBool { - label: "Background" - object: render.mainViewTask.getConfig("LightingModel") - property: "enableBackground" - anchors.left: parent.left - anchors.right: parent.right - }*/ - /* Prop.PropGroup { - label: "My group" - propItems: [ - {"type": "PropBool", "object": render.mainViewTask.getConfig("LightingModel"), "property": "enableBackground"}, - {"type": "PropScalar", "object": render.mainViewTask.getConfig("ToneMapping"), "property": "exposure"}, - {"type": "PropBool", "object": render.mainViewTask.getConfig("LightingModel"), "property": "enableEmissive"}, - {"type": "PropEnum", "object": render.mainViewTask.getConfig("ToneMapping"), "property": "curve", enums: [ - "RGB", - "SRGB", - "Reinhard", - "Filmic", - ]}, - ] - anchors.left: parent.left - anchors.right: parent.right - }*/ + Jet.TaskPropView { + jobPath: "RenderMainView.Antialiasing" + label: "Le Antialiasing Job" - Jet.TaskPropView { - jobPath: "RenderMainView.ToneMapping" - label: "Le ToneMapping Job" - - anchors.left: parent.left - anchors.right: parent.right - } - Jet.TaskPropView { - jobPath: "RenderMainView.Antialiasing" - label: "Le Antialiasing Job" - - anchors.left: parent.left - anchors.right: parent.right + anchors.left: parent.left + anchors.right: parent.right + } } } } \ No newline at end of file From f2fc9c010219dd97f62e6517bf48f9b60c05f019 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 12 Mar 2019 16:40:28 -0700 Subject: [PATCH 18/85] Multiple task view --- scripts/developer/utilities/lib/jet/jet.js | 10 +++- .../utilities/lib/jet/qml/TaskPropView.qml | 32 ++++++++++-- .../utilities/lib/prop/PropGroup.qml | 51 +++++++++++-------- .../developer/utilities/lib/prop/PropItem.qml | 2 +- scripts/developer/utilities/render/luci.qml | 18 +++++-- 5 files changed, 83 insertions(+), 30 deletions(-) diff --git a/scripts/developer/utilities/lib/jet/jet.js b/scripts/developer/utilities/lib/jet/jet.js index 40563e4b2c..52c13c5279 100644 --- a/scripts/developer/utilities/lib/jet/jet.js +++ b/scripts/developer/utilities/lib/jet/jet.js @@ -10,7 +10,12 @@ // "use strict"; - // traverse task tree + // traverse task tree recursively + // + // @param root: the root job config from where to traverse + // @param functor: the functor function() which is applied on every subjobs of root traversed + // if return true, then 'task_tree' is called recursively on that subjob + // @param depth: the depth of the recurse loop since the initial call. function task_traverse(root, functor, depth) { if (root.isTask()) { depth++; @@ -22,6 +27,9 @@ function task_traverse(root, functor, depth) { } } } + +// same function as 'task_traverse' with the depth being 0 +// and visisting the root job first. function task_traverseTree(root, functor) { if (functor(root, 0, 0)) { task_traverse(root, functor, 0) diff --git a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml index 2188df7cc0..af4cbb1c9a 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml @@ -1,5 +1,5 @@ // -// jet/TaskListView.qml +// jet/TaskPropView.qml // // Created by Sam Gateau, 2018/05/09 // Copyright 2018 High Fidelity, Inc. @@ -27,13 +27,37 @@ Prop.PropGroup { property var jobPath: "" property alias label: root.label - Component.onCompleted: { + function populatePropItems() { + var propsModel = [] var props = Jet.job_propKeys(rootConfig.getConfig(jobPath)); console.log(JSON.stringify(props)); for (var p in props) { - root.propItems.push({"object": rootConfig.getConfig(jobPath), "property":props[p] }) + propsModel.push({"object": rootConfig.getConfig(jobPath), "property":props[p] }) } - root.updatePropItems(); + root.updatePropItems(propsModel); + + + Jet.task_traverse(rootConfig.getConfig(jobPath), + function(job, depth, index) { + var component = Qt.createComponent("./TaskPropView.qml"); + component.createObject(root.propItemsPanel, { + "label": job.objectName, + "rootConfig": root.rootConfig, + "jobPath": root.jobPath + '.' + job.objectName + }) + /* var component = Qt.createComponent("../../prop/PropItem.qml"); + component.createObject(root.propItemsPanel, { + "label": root.jobPath + '.' + job.objectName + ' num=' + index, + })*/ + // propsModel.push({"type": "printLabel", "label": root.jobPath + '.' + job.objectName + ' num=' + index }) + + return (depth < 1); + }, 0) + + } + + Component.onCompleted: { + populatePropItems() } diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 4465ec420e..544a687f70 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -13,20 +13,12 @@ import QtQuick 2.7 Item { Global { id: global } id: root - - // Prop Group is designed to author an array of ProItems, they are defined with an array of the tuplets describing each individual item: - // [ ..., PropItemInfo, ...] - // PropItemInfo { - // type: "PropXXXX", object: JSobject, property: "propName" - // } - // - property var propItems: [] - property var label: "group" property alias isUnfold: headerRect.icon - + property alias propItemsPanel: propItemsContainer + Item { id: header height: global.slimHeight @@ -89,7 +81,7 @@ Item { anchors.bottom: root.bottom Column { - id: column + id: propItemsContainer // anchors.top: header.bottom anchors.left: parent.left anchors.right: parent.right @@ -99,13 +91,22 @@ Item { } } - height: header.height + isUnfold * column.height + height: header.height + isUnfold * propItemsContainer.height anchors.leftMargin: global.horizontalMargin anchors.rightMargin: global.horizontalMargin + anchors.left: parent.left + anchors.right: parent.right - function updatePropItems() { - for (var i = 0; i < root.propItems.length; i++) { - var proItem = root.propItems[i]; + + // Prop Group is designed to author an array of ProItems, they are defined with an array of the tuplets describing each individual item: + // [ ..., PropItemInfo, ...] + // PropItemInfo { + // type: "PropXXXX", object: JSobject, property: "propName" + // } + // + function updatePropItems(propItemsModel) { + for (var i = 0; i < propItemsModel.length; i++) { + var proItem = propItemsModel[i]; // valid object if (proItem['object'] !== undefined && proItem['object'] !== null ) { // valid property @@ -118,7 +119,7 @@ Item { case 'boolean': case 'PropBool': { var component = Qt.createComponent("PropBool.qml"); - component.createObject(column, { + component.createObject(propItemsContainer, { "label": proItem.property, "object": proItem.object, "property": proItem.property @@ -127,7 +128,7 @@ Item { case 'number': case 'PropScalar': { var component = Qt.createComponent("PropScalar.qml"); - component.createObject(column, { + component.createObject(propItemsContainer, { "label": proItem.property, "object": proItem.object, "property": proItem.property, @@ -138,7 +139,7 @@ Item { } break; case 'PropEnum': { var component = Qt.createComponent("PropEnum.qml"); - component.createObject(column, { + component.createObject(propItemsContainer, { "label": proItem.property, "object": proItem.object, "property": proItem.property, @@ -147,22 +148,32 @@ Item { } break; case 'object': { var component = Qt.createComponent("PropItem.qml"); - component.createObject(column, { + component.createObject(propItemsContainer, { "label": proItem.property, "object": proItem.object, "property": proItem.property, }) } break; + case 'printLabel': { + var component = Qt.createComponent("PropItem.qml"); + component.createObject(propItemsContainer, { + "label": proItem.property + }) + } break; } } else { console.log('Invalid property: ' + JSON.stringify(proItem)); } + } else if (proItem['type'] === 'printLabel') { + var component = Qt.createComponent("PropItem.qml"); + component.createObject(propItemsContainer, { + "label": proItem.label + }) } else { console.log('Invalid object: ' + JSON.stringify(proItem)); } } } Component.onCompleted: { - updatePropItems(); } } diff --git a/scripts/developer/utilities/lib/prop/PropItem.qml b/scripts/developer/utilities/lib/prop/PropItem.qml index 00314512f2..339ff10422 100644 --- a/scripts/developer/utilities/lib/prop/PropItem.qml +++ b/scripts/developer/utilities/lib/prop/PropItem.qml @@ -16,7 +16,7 @@ Item { id: root // Prop item is designed to author an object[property]: - property var object: NULL + property var object: {} property string property: "" // value is accessed through the "valueVarSetter" and "valueVarGetter" diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml index 89794b037e..85ca69e998 100644 --- a/scripts/developer/utilities/render/luci.qml +++ b/scripts/developer/utilities/render/luci.qml @@ -32,7 +32,7 @@ Rectangle { Column { width: render.width - Prop.PropEnum { + /* Prop.PropEnum { label: "Tone Curve" object: render.mainViewTask.getConfig("ToneMapping") property: "curve" @@ -44,9 +44,16 @@ Rectangle { ] anchors.left: parent.left anchors.right: parent.right - } - + } */ Jet.TaskPropView { + id: "the" + jobPath: "RenderMainView.RenderDeferredTask" + label: "Le Render Deferred Job" + + anchors.left: parent.left + anchors.right: parent.right + } + /* Jet.TaskPropView { jobPath: "RenderMainView.ToneMapping" label: "Le ToneMapping Job" @@ -59,7 +66,10 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right - } + }*/ } } + + Component.onCompleted: { + } } \ No newline at end of file From 9581f5c958c17b830b617f87c0953ed7bb7b4e50 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 5 Apr 2019 08:44:49 -0700 Subject: [PATCH 19/85] separating the group header container --- .../utilities/lib/prop/PropGroup.qml | 43 +++++++++++-------- scripts/developer/utilities/render/luci.qml | 16 +++++++ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 544a687f70..f4b1ba02d6 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -16,24 +16,27 @@ Item { property var label: "group" - property alias isUnfold: headerRect.icon + property alias isUnfold: headerFolderIcon.icon property alias propItemsPanel: propItemsContainer + property alias headerContainer: headerContainer + // Header Item Item { id: header height: global.slimHeight anchors.left: parent.left anchors.right: parent.right + // First in the header, the folder button / indicator Item { - id: folder + id: headerFolder anchors.left: header.left - width: headerRect.width * 2 + width: headerFolderIcon.width * 2 anchors.verticalCenter: header.verticalCenter height: parent.height PropCanvasIcon { - id: headerRect + id: headerFolderIcon anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter @@ -47,26 +50,28 @@ Item { } } - PropLabel { - id: labelControl - anchors.left: folder.right + // Next the header container + // by default containing a Label showing the root.label + Item { + id: headerContainer + anchors.left: headerFolder.right anchors.right: header.right anchors.verticalCenter: header.verticalCenter - text: root.label - horizontalAlignment: Text.AlignHCenter - } + height: parent.height - /* Rectangle { - anchors.left: parent.left - anchors.right: parent.right - height: 1 - anchors.bottom: parent.bottom - color: global.colorBorderHighight - - visible: root.isUnfold - }*/ + PropLabel { + id: labelControl + anchors.left: headerContainer.left + anchors.right: headerContainer.right + anchors.verticalCenter: headerContainer.verticalCenter + text: root.label + horizontalAlignment: Text.AlignHCenter + } + + } } + // The Panel container Rectangle { visible: root.isUnfold diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml index 85ca69e998..8c68d654a1 100644 --- a/scripts/developer/utilities/render/luci.qml +++ b/scripts/developer/utilities/render/luci.qml @@ -45,6 +45,22 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right } */ + Jet.TaskPropView { + id: "lightingModel" + jobPath: "RenderMainView.LightingModel" + label: "Le LightingModel" + + anchors.left: parent.left + anchors.right: parent.right + } + Jet.TaskPropView { + id: "theView" + jobPath: "RenderMainView" + label: "Le Render Main View" + + anchors.left: parent.left + anchors.right: parent.right + } Jet.TaskPropView { id: "the" jobPath: "RenderMainView.RenderDeferredTask" From c730e51d1e7d37271a6c7b948f6d62806de6a3a0 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 11 Apr 2019 13:22:51 -0700 Subject: [PATCH 20/85] wip redo stencil work --- cmake/externals/LibOVR/CMakeLists.txt | 4 +- interface/src/Application.cpp | 24 +++++-- interface/src/Application.h | 6 ++ interface/src/SecondaryCamera.cpp | 11 +-- interface/src/graphics/GraphicsEngine.cpp | 4 ++ interface/src/ui/SnapshotAnimated.cpp | 67 ++++++++++--------- .../display-plugins/OpenGLDisplayPlugin.cpp | 9 +++ .../display-plugins/hmd/HmdDisplayPlugin.h | 2 + libraries/gpu/src/gpu/Frame.h | 3 + libraries/plugins/src/plugins/DisplayPlugin.h | 5 ++ .../render-utils/src/StencilMaskPass.cpp | 42 +++++++----- libraries/render-utils/src/StencilMaskPass.h | 13 ++-- libraries/render/src/render/Args.h | 5 ++ libraries/shared/src/RegisteredMetaTypes.cpp | 11 +++ libraries/shared/src/RegisteredMetaTypes.h | 4 ++ libraries/shared/src/StencilMaskMode.h | 18 +++++ plugins/oculus/CMakeLists.txt | 2 +- .../oculus/src/OculusBaseDisplayPlugin.cpp | 63 +++++++++++++++++ plugins/oculus/src/OculusBaseDisplayPlugin.h | 8 +++ plugins/openvr/src/OpenVrDisplayPlugin.cpp | 45 +++++++++++++ plugins/openvr/src/OpenVrDisplayPlugin.h | 8 +++ 21 files changed, 288 insertions(+), 66 deletions(-) create mode 100644 libraries/shared/src/StencilMaskMode.h diff --git a/cmake/externals/LibOVR/CMakeLists.txt b/cmake/externals/LibOVR/CMakeLists.txt index ae4cf6320e..481753f7e0 100644 --- a/cmake/externals/LibOVR/CMakeLists.txt +++ b/cmake/externals/LibOVR/CMakeLists.txt @@ -17,8 +17,8 @@ if (WIN32) ExternalProject_Add( ${EXTERNAL_NAME} - URL https://public.highfidelity.com/dependencies/ovr_sdk_win_1.26.0_public.zip - URL_MD5 06804ff9727b910dcd04a37c800053b5 + URL https://hifi-public.s3.amazonaws.com/dependencies/ovr_sdk_win_1.35.0.zip + URL_MD5 1e3e8b2101387af07ff9c841d0ea285e CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= PATCH_COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/LibOVRCMakeLists.txt" /CMakeLists.txt LOG_DOWNLOAD 1 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e809120a74..bc0feefb9a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6725,6 +6725,11 @@ void Application::updateRenderArgs(float deltaTime) { } } + appRenderArgs._renderArgs._stencilMaskMode = getActiveDisplayPlugin()->getStencilMaskMode(); + if (appRenderArgs._renderArgs._stencilMaskMode == StencilMaskMode::MESH) { + appRenderArgs._renderArgs._stencilMaskOperator = getActiveDisplayPlugin()->getStencilMaskMeshOperator(); + } + { QMutexLocker viewLocker(&_viewMutex); _myCamera.loadViewFrustum(_displayViewFrustum); @@ -8412,11 +8417,20 @@ void Application::loadAvatarBrowser() const { DependencyManager::get()->openTablet(); } +void Application::addSnapshotOperator(const SnapshotOperator& snapshotOperator) { + std::lock_guard lock(_snapshotMutex); + _snapshotOperators.push(snapshotOperator); +} + +bool Application::takeSnapshotOperators(std::queue& snapshotOperators) { + std::lock_guard lock(_snapshotMutex); + _snapshotOperators.swap(snapshotOperators); + return !snapshotOperators.empty(); +} + void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { - postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] { - // Get a screenshot and save it - QString path = DependencyManager::get()->saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, - TestScriptingInterface::getInstance()->getTestResultsLocation()); + addSnapshotOperator({ [notify, includeAnimated, aspectRatio, filename](const QImage& snapshot) { + QString path = DependencyManager::get()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation()); // If we're not doing an animated snapshot as well... if (!includeAnimated) { @@ -8428,7 +8442,7 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa // Get an animated GIF snapshot and save it SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get()); } - }); + }, aspectRatio }); } void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 99e57f1866..b2e59e97d4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -344,6 +344,10 @@ public: void toggleAwayMode(); #endif + using SnapshotOperator = std::pair, float>; + void addSnapshotOperator(const SnapshotOperator& snapshotOperator); + bool takeSnapshotOperators(std::queue& snapshotOperators); + signals: void svoImportRequested(const QString& url); @@ -788,6 +792,8 @@ private: AudioInjectorPointer _snapshotSoundInjector; SharedSoundPointer _snapshotSound; SharedSoundPointer _sampleSound; + std::mutex _snapshotMutex; + std::queue _snapshotOperators; DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin; QString _autoSwitchDisplayModeSupportedHMDPluginName; diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 12c9636746..da2874a3f4 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -152,10 +152,12 @@ public: _cachedArgsPointer->_viewport = args->_viewport; _cachedArgsPointer->_displayMode = args->_displayMode; _cachedArgsPointer->_renderMode = args->_renderMode; + _cachedArgsPointer->_stencilMaskMode = args->_stencilMaskMode; args->_blitFramebuffer = destFramebuffer; args->_viewport = glm::ivec4(0, 0, destFramebuffer->getWidth(), destFramebuffer->getHeight()); args->_displayMode = RenderArgs::MONO; args->_renderMode = RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE; + args->_stencilMaskMode = StencilMaskMode::NONE; gpu::doInBatch("SecondaryCameraJob::run", args->_context, [&](gpu::Batch& batch) { batch.disableContextStereo(); @@ -255,10 +257,11 @@ public: void run(const render::RenderContextPointer& renderContext, const RenderArgsPointer& cachedArgs) { auto args = renderContext->args; if (cachedArgs) { - args->_blitFramebuffer = cachedArgs->_blitFramebuffer; - args->_viewport = cachedArgs->_viewport; - args->_displayMode = cachedArgs->_displayMode; - args->_renderMode = cachedArgs->_renderMode; + args->_blitFramebuffer = cachedArgs->_blitFramebuffer; + args->_viewport = cachedArgs->_viewport; + args->_displayMode = cachedArgs->_displayMode; + args->_renderMode = cachedArgs->_renderMode; + args->_stencilMaskMode = cachedArgs->_stencilMaskMode; } args->popViewFrustum(); diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp index c2137d3d97..1842c20e4e 100644 --- a/interface/src/graphics/GraphicsEngine.cpp +++ b/interface/src/graphics/GraphicsEngine.cpp @@ -244,6 +244,7 @@ void GraphicsEngine::render_performFrame() { finalFramebuffer = framebufferCache->getFramebuffer(); } + std::queue snapshotOperators; if (!_programsCompiled.load()) { gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) { batch.setFramebuffer(finalFramebuffer); @@ -271,11 +272,13 @@ void GraphicsEngine::render_performFrame() { PROFILE_RANGE(render, "/runRenderFrame"); renderArgs._hudOperator = displayPlugin->getHUDOperator(); renderArgs._hudTexture = qApp->getApplicationOverlay().getOverlayTexture(); + renderArgs._takingSnapshot = qApp->takeSnapshotOperators(snapshotOperators); renderArgs._blitFramebuffer = finalFramebuffer; render_runRenderFrame(&renderArgs); } } + qDebug() << "boop" << renderArgs._takingSnapshot << snapshotOperators.size(); auto frame = getGPUContext()->endFrame(); frame->frameIndex = _renderFrameCount; frame->framebuffer = finalFramebuffer; @@ -285,6 +288,7 @@ void GraphicsEngine::render_performFrame() { frameBufferCache->releaseFramebuffer(framebuffer); } }; + frame->snapshotOperators = snapshotOperators; // deliver final scene rendering commands to the display plugin { PROFILE_RANGE(render, "/pluginOutput"); diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index 9d58d89385..1c5a25b68f 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -62,44 +62,45 @@ void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio void SnapshotAnimated::captureFrames() { if (SnapshotAnimated::snapshotAnimatedTimerRunning) { - // Get a screenshot from the display, then scale the screenshot down, - // then convert it to the image format the GIF library needs, - // then save all that to the QImage named "frame" - QImage frame(SnapshotAnimated::app->getActiveDisplayPlugin()->getScreenshot(SnapshotAnimated::aspectRatio)); - frame = frame.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH); - SnapshotAnimated::snapshotAnimatedFrameVector.append(frame); + SnapshotAnimated::app->addSnapshotOperator({ [](const QImage& snapshot) { + // Get a screenshot from the display, then scale the screenshot down, + // then convert it to the image format the GIF library needs, + // then save all that to the QImage named "frame" + QImage frame = snapshot.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH); + SnapshotAnimated::snapshotAnimatedFrameVector.append(frame); - // If that was the first frame... - if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) { - // Record the current frame timestamp - SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); - // Record the first frame timestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp; - SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10); - // If this is an intermediate or the final frame... - } else { - // Push the current frame delay onto the vector - SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10)); - // Record the current frame timestamp - SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); + // If that was the first frame... + if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) { + // Record the current frame timestamp + SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); + // Record the first frame timestamp + SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp; + SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10); + // If this is an intermediate or the final frame... + } else { + // Push the current frame delay onto the vector + SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10)); + // Record the current frame timestamp + SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); - // If that was the last frame... - if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) { - SnapshotAnimated::snapshotAnimatedTimerRunning = false; + // If that was the last frame... + if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) { + SnapshotAnimated::snapshotAnimatedTimerRunning = false; - // Notify the user that we're processing the snapshot - // This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called. - emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath); + // Notify the user that we're processing the snapshot + // This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called. + emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath); - // Kick off the thread that'll pack the frames into the GIF - QtConcurrent::run(processFrames); - // Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE - // that the slot will not be called again in the future. - // See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html - SnapshotAnimated::snapshotAnimatedTimer->stop(); - delete SnapshotAnimated::snapshotAnimatedTimer; + // Kick off the thread that'll pack the frames into the GIF + QtConcurrent::run(processFrames); + // Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE + // that the slot will not be called again in the future. + // See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html + SnapshotAnimated::snapshotAnimatedTimer->stop(); + delete SnapshotAnimated::snapshotAnimatedTimer; + } } - } + }, SnapshotAnimated::aspectRatio }); } } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index c536e6b6e2..1700ca9d54 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -723,6 +723,15 @@ void OpenGLDisplayPlugin::present() { compositeLayers(); } + { // If we have any snapshots this frame, handle them + PROFILE_RANGE_EX(render, "snapshotOperators", 0xffff00ff, frameId) + while (!_currentFrame->snapshotOperators.empty()) { + auto& snapshotOperator = _currentFrame->snapshotOperators.front(); + snapshotOperator.first(getScreenshot(snapshotOperator.second)); + _currentFrame->snapshotOperators.pop(); + } + } + // Take the composite framebuffer and send it to the output device { PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId) diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index d8c0ce8e1d..e952b1e8db 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -48,6 +48,8 @@ public: void pluginUpdate() override {}; + virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::PAINT; } + signals: void hmdMountedChanged(); void hmdVisibleChanged(bool visible); diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index 3787ebfacd..94c0919fdb 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -9,6 +9,7 @@ #define hifi_gpu_Frame_h #include +#include #include "Forward.h" #include "Batch.h" @@ -41,6 +42,8 @@ namespace gpu { /// How to process the framebuffer when the frame dies. MUST BE THREAD SAFE FramebufferRecycler framebufferRecycler; + std::queue, float>> snapshotOperators; + protected: friend class Deserializer; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index aa52e57c3f..9db8fc995c 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -27,6 +27,7 @@ #include #include #include "Plugin.h" +#include "StencilMaskMode.h" class QOpenGLFramebufferObject; @@ -221,6 +222,10 @@ public: // for updating plugin-related commands. Mimics the input plugin. virtual void pluginUpdate() = 0; + virtual StencilMaskMode getStencilMaskMode() const { return StencilMaskMode::NONE; } + using StencilMaskMeshOperator = std::function; + virtual StencilMaskMeshOperator getStencilMaskMeshOperator() { return nullptr; } + signals: void recommendedFramebufferSizeChanged(const QSize& size); void resetSensorsRequested(); diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index 7217a3e5eb..7c89e6b601 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -19,7 +19,6 @@ using namespace render; void PrepareStencil::configure(const Config& config) { _maskMode = config.maskMode; - _forceDraw = config.forceDraw; } graphics::MeshPointer PrepareStencil::getMesh() { @@ -43,6 +42,7 @@ gpu::PipelinePointer PrepareStencil::getMeshStencilPipeline() { auto state = std::make_shared(); drawMask(*state); state->setColorWriteMask(gpu::State::WRITE_NONE); + state->setCullMode(gpu::State::CullMode::CULL_NONE); _meshStencilPipeline = gpu::Pipeline::create(program, state); } @@ -64,8 +64,28 @@ gpu::PipelinePointer PrepareStencil::getPaintStencilPipeline() { void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) { RenderArgs* args = renderContext->args; - // Only draw the stencil mask if in HMD mode or not forced. - if (!_forceDraw && (args->_displayMode != RenderArgs::STEREO_HMD)) { + if (args->_takingSnapshot) { + return; + } + + StencilMaskMode maskMode = _maskMode; + std::function maskOperator = [this](gpu::Batch& batch) { + auto mesh = getMesh(); + batch.setIndexBuffer(mesh->getIndexBuffer()); + batch.setInputFormat((mesh->getVertexFormat())); + batch.setInputStream(0, mesh->getVertexStream()); + + // Draw + auto part = mesh->getPartBuffer().get(0); + batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex); + }; + + if (maskMode == StencilMaskMode::NONE) { + maskMode = args->_stencilMaskMode; + maskOperator = args->_stencilMaskOperator; + } + + if (maskMode == StencilMaskMode::NONE || (maskMode == StencilMaskMode::MESH && !maskOperator)) { return; } @@ -74,20 +94,12 @@ void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::F batch.setViewportTransform(args->_viewport); - if (_maskMode < 0) { - batch.setPipeline(getMeshStencilPipeline()); - - auto mesh = getMesh(); - batch.setIndexBuffer(mesh->getIndexBuffer()); - batch.setInputFormat((mesh->getVertexFormat())); - batch.setInputStream(0, mesh->getVertexStream()); - - // Draw - auto part = mesh->getPartBuffer().get(0); - batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex); - } else { + if (maskMode == StencilMaskMode::PAINT) { batch.setPipeline(getPaintStencilPipeline()); batch.draw(gpu::TRIANGLE_STRIP, 4); + } else if (maskMode == StencilMaskMode::MESH) { + batch.setPipeline(getMeshStencilPipeline()); + maskOperator(batch); } }); } diff --git a/libraries/render-utils/src/StencilMaskPass.h b/libraries/render-utils/src/StencilMaskPass.h index a8e4d1e1f2..bca2ef17a5 100644 --- a/libraries/render-utils/src/StencilMaskPass.h +++ b/libraries/render-utils/src/StencilMaskPass.h @@ -15,17 +15,19 @@ #include #include #include +#include class PrepareStencilConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int maskMode MEMBER maskMode NOTIFY dirty) - Q_PROPERTY(bool forceDraw MEMBER forceDraw NOTIFY dirty) + Q_PROPERTY(StencilMaskMode maskMode MEMBER maskMode NOTIFY dirty) public: PrepareStencilConfig(bool enabled = true) : JobConfig(enabled) {} - int maskMode { 0 }; - bool forceDraw { false }; + // -1 -> don't force drawing (fallback to render args mode) + // 0 -> force draw without mesh + // 1 -> force draw with mesh + StencilMaskMode maskMode { StencilMaskMode::NONE }; signals: void dirty(); @@ -66,8 +68,7 @@ private: graphics::MeshPointer _mesh; graphics::MeshPointer getMesh(); - int _maskMode { 0 }; - bool _forceDraw { false }; + StencilMaskMode _maskMode { StencilMaskMode::NONE }; }; diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index b5c98e3428..c4b823256e 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -16,6 +16,7 @@ #include #include +#include #include #include "Forward.h" @@ -133,6 +134,10 @@ namespace render { std::function _hudOperator; gpu::TexturePointer _hudTexture; + + bool _takingSnapshot { false }; + StencilMaskMode _stencilMaskMode { StencilMaskMode::NONE }; + std::function _stencilMaskOperator; }; } diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 597e537d8d..9a35796db8 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -40,6 +40,7 @@ int qMapURLStringMetaTypeId = qRegisterMetaType>(); int socketErrorMetaTypeId = qRegisterMetaType(); int voidLambdaType = qRegisterMetaType>(); int variantLambdaType = qRegisterMetaType>(); +int stencilModeMetaTypeId = qRegisterMetaType(); void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec2ToScriptValue, vec2FromScriptValue); @@ -64,6 +65,8 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue); qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue); qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue); + + qScriptRegisterMetaType(engine, stencilMaskModeToScriptValue, stencilMaskModeFromScriptValue); } QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2) { @@ -1283,4 +1286,12 @@ QVariantMap parseTexturesToMap(QString newTextures, const QVariantMap& defaultTe } return toReturn; +} + +QScriptValue stencilMaskModeToScriptValue(QScriptEngine* engine, const StencilMaskMode& stencilMode) { + return engine->newVariant((int)stencilMode); +} + +void stencilMaskModeFromScriptValue(const QScriptValue& object, StencilMaskMode& stencilMode) { + stencilMode = StencilMaskMode(object.toVariant().toInt()); } \ No newline at end of file diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index ea2c5b8354..e663a9d90f 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -25,6 +25,7 @@ #include "shared/Bilateral.h" #include "Transform.h" #include "PhysicsCollisionGroups.h" +#include "StencilMaskMode.h" class QColor; class QUrl; @@ -729,5 +730,8 @@ void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures); +Q_DECLARE_METATYPE(StencilMaskMode) +QScriptValue stencilMaskModeToScriptValue(QScriptEngine* engine, const StencilMaskMode& stencilMode); +void stencilMaskModeFromScriptValue(const QScriptValue& object, StencilMaskMode& stencilMode); #endif // hifi_RegisteredMetaTypes_h diff --git a/libraries/shared/src/StencilMaskMode.h b/libraries/shared/src/StencilMaskMode.h new file mode 100644 index 0000000000..98372890ec --- /dev/null +++ b/libraries/shared/src/StencilMaskMode.h @@ -0,0 +1,18 @@ +// +// Created by Sam Gondelman on 3/26/19. +// Copyright 2019 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_StencilMaskMode_h +#define hifi_StencilMaskMode_h + +enum class StencilMaskMode { + NONE = -1, // for legacy reasons, this is -1 + PAINT = 0, + MESH = 1 +}; + +#endif // hifi_StencilMaskMode_h \ No newline at end of file diff --git a/plugins/oculus/CMakeLists.txt b/plugins/oculus/CMakeLists.txt index abce753b4d..6ddc75e1e5 100644 --- a/plugins/oculus/CMakeLists.txt +++ b/plugins/oculus/CMakeLists.txt @@ -18,7 +18,7 @@ if (WIN32 AND (NOT USE_GLES)) link_hifi_libraries( shared task gl shaders gpu ${PLATFORM_GL_BACKEND} controllers ui qml plugins ui-plugins display-plugins input-plugins - audio-client networking render-utils + audio-client networking render-utils graphics ${PLATFORM_GL_BACKEND} ) include_hifi_library_headers(octree) diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index a67e3127e5..db26537a19 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -227,3 +227,66 @@ QVector OculusBaseDisplayPlugin::getSensorPositions() { return result; } + +DisplayPlugin::StencilMaskMeshOperator OculusBaseDisplayPlugin::getStencilMaskMeshOperator() { + if (_session) { + if (!_stencilMeshesInitialized) { + _stencilMeshesInitialized = true; + ovr::for_each_eye([&](ovrEyeType eye) { + ovrFovStencilDesc stencilDesc = { + ovrFovStencil_HiddenArea, 0, eye, + _eyeRenderDescs[eye].Fov, _eyeRenderDescs[eye].HmdToEyePose.Orientation + }; + // First we get the size of the buffer we need + ovrFovStencilMeshBuffer buffer = { 0, 0, nullptr, 0, 0, nullptr }; + ovrResult result = ovr_GetFovStencil(_session, &stencilDesc, &buffer); + if (!OVR_SUCCESS(result)) { + _stencilMeshesInitialized = false; + return; + } + + std::vector ovrVertices(buffer.UsedVertexCount); + std::vector ovrIndices(buffer.UsedIndexCount); + + // Now we populate the actual buffer + buffer = { (int)ovrVertices.size(), 0, ovrVertices.data(), (int)ovrIndices.size(), 0, ovrIndices.data() }; + result = ovr_GetFovStencil(_session, &stencilDesc, &buffer); + + if (!OVR_SUCCESS(result)) { + _stencilMeshesInitialized = false; + return; + } + + std::vector vertices; + vertices.reserve(ovrVertices.size()); + for (auto& ovrVertex : ovrVertices) { + // We need the vertices in clip space + vertices.emplace_back(ovrVertex.x - (1.0f - (float)eye), 2.0f * ovrVertex.y - 1.0f, 0.0f); + } + + std::vector indices; + indices.reserve(ovrIndices.size()); + for (auto& ovrIndex : ovrIndices) { + indices.push_back(ovrIndex); + } + + _stencilMeshes[eye] = graphics::Mesh::createIndexedTriangles_P3F((uint32_t)vertices.size(), (uint32_t)indices.size(), vertices.data(), indices.data()); + }); + } + + if (_stencilMeshesInitialized) { + return [&](gpu::Batch& batch) { + for (auto& mesh : _stencilMeshes) { + batch.setIndexBuffer(mesh->getIndexBuffer()); + batch.setInputFormat((mesh->getVertexFormat())); + batch.setInputStream(0, mesh->getVertexStream()); + + // Draw + auto part = mesh->getPartBuffer().get(0); + batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex); + } + }; + } + } + return nullptr; +} \ No newline at end of file diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index 1abb7cdad7..d442c365ae 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -16,6 +16,8 @@ #define OVRPL_DISABLED #include +#include + class OculusBaseDisplayPlugin : public HmdDisplayPlugin { using Parent = HmdDisplayPlugin; public: @@ -34,6 +36,9 @@ public: QRectF getPlayAreaRect() override; QVector getSensorPositions() override; + virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; } + virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override; + protected: void customizeContext() override; void uncustomizeContext() override; @@ -52,4 +57,7 @@ protected: // ovrLayerEyeFovDepth _depthLayer; bool _hmdMounted { false }; bool _visible { true }; + + std::array _stencilMeshes; + bool _stencilMeshesInitialized { false }; }; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 11d941dcd0..cd318dd9b4 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -784,3 +784,48 @@ QRectF OpenVrDisplayPlugin::getPlayAreaRect() { return QRectF(center.x, center.y, dimensions.x, dimensions.y); } + +DisplayPlugin::StencilMaskMeshOperator OpenVrDisplayPlugin::getStencilMaskMeshOperator() { + if (_system) { + if (!_stencilMeshesInitialized) { + _stencilMeshesInitialized = true; + for (auto eye : VR_EYES) { + vr::HiddenAreaMesh_t stencilMesh = _system->GetHiddenAreaMesh(eye); + if (stencilMesh.pVertexData && stencilMesh.unTriangleCount > 0) { + std::vector vertices; + std::vector indices; + + const int NUM_INDICES_PER_TRIANGLE = 3; + int numIndices = stencilMesh.unTriangleCount * NUM_INDICES_PER_TRIANGLE; + vertices.reserve(numIndices); + indices.reserve(numIndices); + for (int i = 0; i < numIndices; i++) { + vr::HmdVector2_t vertex2D = stencilMesh.pVertexData[i]; + // We need the vertices in clip space + vertices.emplace_back(vertex2D.v[0] - (1.0f - (float)eye), 2.0f * vertex2D.v[1] - 1.0f, 0.0f); + indices.push_back(i); + } + + _stencilMeshes[eye] = graphics::Mesh::createIndexedTriangles_P3F((uint32_t)vertices.size(), (uint32_t)indices.size(), vertices.data(), indices.data()); + } else { + _stencilMeshesInitialized = false; + } + } + } + + if (_stencilMeshesInitialized) { + return [&](gpu::Batch& batch) { + for (auto& mesh : _stencilMeshes) { + batch.setIndexBuffer(mesh->getIndexBuffer()); + batch.setInputFormat((mesh->getVertexFormat())); + batch.setInputStream(0, mesh->getVertexStream()); + + // Draw + auto part = mesh->getPartBuffer().get(0); + batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex); + } + }; + } + } + return nullptr; +} \ No newline at end of file diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 265f328920..4b042a700d 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -13,6 +13,8 @@ #include +#include + const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device property? This number is vive-only. namespace gl { @@ -67,6 +69,9 @@ public: QRectF getPlayAreaRect() override; + virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; } + virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override; + protected: bool internalActivate() override; void internalDeactivate() override; @@ -94,4 +99,7 @@ private: bool _asyncReprojectionActive { false }; bool _hmdMounted { false }; + + std::array _stencilMeshes; + bool _stencilMeshesInitialized { false }; }; From feda884c71d9be5ded70ed4dd13a8d4c5bdf92ea Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 12 Apr 2019 09:21:48 -0700 Subject: [PATCH 21/85] remove view/Independent-Mode and view/Entity-Mode menu items and related code. Leave the modes available for scripts. --- interface/src/Application.cpp | 23 ------------------- interface/src/Menu.cpp | 14 ----------- interface/src/Menu.h | 2 -- interface/src/ModelSelector.cpp | 3 --- scripts/system/hmd.js | 6 ----- .../AppDataHighFidelity/Interface.json | 2 -- 6 files changed, 50 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d1add60647..099a2ec483 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1861,12 +1861,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN); // So that camera doesn't auto-switch to third person. - } else if (Menu::getInstance()->isOptionChecked(MenuOption::IndependentMode)) { - Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); - cameraMenuChanged(); - } else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) { - Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); - cameraMenuChanged(); } { @@ -5757,9 +5751,6 @@ void Application::cycleCamera() { menu->setIsOptionChecked(MenuOption::ThirdPerson, false); menu->setIsOptionChecked(MenuOption::FullscreenMirror, true); - } else if (menu->isOptionChecked(MenuOption::IndependentMode) || menu->isOptionChecked(MenuOption::CameraEntityMode)) { - // do nothing if in independent or camera entity modes - return; } cameraMenuChanged(); // handle the menu change } @@ -5775,12 +5766,6 @@ void Application::cameraModeChanged() { case CAMERA_MODE_MIRROR: Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true); break; - case CAMERA_MODE_INDEPENDENT: - Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true); - break; - case CAMERA_MODE_ENTITY: - Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true); - break; default: break; } @@ -5824,14 +5809,6 @@ void Application::cameraMenuChanged() { getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT); } } - } else if (menu->isOptionChecked(MenuOption::IndependentMode)) { - if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { - _myCamera.setMode(CAMERA_MODE_INDEPENDENT); - } - } else if (menu->isOptionChecked(MenuOption::CameraEntityMode)) { - if (_myCamera.getMode() != CAMERA_MODE_ENTITY) { - _myCamera.setMode(CAMERA_MODE_ENTITY); - } } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e9aadea2b6..5b6774b9bc 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -194,20 +194,6 @@ Menu::Menu() { viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); - // View > Independent - auto viewIndependentAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::IndependentMode, 0, - false, qApp, SLOT(cameraMenuChanged()))); - - viewIndependentAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); - - // View > Entity Camera - auto viewEntityCameraAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::CameraEntityMode, 0, - false, qApp, SLOT(cameraMenuChanged()))); - - viewEntityCameraAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup)); - viewMenu->addSeparator(); // View > Center Player In View diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 3611faaf8f..550913bb21 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -53,7 +53,6 @@ namespace MenuOption { const QString BookmarkAvatarEntities = "Bookmark Avatar Entities"; const QString BookmarkLocation = "Bookmark Location"; const QString CalibrateCamera = "Calibrate Camera"; - const QString CameraEntityMode = "Entity Mode"; const QString CenterPlayerInView = "Center Player In View"; const QString Chat = "Chat..."; const QString ClearDiskCache = "Clear Disk Cache"; @@ -120,7 +119,6 @@ namespace MenuOption { const QString Help = "Help..."; const QString HomeLocation = "Home "; const QString IncreaseAvatarSize = "Increase Avatar Size"; - const QString IndependentMode = "Independent Mode"; const QString ActionMotorControl = "Enable Default Motor Control"; const QString LastLocation = "Last Location"; const QString LoadScript = "Open and Run Script File..."; diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp index 3223e3ab9c..6da9327cac 100644 --- a/interface/src/ModelSelector.cpp +++ b/interface/src/ModelSelector.cpp @@ -18,9 +18,6 @@ #include #include -static const QString AVATAR_HEAD_AND_BODY_STRING = "Avatar Body with Head"; -static const QString ENTITY_MODEL_STRING = "Entity Model"; - ModelSelector::ModelSelector() { QFormLayout* form = new QFormLayout(this); diff --git a/scripts/system/hmd.js b/scripts/system/hmd.js index 2d4a2d3e97..858b93ef1e 100644 --- a/scripts/system/hmd.js +++ b/scripts/system/hmd.js @@ -40,9 +40,6 @@ function updateControllerDisplay() { var button; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); -// Independent and Entity mode make people sick; disable them in hmd. -var desktopOnlyViews = ['Independent Mode', 'Entity Mode']; - var switchToVR = "ENTER VR"; var switchToDesktop = "EXIT VR"; @@ -59,9 +56,6 @@ function onHmdChanged(isHmd) { text: switchToVR }); } - desktopOnlyViews.forEach(function (view) { - Menu.setMenuEnabled("View>" + view, !isHmd); - }); updateControllerDisplay(); } diff --git a/tools/nitpick/AppDataHighFidelity/Interface.json b/tools/nitpick/AppDataHighFidelity/Interface.json index ca91a092c8..7a8a15e002 100644 --- a/tools/nitpick/AppDataHighFidelity/Interface.json +++ b/tools/nitpick/AppDataHighFidelity/Interface.json @@ -253,9 +253,7 @@ "UserActivityLoggerDisabled": false, "View/Center Player In View": true, "View/Enter First Person Mode in HMD": true, - "View/Entity Mode": false, "View/First Person": true, - "View/Independent Mode": false, "View/Mirror": false, "View/Third Person": false, "WindowGeometry": "@Rect(0 0 1920 1080)", From aef4f28194b3e397373805dd4dd879f46e52853a Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 3 Apr 2019 00:22:21 +0200 Subject: [PATCH 22/85] unused variables / missing semi-colon --- scripts/system/html/js/entityProperties.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 78a6e26ff0..a3e1bf18e1 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1528,7 +1528,6 @@ const GROUPS_PER_TYPE = { }; const EDITOR_TIMEOUT_DURATION = 1500; -const IMAGE_DEBOUNCE_TIMEOUT = 250; const DEBOUNCE_TIMEOUT = 125; const COLOR_MIN = 0; @@ -2069,7 +2068,7 @@ function createNumberProperty(property, elProperty) { type="number" ${propertyData.placeholder ? 'placeholder="' + propertyData.placeholder + '"' : ''} ${propertyData.readOnly ? 'readonly' : ''}> - `) + `); if (propertyData.min !== undefined) { elInput.setAttribute("min", propertyData.min); @@ -3312,8 +3311,6 @@ function materialTargetPropertyUpdate(propertyValue) { function loaded() { openEventBridge(function() { let elPropertiesList = document.getElementById("properties-list"); - - let templatePropertyRow = document.getElementById('property-row'); GROUPS.forEach(function(group) { let elGroup; From 71770e3f4e9b6a0fbf29a5c01a4a9d32ccf9953c Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Wed, 3 Apr 2019 00:32:05 +0200 Subject: [PATCH 23/85] move entity selection update code to its own function --- scripts/system/html/js/entityProperties.js | 637 +++++++++++---------- 1 file changed, 320 insertions(+), 317 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index a3e1bf18e1..f060e0a866 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3308,6 +3308,325 @@ function materialTargetPropertyUpdate(propertyValue) { } +function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { + if (selections.length === 0) { + if (lastEntityID !== null) { + if (editor !== null) { + saveUserData(); + deleteJSONEditor(); + } + if (materialEditor !== null) { + saveMaterialData(); + deleteJSONMaterialEditor(); + } + } + + resetProperties(); + showGroupsForType("None"); + + let elIcon = properties.type.elSpan; + elIcon.innerText = NO_SELECTION; + elIcon.style.display = 'inline-block'; + + deleteJSONEditor(); + getPropertyInputElement("userData").value = ""; + showUserDataTextArea(); + showSaveUserDataButton(); + showNewJSONEditorButton(); + + deleteJSONMaterialEditor(); + getPropertyInputElement("materialData").value = ""; + showMaterialDataTextArea(); + showSaveMaterialDataButton(); + showNewJSONMaterialEditorButton(); + + disableProperties(); + } else if (selections.length > 1) { + deleteJSONEditor(); + deleteJSONMaterialEditor(); + + let ids = []; + let types = {}; + let numTypes = 0; + + for (let i = 0; i < selections.length; ++i) { + ids.push(selections[i].id); + let currentSelectedType = selections[i].properties.type; + if (types[currentSelectedType] === undefined) { + types[currentSelectedType] = 0; + numTypes += 1; + } + types[currentSelectedType]++; + } + + let type = "Multiple"; + if (numTypes === 1) { + type = selections[0].properties.type; + } + + resetProperties(); + showGroupsForType(type); + + let typeProperty = properties["type"]; + typeProperty.elSpan.innerHTML = typeProperty.data.icons[type]; + typeProperty.elSpan.style.display = "inline-block"; + + disableProperties(); + } else { + selectedEntityProperties = selections[0].properties; + + if (lastEntityID !== selectedEntityProperties.id && lastEntityID !== null) { + if (editor !== null) { + saveUserData(); + } + if (materialEditor !== null) { + saveMaterialData(); + } + } + + let hasSelectedEntityChanged = lastEntityID !== selectedEntityProperties.id; + + if (!isPropertiesToolUpdate && !hasSelectedEntityChanged && document.hasFocus()) { + // in case the selection has not changed and we still have focus on the properties page, + // we will ignore the event. + return; + } + + let doSelectElement = !hasSelectedEntityChanged; + + // the event bridge and json parsing handle our avatar id string differently. + lastEntityID = selectedEntityProperties.id; + + showGroupsForType(selectedEntityProperties.type); + + if (selectedEntityProperties.locked) { + disableProperties(); + getPropertyInputElement("locked").removeAttribute('disabled'); + } else { + enableProperties(); + disableSaveUserDataButton(); + disableSaveMaterialDataButton() + } + + for (let propertyID in properties) { + let property = properties[propertyID]; + let propertyData = property.data; + let propertyName = property.name; + let propertyValue = getPropertyValue(propertyName); + + let isSubProperty = propertyData.subPropertyOf !== undefined; + if (propertyValue === undefined && !isSubProperty) { + continue; + } + + if (propertyData.hideIfCertified) { + let shouldHide = selectedEntityProperties.certificateID !== ""; + if (shouldHide) { + propertyValue = "** Certified **"; + property.elInput.disabled = true; + } + } + + let isPropertyNotNumber = false; + switch (propertyData.type) { + case 'number': + case 'number-draggable': + isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; + break; + case 'rect': + case 'vec3': + case 'vec2': + isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; + break; + case 'color': + isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; + break; + } + if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { + propertyValue = getPropertyValue(propertyData.fallbackProperty); + } + + switch (propertyData.type) { + case 'string': { + property.elInput.value = propertyValue; + break; + } + case 'bool': { + let inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; + if (isSubProperty) { + let propertyValue = selectedEntityProperties[propertyData.subPropertyOf]; + let subProperties = propertyValue.split(","); + let subPropertyValue = subProperties.indexOf(propertyName) > -1; + property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; + } else { + property.elInput.checked = inverse ? !propertyValue : propertyValue; + } + break; + } + case 'number': { + property.elInput.value = propertyValue; + break; + } + case 'number-draggable': { + let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + let value = propertyValue / multiplier; + if (propertyData.round !== undefined) { + value = Math.round(value.round) / propertyData.round; + } + property.elNumber.setValue(value); + break; + } + case 'rect': + property.elNumberX.setValue(propertyValue.x); + property.elNumberY.setValue(propertyValue.y); + property.elNumberWidth.setValue(propertyValue.width); + property.elNumberHeight.setValue(propertyValue.height); + break; + case 'vec3': + case 'vec2': { + let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + let valueX = propertyValue.x / multiplier; + let valueY = propertyValue.y / multiplier; + let valueZ = propertyValue.z / multiplier; + if (propertyData.round !== undefined) { + valueX = Math.round(valueX * propertyData.round) / propertyData.round; + valueY = Math.round(valueY * propertyData.round) / propertyData.round; + valueZ = Math.round(valueZ * propertyData.round) / propertyData.round; + } + if (propertyData.decimals !== undefined) { + property.elNumberX.setValue(valueX.toFixed(propertyData.decimals)); + property.elNumberY.setValue(valueY.toFixed(propertyData.decimals)); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(valueZ.toFixed(propertyData.decimals)); + } + } else { + property.elNumberX.setValue(valueX); + property.elNumberY.setValue(valueY); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(valueZ); + } + } + break; + } + case 'color': { + property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," + + propertyValue.green + "," + + propertyValue.blue + ")"; + if (hasSelectedEntityChanged && $(property.elColorPicker).attr('active') === 'true') { + // Set the color picker inactive before setting the color, + // otherwise an update will be sent directly after setting it here. + $(property.elColorPicker).attr('active', 'false'); + colorPickers['#' + property.elementID].colpickSetColor({ + "r": propertyValue.red, + "g": propertyValue.green, + "b": propertyValue.blue + }); + $(property.elColorPicker).attr('active', 'true'); + } + + property.elNumberR.setValue(propertyValue.red); + property.elNumberG.setValue(propertyValue.green); + property.elNumberB.setValue(propertyValue.blue); + break; + } + case 'dropdown': { + property.elInput.value = propertyValue; + setDropdownText(property.elInput); + break; + } + case 'textarea': { + property.elInput.value = propertyValue; + setTextareaScrolling(property.elInput); + break; + } + case 'icon': { + property.elSpan.innerHTML = propertyData.icons[propertyValue]; + property.elSpan.style.display = "inline-block"; + break; + } + case 'texture': { + property.elInput.value = propertyValue; + property.elInput.imageLoad(property.elInput.value); + break; + } + case 'dynamic-multiselect': { + if (property.data.propertyUpdate) { + property.data.propertyUpdate(propertyValue); + } + break; + } + } + + let showPropertyRules = property.showPropertyRules; + if (showPropertyRules !== undefined) { + for (let propertyToShow in showPropertyRules) { + let showIfThisPropertyValue = showPropertyRules[propertyToShow]; + let show = String(propertyValue) === String(showIfThisPropertyValue); + showPropertyElement(propertyToShow, show); + } + } + } + + updateVisibleSpaceModeProperties(); + + + let json = null; + try { + json = JSON.parse(selectedEntityProperties.userData); + } catch (e) { + // normal text + deleteJSONEditor(); + getPropertyInputElement("userData").value = selectedEntityProperties.userData; + showUserDataTextArea(); + showNewJSONEditorButton(); + hideSaveUserDataButton(); + hideUserDataSaved(); + } + if (json !== null) { + if (editor === null) { + createJSONEditor(); + } + setEditorJSON(json); + showSaveUserDataButton(); + hideUserDataTextArea(); + hideNewJSONEditorButton(); + hideUserDataSaved(); + } + + let materialJson = null; + try { + materialJson = JSON.parse(selectedEntityProperties.materialData); + } catch (e) { + // normal text + deleteJSONMaterialEditor(); + getPropertyInputElement("materialData").value = selectedEntityProperties.materialData; + showMaterialDataTextArea(); + showNewJSONMaterialEditorButton(); + hideSaveMaterialDataButton(); + hideMaterialDataSaved(); + } + if (materialJson !== null) { + if (materialEditor === null) { + createJSONMaterialEditor(); + } + setMaterialEditorJSON(materialJson); + showSaveMaterialDataButton(); + hideMaterialDataTextArea(); + hideNewJSONMaterialEditorButton(); + hideMaterialDataSaved(); + } + + if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { + requestMaterialTarget(); + } + + let activeElement = document.activeElement; + if (doSelectElement && typeof activeElement.select !== "undefined") { + activeElement.select(); + } + } +} + function loaded() { openEventBridge(function() { let elPropertiesList = document.getElementById("properties-list"); @@ -3489,323 +3808,7 @@ function loaded() { if (data.spaceMode !== undefined) { currentSpaceMode = data.spaceMode === "local" ? PROPERTY_SPACE_MODE.LOCAL : PROPERTY_SPACE_MODE.WORLD; } - if (data.selections.length === 0) { - if (lastEntityID !== null) { - if (editor !== null) { - saveUserData(); - deleteJSONEditor(); - } - if (materialEditor !== null) { - saveMaterialData(); - deleteJSONMaterialEditor(); - } - } - - resetProperties(); - showGroupsForType("None"); - - let elIcon = properties.type.elSpan; - elIcon.innerText = NO_SELECTION; - elIcon.style.display = 'inline-block'; - - deleteJSONEditor(); - getPropertyInputElement("userData").value = ""; - showUserDataTextArea(); - showSaveUserDataButton(); - showNewJSONEditorButton(); - - deleteJSONMaterialEditor(); - getPropertyInputElement("materialData").value = ""; - showMaterialDataTextArea(); - showSaveMaterialDataButton(); - showNewJSONMaterialEditorButton(); - - disableProperties(); - } else if (data.selections.length > 1) { - deleteJSONEditor(); - deleteJSONMaterialEditor(); - - let selections = data.selections; - - let ids = []; - let types = {}; - let numTypes = 0; - - for (let i = 0; i < selections.length; ++i) { - ids.push(selections[i].id); - let currentSelectedType = selections[i].properties.type; - if (types[currentSelectedType] === undefined) { - types[currentSelectedType] = 0; - numTypes += 1; - } - types[currentSelectedType]++; - } - - let type = "Multiple"; - if (numTypes === 1) { - type = selections[0].properties.type; - } - - resetProperties(); - showGroupsForType(type); - - let typeProperty = properties["type"]; - typeProperty.elSpan.innerHTML = typeProperty.data.icons[type]; - typeProperty.elSpan.style.display = "inline-block"; - - disableProperties(); - } else { - selectedEntityProperties = data.selections[0].properties; - - if (lastEntityID !== '"' + selectedEntityProperties.id + '"' && lastEntityID !== null) { - if (editor !== null) { - saveUserData(); - } - if (materialEditor !== null) { - saveMaterialData(); - } - } - - let hasSelectedEntityChanged = lastEntityID !== '"' + selectedEntityProperties.id + '"'; - - if (!data.isPropertiesToolUpdate && !hasSelectedEntityChanged && document.hasFocus()) { - // in case the selection has not changed and we still have focus on the properties page, - // we will ignore the event. - return; - } - - let doSelectElement = !hasSelectedEntityChanged; - - // the event bridge and json parsing handle our avatar id string differently. - lastEntityID = '"' + selectedEntityProperties.id + '"'; - - showGroupsForType(selectedEntityProperties.type); - - if (selectedEntityProperties.locked) { - disableProperties(); - getPropertyInputElement("locked").removeAttribute('disabled'); - } else { - enableProperties(); - disableSaveUserDataButton(); - disableSaveMaterialDataButton() - } - - for (let propertyID in properties) { - let property = properties[propertyID]; - let propertyData = property.data; - let propertyName = property.name; - let propertyValue = getPropertyValue(propertyName); - - let isSubProperty = propertyData.subPropertyOf !== undefined; - if (propertyValue === undefined && !isSubProperty) { - continue; - } - - if (propertyData.hideIfCertified) { - let shouldHide = selectedEntityProperties.certificateID !== ""; - if (shouldHide) { - propertyValue = "** Certified **"; - property.elInput.disabled = true; - } - } - - let isPropertyNotNumber = false; - switch (propertyData.type) { - case 'number': - case 'number-draggable': - isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; - break; - case 'rect': - case 'vec3': - case 'vec2': - isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; - break; - case 'color': - isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; - break; - } - if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { - propertyValue = getPropertyValue(propertyData.fallbackProperty); - } - - switch (propertyData.type) { - case 'string': { - property.elInput.value = propertyValue; - break; - } - case 'bool': { - let inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; - if (isSubProperty) { - let propertyValue = selectedEntityProperties[propertyData.subPropertyOf]; - let subProperties = propertyValue.split(","); - let subPropertyValue = subProperties.indexOf(propertyName) > -1; - property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; - } else { - property.elInput.checked = inverse ? !propertyValue : propertyValue; - } - break; - } - case 'number': { - property.elInput.value = propertyValue; - break; - } - case 'number-draggable': { - let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - let value = propertyValue / multiplier; - if (propertyData.round !== undefined) { - value = Math.round(value.round) / propertyData.round; - } - property.elNumber.setValue(value); - break; - } - case 'rect': - property.elNumberX.setValue(propertyValue.x); - property.elNumberY.setValue(propertyValue.y); - property.elNumberWidth.setValue(propertyValue.width); - property.elNumberHeight.setValue(propertyValue.height); - break; - case 'vec3': - case 'vec2': { - let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - let valueX = propertyValue.x / multiplier; - let valueY = propertyValue.y / multiplier; - let valueZ = propertyValue.z / multiplier; - if (propertyData.round !== undefined) { - valueX = Math.round(valueX * propertyData.round) / propertyData.round; - valueY = Math.round(valueY * propertyData.round) / propertyData.round; - valueZ = Math.round(valueZ * propertyData.round) / propertyData.round; - } - if (propertyData.decimals !== undefined) { - property.elNumberX.setValue(valueX.toFixed(propertyData.decimals)); - property.elNumberY.setValue(valueY.toFixed(propertyData.decimals)); - if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(valueZ.toFixed(propertyData.decimals)); - } - } else { - property.elNumberX.setValue(valueX); - property.elNumberY.setValue(valueY); - if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(valueZ); - } - } - break; - } - case 'color': { - property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," + - propertyValue.green + "," + - propertyValue.blue + ")"; - if (hasSelectedEntityChanged && $(property.elColorPicker).attr('active') === 'true') { - // Set the color picker inactive before setting the color, - // otherwise an update will be sent directly after setting it here. - $(property.elColorPicker).attr('active', 'false'); - colorPickers['#' + property.elementID].colpickSetColor({ - "r": propertyValue.red, - "g": propertyValue.green, - "b": propertyValue.blue - }); - $(property.elColorPicker).attr('active', 'true'); - } - - property.elNumberR.setValue(propertyValue.red); - property.elNumberG.setValue(propertyValue.green); - property.elNumberB.setValue(propertyValue.blue); - break; - } - case 'dropdown': { - property.elInput.value = propertyValue; - setDropdownText(property.elInput); - break; - } - case 'textarea': { - property.elInput.value = propertyValue; - setTextareaScrolling(property.elInput); - break; - } - case 'icon': { - property.elSpan.innerHTML = propertyData.icons[propertyValue]; - property.elSpan.style.display = "inline-block"; - break; - } - case 'texture': { - property.elInput.value = propertyValue; - property.elInput.imageLoad(property.elInput.value); - break; - } - case 'dynamic-multiselect': { - if (property.data.propertyUpdate) { - property.data.propertyUpdate(propertyValue); - } - break; - } - } - - let showPropertyRules = property.showPropertyRules; - if (showPropertyRules !== undefined) { - for (let propertyToShow in showPropertyRules) { - let showIfThisPropertyValue = showPropertyRules[propertyToShow]; - let show = String(propertyValue) === String(showIfThisPropertyValue); - showPropertyElement(propertyToShow, show); - } - } - } - - updateVisibleSpaceModeProperties(); - - let json = null; - try { - json = JSON.parse(selectedEntityProperties.userData); - } catch (e) { - // normal text - deleteJSONEditor(); - getPropertyInputElement("userData").value = selectedEntityProperties.userData; - showUserDataTextArea(); - showNewJSONEditorButton(); - hideSaveUserDataButton(); - hideUserDataSaved(); - } - if (json !== null) { - if (editor === null) { - createJSONEditor(); - } - setEditorJSON(json); - showSaveUserDataButton(); - hideUserDataTextArea(); - hideNewJSONEditorButton(); - hideUserDataSaved(); - } - - let materialJson = null; - try { - materialJson = JSON.parse(selectedEntityProperties.materialData); - } catch (e) { - // normal text - deleteJSONMaterialEditor(); - getPropertyInputElement("materialData").value = selectedEntityProperties.materialData; - showMaterialDataTextArea(); - showNewJSONMaterialEditorButton(); - hideSaveMaterialDataButton(); - hideMaterialDataSaved(); - } - if (materialJson !== null) { - if (materialEditor === null) { - createJSONMaterialEditor(); - } - setMaterialEditorJSON(materialJson); - showSaveMaterialDataButton(); - hideMaterialDataTextArea(); - hideNewJSONMaterialEditorButton(); - hideMaterialDataSaved(); - } - - if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { - requestMaterialTarget(); - } - - let activeElement = document.activeElement; - if (doSelectElement && typeof activeElement.select !== "undefined") { - activeElement.select(); - } - } + handleEntitySelectionUpdate(data.selections, data.isPropertiesToolUpdate); } else if (data.type === 'tooltipsReply') { createAppTooltip.setIsEnabled(!data.hmdActive); createAppTooltip.setTooltipData(data.tooltips); From 8e97c4f3f208c6c062eb6cd20c6fd27d62dd3082 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 8 Apr 2019 15:52:37 +0200 Subject: [PATCH 24/85] multi-editing support for properties --- scripts/system/edit.js | 87 ++- scripts/system/html/css/edit-style.css | 59 +- scripts/system/html/js/draggableNumber.js | 74 +- scripts/system/html/js/entityProperties.js | 760 +++++++++++++-------- 4 files changed, 631 insertions(+), 349 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 0346e1c7a1..dbb429e86a 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2361,41 +2361,64 @@ var PropertiesTool = function (opts) { } var i, properties, dY, diff, newPosition; if (data.type === "update") { - if (selectionManager.selections.length > 1) { - for (i = 0; i < selectionManager.selections.length; i++) { - Entities.editEntity(selectionManager.selections[i], data.properties); + + if (data.properties || data.propertiesMap) { + var propertiesMap = data.propertiesMap; + if (propertiesMap === undefined) { + var updateEntityIDs = data.ids !== undefined ? data.ids : selectionManager.selections; + propertiesMap = []; + propertiesMap.push({ + entityIDs: updateEntityIDs, + properties: data.properties + }); } - } else if (data.properties) { - if (data.properties.dynamic === false) { - // this object is leaving dynamic, so we zero its velocities - data.properties.localVelocity = Vec3.ZERO; - data.properties.localAngularVelocity = Vec3.ZERO; - } - if (data.properties.rotation !== undefined) { - data.properties.rotation = Quat.fromVec3Degrees(data.properties.rotation); - } - if (data.properties.localRotation !== undefined) { - data.properties.localRotation = Quat.fromVec3Degrees(data.properties.localRotation); - } - if (data.properties.emitOrientation !== undefined) { - data.properties.emitOrientation = Quat.fromVec3Degrees(data.properties.emitOrientation); - } - if (data.properties.keyLight !== undefined && data.properties.keyLight.direction !== undefined) { - var currentKeyLightDirection = Vec3.toPolar(Entities.getEntityProperties(selectionManager.selections[0], ['keyLight.direction']).keyLight.direction); - if (data.properties.keyLight.direction.x === undefined) { - data.properties.keyLight.direction.x = currentKeyLightDirection.x; + + var sendListUpdate = false; + propertiesMap.forEach(function(propertiesObject) { + var properties = propertiesObject.properties; + var updateEntityIDs = propertiesObject.entityIDs; + if (properties.dynamic === false) { + // this object is leaving dynamic, so we zero its velocities + properties.localVelocity = Vec3.ZERO; + properties.localAngularVelocity = Vec3.ZERO; } - if (data.properties.keyLight.direction.y === undefined) { - data.properties.keyLight.direction.y = currentKeyLightDirection.y; + if (properties.rotation !== undefined) { + properties.rotation = Quat.fromVec3Degrees(properties.rotation); } - data.properties.keyLight.direction = Vec3.fromPolar(data.properties.keyLight.direction.x, data.properties.keyLight.direction.y); - } - Entities.editEntity(selectionManager.selections[0], data.properties); - if (data.properties.name !== undefined || data.properties.modelURL !== undefined || data.properties.materialURL !== undefined || - data.properties.visible !== undefined || data.properties.locked !== undefined) { + if (properties.localRotation !== undefined) { + properties.localRotation = Quat.fromVec3Degrees(properties.localRotation); + } + if (properties.emitOrientation !== undefined) { + properties.emitOrientation = Quat.fromVec3Degrees(properties.emitOrientation); + } + if (properties.keyLight !== undefined && properties.keyLight.direction !== undefined) { + var currentKeyLightDirection = Vec3.toPolar(Entities.getEntityProperties(selectionManager.selections[0], ['keyLight.direction']).keyLight.direction); + if (properties.keyLight.direction.x === undefined) { + properties.keyLight.direction.x = currentKeyLightDirection.x; + } + if (properties.keyLight.direction.y === undefined) { + properties.keyLight.direction.y = currentKeyLightDirection.y; + } + properties.keyLight.direction = Vec3.fromPolar(properties.keyLight.direction.x, properties.keyLight.direction.y); + } + + updateEntityIDs.forEach(function (entityID) { + Entities.editEntity(entityID, properties); + }); + + if (properties.name !== undefined || properties.modelURL !== undefined || properties.materialURL !== undefined || + properties.visible !== undefined || properties.locked !== undefined) { + + sendListUpdate = true; + } + + }); + if (sendListUpdate) { entityListTool.sendUpdate(); } } + + if (data.onlyUpdateEntities) { blockPropertyUpdates = true; } else { @@ -2405,9 +2428,9 @@ var PropertiesTool = function (opts) { selectionManager._update(false, this); blockPropertyUpdates = false; } else if (data.type === 'saveUserData' || data.type === 'saveMaterialData') { - //the event bridge and json parsing handle our avatar id string differently. - var actualID = data.id.split('"')[1]; - Entities.editEntity(actualID, data.properties); + data.ids.forEach(function(entityID) { + Entities.editEntity(entityID, data.properties); + }); } else if (data.type === "showMarketplace") { showMarketplace(); } else if (data.type === "action") { diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index d6a281b0c4..6190469c98 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -193,8 +193,8 @@ td { } td.hidden { - padding-left: 0px; - padding-right: 0px; + padding-left: 0; + padding-right: 0; } td.url { @@ -262,6 +262,40 @@ input[type="text"] { width: 100%; } +input.multi-diff:not(:focus) + span.multi-diff, +.draggable-number.multi-diff span.multi-diff, +dl>dt.multi-diff:not(:focus) + span.multi-diff { + visibility: visible; + position: absolute; + display: inline-block; + z-index: 2; + left: 20px; + width: 50px; + height: 40%; + background-image: linear-gradient(transparent 0%, transparent 10%, #afafaf 10%, #afafaf 20%, transparent 20%, transparent 45%, #afafaf 45%, #afafaf 55%, transparent 55%, transparent 80%, #afafaf 80%, #afafaf 90%, transparent 90%, transparent 100%); + background-repeat: no-repeat; + pointer-events: none; +} + +input.multi-diff:not(:focus)::-webkit-input-placeholder, input.multi-diff:not(:focus) { + color: transparent; +} + +.draggable-number.multi-diff .text { + color: transparent; +} + +.dropdown > span.multi-diff { + top: 5px; + left: 10px; +} + +.text, .url { + position: relative; + display: flex; + align-items: center; +} + input[type="search"] { height: 28px; width: 100%; @@ -333,7 +367,7 @@ input[type=range]::-webkit-slider-thumb { input[type=range]::-webkit-slider-thumb:hover { background-color: white; } -input[type=range]:focus { /*#252525*/ +input[type=range]:focus { outline: none; } @@ -443,6 +477,12 @@ input[type=checkbox]:checked + label { input[type=checkbox]:checked + label:hover { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEySURBVDhPnZLPSsNAEMa/XVPBCE0RhNy0OarP4Av4AD6JB0GwVBA8efBBxHsgh4CQswcRoUIpiIpVAm3zZ5M4szFSbQPBH3xkJvNNZskOer2eLIriKM/ze1JOcS1UHmdZduF5ngEKjr/fN4Z6+oKerwA2gxC4HAFPEWVLsAzgZAvYt3Q6Enw6jg7uBAaTFMNwhpnKdbXCkAJdy8ROu4XrXW2HTJIErHcFDD6nC02Mom8PwymeE2gvS0ZRBBaTlsOXEmdlrfLLOI7Bakrl/zWxCT8T/904f9QW/b06qtrCUdtFCqdjYs2Q2jAPX8c2XQd7Kr/wfV8vwIPs4Ga1ixe5Xrr/YFLTYfKIvWzM6ZtwXZdX7lxXG0L+sxXHcW5t254opRzawQ0S72+dPmjTroIgOP0CQSMt5LDn1T8AAAAASUVORK5CYII=); } +input.multi-diff[type=checkbox] + label { + background-image: url(data:image/png;base64,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) +} +input.multi-diff[type=checkbox] + label:hover { + background-image: url(data:image/png;base64,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) +} .icon-input input { position: relative; @@ -731,8 +771,6 @@ span.indented { .dropdown dl { clear: both; cursor: pointer; -} -.dropdown dl { font-family: FiraSans-SemiBold; font-size: 15px; width: 292px; @@ -741,7 +779,10 @@ span.indented { color: #afafaf; background: #575757; position: relative; + display: flex; + align-items: center; } + .dropdown dl[dropped="true"] { color: #404040; background: linear-gradient(#afafaf, #afafaf); @@ -878,6 +919,8 @@ div.refresh { div.refresh input[type="button"] { float: right; margin-right: -44px; + position: relative; + left: 10px; } .color-picker { @@ -930,6 +973,8 @@ div.refresh input[type="button"] { position: relative; height: 28px; flex: 0 1 124px; + display: flex; + align-items: center; } .draggable-number .text { @@ -1735,3 +1780,7 @@ input[type=number].hide-spinner::-webkit-inner-spin-button { -webkit-appearance: none; visibility: hidden; } + +div.jsoneditor-menu a.jsoneditor-poweredBy { + display: none; +} diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 0d6af01ebd..96280b82f6 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -13,6 +13,7 @@ function DraggableNumber(min, max, step, decimals, dragStart, dragEnd) { this.min = min; this.max = max; this.step = step !== undefined ? step : 1; + this.multiDiffModeEnabled = false; this.decimals = decimals; this.dragStartFunction = dragStart; this.dragEndFunction = dragEnd; @@ -20,6 +21,7 @@ function DraggableNumber(min, max, step, decimals, dragStart, dragEnd) { this.initialMouseEvent = null; this.lastMouseEvent = null; this.valueChangeFunction = null; + this.multiDiffDragFunction = null; this.initialize(); } @@ -71,21 +73,26 @@ DraggableNumber.prototype = { } if (this.dragging && this.lastMouseEvent) { let initialValue = this.elInput.value; - let dx = event.clientX - this.lastMouseEvent.clientX; - let changeValue = dx !== 0; - if (changeValue) { - while (dx !== 0) { - if (dx > 0) { - this.elInput.stepUp(); - --dx; - } else { - this.elInput.stepDown(); - ++dx; + let changeDelta = event.clientX - this.lastMouseEvent.clientX; + if (changeDelta !== 0) { + if (this.multiDiffModeEnabled) { + if (this.multiDiffDragFunction) { + this.multiDiffDragFunction(changeDelta * this.step); + } + } else { + while (changeDelta !== 0) { + if (changeDelta > 0) { + this.elInput.stepUp(); + --changeDelta; + } else { + this.elInput.stepDown(); + ++changeDelta; + } + } + this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); } - } - this.inputChange(); - if (this.valueChangeFunction) { - this.valueChangeFunction(); } } this.lastMouseEvent = event; @@ -124,20 +131,39 @@ DraggableNumber.prototype = { } }, - setValue: function(newValue) { - if (newValue !== "" && this.decimals !== undefined) { - this.elInput.value = parseFloat(newValue).toFixed(this.decimals); - } else { - this.elInput.value = newValue; + setValue: function(newValue, isMultiDiff) { + if (isMultiDiff !== undefined) { + this.setMultiDiff(isMultiDiff); + } + + if (!isMultiDiff) { + if (newValue !== "" && this.decimals !== undefined) { + this.elInput.value = parseFloat(newValue).toFixed(this.decimals); + } else { + this.elInput.value = newValue; + } + this.elText.firstChild.data = this.elInput.value; + } + }, + + setMultiDiff: function(isMultiDiff) { + this.multiDiffModeEnabled = isMultiDiff; + if (isMultiDiff) { + this.elDiv.classList.add('multi-diff'); + } else { + this.elDiv.classList.remove('multi-diff'); } - this.elText.firstChild.data = this.elInput.value; }, setValueChangeFunction: function(valueChangeFunction) { this.valueChangeFunction = valueChangeFunction.bind(this.elInput); this.elInput.addEventListener("change", this.valueChangeFunction); }, - + + setMultiDiffDragFunction: function(multiDiffDragFunction) { + this.multiDiffDragFunction = multiDiffDragFunction; + }, + inputChange: function() { let value = this.elInput.value; if (this.max !== undefined) { @@ -203,7 +229,10 @@ DraggableNumber.prototype = { this.elRightArrow.className = 'right-arrow'; this.elRightArrow.innerHTML = 'D'; this.elRightArrow.addEventListener("click", this.onStepUp); - + + this.elMultiDiff = document.createElement('span'); + this.elMultiDiff.className = 'multi-diff'; + this.elInput = document.createElement('input'); this.elInput.className = "input"; this.elInput.setAttribute("type", "number"); @@ -220,6 +249,7 @@ DraggableNumber.prototype = { this.elDiv.appendChild(this.elLeftArrow); this.elDiv.appendChild(this.elText); this.elDiv.appendChild(this.elInput); + this.elDiv.appendChild(this.elMultiDiff); this.elDiv.appendChild(this.elRightArrow); } }; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index f060e0a866..df244d435a 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html /* global alert, augmentSpinButtons, clearTimeout, console, document, Element, - EventBridge, JSONEditor, openEventBridge, setTimeout, window, _ $ */ + EventBridge, JSONEditor, openEventBridge, setTimeout, window, _, $ */ const DEGREES_TO_RADIANS = Math.PI / 180.0; @@ -20,6 +20,16 @@ const PROPERTY_SPACE_MODE = { WORLD: 2 }; +// Multiple-selection behavior +const PROPERTY_MULTI_DISPLAY_MODE = { + DEFAULT: 0, + /** + * Comma separated values + * Limited for properties with type "string" or "textarea" and readOnly enabled + */ + COMMA_SEPARATED_VALUES: 1, +}; + const GROUPS = [ { id: "base", @@ -45,6 +55,7 @@ const GROUPS = [ placeholder: "ID", readOnly: true, replaceID: "placeholder-property-id", + multiDisplayMode: PROPERTY_MULTI_DISPLAY_MODE.COMMA_SEPARATED_VALUES, }, { label: "Description", @@ -1552,7 +1563,7 @@ const ENABLE_DISABLE_SELECTOR = "input, textarea, span, .dropdown dl, .color-pic const PROPERTY_NAME_DIVISION = { GROUP: 0, PROPERTY: 1, - SUBPROPERTY: 2, + SUB_PROPERTY: 2, }; const RECT_ELEMENTS = { @@ -1587,11 +1598,53 @@ let properties = {}; let propertyRangeRequests = []; let colorPickers = {}; let particlePropertyUpdates = {}; -let selectedEntityProperties; -let lastEntityID = null; +let selectedEntityIDs = new Set(); +let currentSelections = []; let createAppTooltip = new CreateAppTooltip(); let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; + + +// mergeDeep function from https://stackoverflow.com/a/34749873 +/** + * Simple object check. + * @param item + * @returns {boolean} + */ +function isObject(item) { + return (item && typeof item === 'object' && !Array.isArray(item)); +} + +/** + * Deep merge two objects. + * @param target + * @param sources + */ +function mergeDeep(target, ...sources) { + if (!sources.length) { + return target; + } + const source = sources.shift(); + + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (!source.hasOwnProperty(key)) { + continue; + } + if (isObject(source[key])) { + if (!target[key]) { + Object.assign(target, { [key]: {} }); + } + mergeDeep(target[key], source[key]); + } else { + Object.assign(target, { [key]: source[key] }); + } + } + } + + return mergeDeep(target, ...sources); +} + function createElementFromHTML(htmlString) { let elTemplate = document.createElement('template'); elTemplate.innerHTML = htmlString.trim(); @@ -1691,6 +1744,7 @@ function resetProperties() { switch (propertyData.type) { case 'number': case 'string': { + property.elInput.classList.remove('multi-diff'); if (propertyData.defaultValue !== undefined) { property.elInput.value = propertyData.defaultValue; } else { @@ -1699,6 +1753,7 @@ function resetProperties() { break; } case 'bool': { + property.elInput.classList.remove('multi-diff'); property.elInput.checked = false; break; } @@ -1765,7 +1820,11 @@ function resetProperties() { } } } - + + resetServerScriptStatus(); +} + +function resetServerScriptStatus() { let elServerScriptError = document.getElementById("property-serverScripts-error"); let elServerScriptStatus = document.getElementById("property-serverScripts-status"); elServerScriptError.parentElement.style.display = "none"; @@ -1774,43 +1833,71 @@ function resetProperties() { function showGroupsForType(type) { if (type === "Box" || type === "Sphere") { - type = "Shape"; + showGroupsForTypes(["Shape"]); + return; } - - let typeGroups = GROUPS_PER_TYPE[type]; + showGroupsForTypes([type]); +} - for (let groupKey in elGroups) { - let elGroup = elGroups[groupKey]; - if (typeGroups && typeGroups.indexOf(groupKey) > -1) { +function showGroupsForTypes(types) { + Object.entries(elGroups).forEach(([groupKey, elGroup]) => { + if (types.map(type => GROUPS_PER_TYPE[type].includes(groupKey)).every(function (hasGroup) { return hasGroup; })) { elGroup.style.display = "block"; } else { elGroup.style.display = "none"; } - } + }); } -function getPropertyValue(originalPropertyName) { - // if this is a compound property name (i.e. animation.running) +function getMultiplePropertyValue(originalPropertyName) { + // if this is a compound property name (i.e. animation.running) // then split it by . up to 3 times to find property value - let propertyValue; + let propertyValues = []; let splitPropertyName = originalPropertyName.split('.'); if (splitPropertyName.length > 1) { let propertyGroupName = splitPropertyName[PROPERTY_NAME_DIVISION.GROUP]; let propertyName = splitPropertyName[PROPERTY_NAME_DIVISION.PROPERTY]; - let groupProperties = selectedEntityProperties[propertyGroupName]; - if (groupProperties === undefined || groupProperties[propertyName] === undefined) { - return undefined; - } - if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUBPROPERTY + 1) { - let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUBPROPERTY]; - propertyValue = groupProperties[propertyName][subPropertyName]; - } else { - propertyValue = groupProperties[propertyName]; - } + propertyValue = currentSelections.map(selection => { + let groupProperties = selection.properties[propertyGroupName]; + if (groupProperties === undefined || groupProperties[propertyName] === undefined) { + return undefined; + } + if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUB_PROPERTY + 1) { + let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUB_PROPERTY]; + return groupProperties[propertyName][subPropertyName]; + } else { + return groupProperties[propertyName]; + } + }); } else { - propertyValue = selectedEntityProperties[originalPropertyName]; + propertyValues = currentSelections.map(selection => selection.properties[originalPropertyName]); } - return propertyValue; + + let detailedValues = []; + propertyValues.forEach(function(propertyValue) { + if (typeof propertyValues === "object") { + + } else { + detailedValues.push(propertyValue); + } + }); + + const uniquePropertyValues = [...new Set(propertyValues)]; + const isMultiDiffValue = uniquePropertyValues.length > 1; + + if (isMultiDiffValue) { + return { + value: undefined, + values: propertyValues, + isMultiDiffValue: true + } + } + + return { + value: uniquePropertyValues[0], + values: propertyValues, + isMultiDiffValue: false + }; } function updateVisibleSpaceModeProperties() { @@ -1828,12 +1915,11 @@ function updateVisibleSpaceModeProperties() { } } - /** * PROPERTY UPDATE FUNCTIONS */ -function updateProperty(originalPropertyName, propertyValue, isParticleProperty) { +function createPropertyUpdateObject(originalPropertyName, propertyValue) { let propertyUpdate = {}; // if this is a compound property name (i.e. animation.running) then split it by . up to 3 times let splitPropertyName = originalPropertyName.split('.'); @@ -1841,8 +1927,8 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) let propertyGroupName = splitPropertyName[PROPERTY_NAME_DIVISION.GROUP]; let propertyName = splitPropertyName[PROPERTY_NAME_DIVISION.PROPERTY]; propertyUpdate[propertyGroupName] = {}; - if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUBPROPERTY + 1) { - let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUBPROPERTY]; + if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUB_PROPERTY + 1) { + let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUB_PROPERTY]; propertyUpdate[propertyGroupName][propertyName] = {}; propertyUpdate[propertyGroupName][propertyName][subPropertyName] = propertyValue; } else { @@ -1851,6 +1937,12 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) } else { propertyUpdate[originalPropertyName] = propertyValue; } + return propertyUpdate; +} + +function updateProperty(originalPropertyName, propertyValue, isParticleProperty) { + let propertyUpdate = createPropertyUpdateObject(originalPropertyName, propertyValue); + // queue up particle property changes with the debounced sync to avoid // causing particle emitting to reset excessively with each value change if (isParticleProperty) { @@ -1860,7 +1952,7 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) particleSyncDebounce(); } else { // only update the entity property value itself if in the middle of dragging - // prevent undo command push, saving new property values, and property update + // prevent undo command push, saving new property values, and property update // callback until drag is complete (additional update sent via dragEnd callback) let onlyUpdateEntity = properties[originalPropertyName] && properties[originalPropertyName].dragging === true; updateProperties(propertyUpdate, onlyUpdateEntity); @@ -1877,15 +1969,32 @@ function updateProperties(propertiesToUpdate, onlyUpdateEntity) { onlyUpdateEntity = false; } EventBridge.emitWebEvent(JSON.stringify({ - id: lastEntityID, + ids: [...selectedEntityIDs], type: "update", properties: propertiesToUpdate, onlyUpdateEntities: onlyUpdateEntity })); } +function updateMultiDiffProperties(propertiesMapToUpdate, onlyUpdateEntity) { + if (onlyUpdateEntity === undefined) { + onlyUpdateEntity = false; + } + EventBridge.emitWebEvent(JSON.stringify({ + type: "update", + propertiesMap: propertiesMapToUpdate, + onlyUpdateEntities: onlyUpdateEntity + })); + console.log(JSON.stringify({ + type: "update", + propertiesMap: propertiesMapToUpdate, + onlyUpdateEntities: onlyUpdateEntity + })) +} + function createEmitTextPropertyUpdateFunction(property) { return function() { + property.elInput.classList.remove('multi-diff'); updateProperty(property.name, this.value, property.isParticleProperty); }; } @@ -1905,8 +2014,26 @@ function createDragStartFunction(property) { function createDragEndFunction(property) { return function() { property.dragging = false; - // send an additional update post-dragging to consider whole property change from dragStart to dragEnd to be 1 action - this.valueChangeFunction(); + + if (this.multiDiffModeEnabled) { + let propertyMultiValue = getMultiplePropertyValue(property.name); + let updateObjects = []; + const selectedEntityIDsArray = [...selectedEntityIDs]; + + for (var i = 0; i < selectedEntityIDsArray.length; ++i) { + let entityID = selectedEntityIDsArray[i]; + updateObjects.push({ + entityIDs: [entityID], + properties: createPropertyUpdateObject(property.name, propertyMultiValue.values[i]), + }); + } + + // send a full updateMultiDiff post-dragging to count as an action in the undo stack + updateMultiDiffProperties(updateObjects); + } else { + // send an additional update post-dragging to consider whole property change from dragStart to dragEnd to be 1 action + this.valueChangeFunction(); + } }; } @@ -2004,7 +2131,7 @@ function createStringProperty(property, elProperty) { + ${propertyData.readOnly ? 'readonly' : ''}/> `); @@ -2013,7 +2140,12 @@ function createStringProperty(property, elProperty) { elInput.addEventListener('change', propertyData.onChange); } + + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + elProperty.appendChild(elInput); + elProperty.appendChild(elMultiDiff); if (propertyData.buttons !== undefined) { addButtons(elProperty, elementID, propertyData.buttons, false); @@ -2046,7 +2178,10 @@ function createBoolProperty(property, elProperty) { let subPropertyOf = propertyData.subPropertyOf; if (subPropertyOf !== undefined) { elInput.addEventListener('change', function() { - updateCheckedSubProperty(subPropertyOf, selectedEntityProperties[subPropertyOf], + let subPropertyMultiValue = getMultiplePropertyValue(subPropertyOf); + + updateCheckedSubProperty(subPropertyOf, + subPropertyMultiValue.value, elInput, propertyName, property.isParticleProperty); }); } else { @@ -2067,7 +2202,7 @@ function createNumberProperty(property, elProperty) { class='hide-spinner' type="number" ${propertyData.placeholder ? 'placeholder="' + propertyData.placeholder + '"' : ''} - ${propertyData.readOnly ? 'readonly' : ''}> + ${propertyData.readOnly ? 'readonly' : ''}/> `); if (propertyData.min !== undefined) { @@ -2085,7 +2220,11 @@ function createNumberProperty(property, elProperty) { elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(property)); + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + elProperty.appendChild(elInput); + elProperty.appendChild(elMultiDiff); if (propertyData.buttons !== undefined) { addButtons(elProperty, elementID, propertyData.buttons, false); @@ -2124,6 +2263,37 @@ function createNumberDraggableProperty(property, elProperty) { let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); elDraggableNumber.setValueChangeFunction(valueChangeFunction); + + elDraggableNumber.setMultiDiffDragFunction((changedDelta) => { + let propertyMultiValue = getMultiplePropertyValue(property.name); + if (!propertyMultiValue.isMultiDiffValue) { + console.log("setMultiDiffDragFunction is only supposed to be called in MultiDiff mode."); + return; + } + + let multiplier = property.data.multiplier !== undefined ? property.data.multiplier : 1; + + let applyDelta = changedDelta * multiplier; + console.log(applyDelta); + + if (selectedEntityIDs.size !== propertyMultiValue.values.length) { + console.log("selectedEntityIDs and propertyMultiValue got out of sync."); + return; + } + let updateObjects = {}; + const selectedEntityIDsArray = [...selectedEntityIDs]; + + for (var i = 0; i < selectedEntityIDsArray.length; ++i) { + let entityID = selectedEntityIDsArray[i]; + let updatedValue = propertyMultiValue.values[i] + applyDelta; + // FIXME: handle min/max per value? + updateObjects[entityID] = createPropertyUpdateObject(property.name, updatedValue); + // We need to store these so that we can send a full update on the dragEnd + mergeDeep(currentSelections[i].properties, updateObjects[entityID]); + } + + updateMultiDiffProperties(updateObjects, true); + }); elDraggableNumber.elInput.setAttribute("id", elementID); elProperty.appendChild(elDraggableNumber.elDiv); @@ -2341,7 +2511,7 @@ function createDropdownProperty(property, propertyID, elProperty) { } elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); - + elProperty.appendChild(elInput); return elInput; @@ -2360,8 +2530,12 @@ function createTextareaProperty(property, elProperty) { } elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); - + + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + elProperty.appendChild(elInput); + elProperty.appendChild(elMultiDiff); if (propertyData.buttons !== undefined) { addButtons(elProperty, elementID, propertyData.buttons, true); @@ -2373,7 +2547,7 @@ function createTextareaProperty(property, elProperty) { function createIconProperty(property, elProperty) { let elementID = property.elementID; let propertyData = property.data; - + elProperty.className = "value"; let elSpan = document.createElement('span'); @@ -2718,8 +2892,11 @@ function newJSONEditor() { showSaveUserDataButton(); } -function saveUserData() { - saveJSONUserData(true); +/** + * @param {Set.} [entityIDsToUpdate] Entity IDs to update userData for. + */ +function saveUserData(entityIDsToUpdate) { + saveJSONUserData(true, entityIDsToUpdate); } function setJSONError(property, isError) { @@ -2729,11 +2906,14 @@ function setJSONError(property, isError) { $propertyUserDataEditorStatus.text(isError ? 'Invalid JSON code - look for red X in your code' : ''); } -function setUserDataFromEditor(noUpdate) { - let json = null; +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] - Entity IDs to update userData for. + */ +function setUserDataFromEditor(noUpdate, entityIDsToUpdate) { let errorFound = false; try { - json = editor.get(); + editor.get(); } catch (e) { errorFound = true; } @@ -2748,7 +2928,7 @@ function setUserDataFromEditor(noUpdate) { if (noUpdate) { EventBridge.emitWebEvent( JSON.stringify({ - id: lastEntityID, + ids: [...entityIDsToUpdate], type: "saveUserData", properties: { userData: text @@ -2760,61 +2940,6 @@ function setUserDataFromEditor(noUpdate) { } } -function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, removeKeys) { - let propertyUpdate = {}; - let parsedData = {}; - let keysToBeRemoved = removeKeys ? removeKeys : []; - try { - if ($('#property-userData-editor').css('height') !== "0px") { - // if there is an expanded, we want to use its json. - parsedData = getEditorJSON(); - } else { - parsedData = JSON.parse(userDataElement.value); - } - } catch (e) { - // TODO: Should an alert go here? - } - - if (!(groupName in parsedData)) { - parsedData[groupName] = {}; - } - let keys = Object.keys(updateKeyPair); - keys.forEach(function (key) { - if (updateKeyPair[key] !== null && updateKeyPair[key] !== "null") { - if (updateKeyPair[key] instanceof Element) { - if (updateKeyPair[key].type === "checkbox") { - parsedData[groupName][key] = updateKeyPair[key].checked; - } else { - let val = isNaN(updateKeyPair[key].value) ? updateKeyPair[key].value : parseInt(updateKeyPair[key].value); - parsedData[groupName][key] = val; - } - } else { - parsedData[groupName][key] = updateKeyPair[key]; - } - } else if (defaults[key] !== null && defaults[key] !== "null") { - parsedData[groupName][key] = defaults[key]; - } - }); - keysToBeRemoved.forEach(function(key) { - if (parsedData[groupName].hasOwnProperty(key)) { - delete parsedData[groupName][key]; - } - }); - - if (Object.keys(parsedData[groupName]).length === 0) { - delete parsedData[groupName]; - } - if (Object.keys(parsedData).length > 0) { - propertyUpdate.userData = JSON.stringify(parsedData); - } else { - propertyUpdate.userData = ''; - } - - userDataElement.value = propertyUpdate.userData; - - updateProperties(propertyUpdate, false); -} - let editor = null; function createJSONEditor() { @@ -2837,8 +2962,6 @@ function createJSONEditor() { return; } $('#property-userData-button-save').attr('disabled', false); - - } }; editor = new JSONEditor(container, options); @@ -2896,10 +3019,6 @@ function setEditorJSON(json) { } } -function getEditorJSON() { - return editor.get(); -} - function deleteJSONEditor() { if (editor !== null) { setJSONError('userData', false); @@ -2910,8 +3029,12 @@ function deleteJSONEditor() { let savedJSONTimer = null; -function saveJSONUserData(noUpdate) { - setUserDataFromEditor(noUpdate); +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] Entity IDs to update userData for + */ +function saveJSONUserData(noUpdate, entityIDsToUpdate) { + setUserDataFromEditor(noUpdate, entityIDsToUpdate ? entityIDsToUpdate : selectedEntityIDs); $('#property-userData-saved').show(); $('#property-userData-button-save').attr('disabled', true); if (savedJSONTimer !== null) { @@ -2951,11 +3074,14 @@ function saveMaterialData() { saveJSONMaterialData(true); } -function setMaterialDataFromEditor(noUpdate) { - let json = null; +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] - Entity IDs to update materialData for. + */ +function setMaterialDataFromEditor(noUpdate, entityIDsToUpdate) { let errorFound = false; try { - json = materialEditor.get(); + materialEditor.get(); } catch (e) { errorFound = true; } @@ -2969,7 +3095,7 @@ function setMaterialDataFromEditor(noUpdate) { if (noUpdate) { EventBridge.emitWebEvent( JSON.stringify({ - id: lastEntityID, + ids: [...entityIDsToUpdate], type: "saveMaterialData", properties: { materialData: text @@ -2990,9 +3116,6 @@ function createJSONMaterialEditor() { mode: 'tree', modes: ['code', 'tree'], name: 'materialData', - onModeChange: function() { - $('.jsoneditor-poweredBy').remove(); - }, onError: function(e) { alert('JSON editor:' + e); }, @@ -3003,8 +3126,6 @@ function createJSONMaterialEditor() { return; } $('#property-materialData-button-save').attr('disabled', false); - - } }; materialEditor = new JSONEditor(container, options); @@ -3062,10 +3183,6 @@ function setMaterialEditorJSON(json) { } } -function getMaterialEditorJSON() { - return materialEditor.get(); -} - function deleteJSONMaterialEditor() { if (materialEditor !== null) { setJSONError('materialData', false); @@ -3076,8 +3193,12 @@ function deleteJSONMaterialEditor() { let savedMaterialJSONTimer = null; -function saveJSONMaterialData(noUpdate) { - setMaterialDataFromEditor(noUpdate); +/** + * @param {boolean} noUpdate - don't update the UI, but do send a property update. + * @param {Set.} [entityIDsToUpdate] - Entity IDs to update materialData for. + */ +function saveJSONMaterialData(noUpdate, entityIDsToUpdate) { + setMaterialDataFromEditor(noUpdate, entityIDsToUpdate ? entityIDsToUpdate : selectedEntityIDs); $('#property-materialData-saved').show(); $('#property-materialData-button-save').attr('disabled', true); if (savedMaterialJSONTimer !== null) { @@ -3100,13 +3221,12 @@ function bindAllNonJSONEditorElements() { if (e.target.id === "property-userData-button-edit" || e.target.id === "property-userData-button-clear" || e.target.id === "property-materialData-button-edit" || e.target.id === "property-materialData-button-clear") { return; - } else { - if ($('#property-userData-editor').css('height') !== "0px") { - saveUserData(); - } - if ($('#property-materialData-editor').css('height') !== "0px") { - saveMaterialData(); - } + } + if ($('#property-userData-editor').css('height') !== "0px") { + saveUserData(); + } + if ($('#property-materialData-editor').css('height') !== "0px") { + saveMaterialData(); } }); } @@ -3139,14 +3259,14 @@ function toggleDropdown(event) { } function closeAllDropdowns() { - elDropdowns = document.querySelectorAll("div.dropdown > dl"); + let elDropdowns = document.querySelectorAll("div.dropdown > dl"); for (let i = 0; i < elDropdowns.length; ++i) { elDropdowns[i].setAttribute('dropped', 'false'); } } function setDropdownValue(event) { - let dt = event.target.parentNode.parentNode.previousSibling; + let dt = event.target.parentNode.parentNode.previousSibling.previousSibling; dt.value = event.target.getAttribute("value"); dt.firstChild.textContent = event.target.textContent; @@ -3308,18 +3428,41 @@ function materialTargetPropertyUpdate(propertyValue) { } +function applyNumberPropertyModifiers(number, propertyData) { + const multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + let result = number / multiplier; + if (propertyData.round !== undefined) { + result = Math.round(result * propertyData.round) / propertyData.round; + } + if (propertyData.decimals !== undefined) { + return result.toFixed(propertyData.decimals) + } + return result; +} + +const isSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); + + function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { - if (selections.length === 0) { - if (lastEntityID !== null) { - if (editor !== null) { - saveUserData(); - deleteJSONEditor(); - } - if (materialEditor !== null) { - saveMaterialData(); - deleteJSONMaterialEditor(); - } + const previouslySelectedEntityIDs = selectedEntityIDs; + currentSelections = selections; + selectedEntityIDs = new Set(selections.map(selection => selection.id)); + const multipleSelections = currentSelections.length > 1; + const hasSelectedEntityChanged = !isSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs); + + // FIXME: do we really want to save userData/materialData here instead of saving it on the blur event of the json editor? + if (hasSelectedEntityChanged) { + if (editor !== null) { + saveUserData(previouslySelectedEntityIDs); } + if (materialEditor !== null) { + saveMaterialData(previouslySelectedEntityIDs); + } + } + + if (selections.length === 0) { + deleteJSONEditor(); + deleteJSONMaterialEditor(); resetProperties(); showGroupsForType("None"); @@ -3328,143 +3471,151 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { elIcon.innerText = NO_SELECTION; elIcon.style.display = 'inline-block'; - deleteJSONEditor(); getPropertyInputElement("userData").value = ""; showUserDataTextArea(); showSaveUserDataButton(); showNewJSONEditorButton(); - deleteJSONMaterialEditor(); getPropertyInputElement("materialData").value = ""; showMaterialDataTextArea(); showSaveMaterialDataButton(); showNewJSONMaterialEditorButton(); - disableProperties(); - } else if (selections.length > 1) { - deleteJSONEditor(); - deleteJSONMaterialEditor(); - - let ids = []; - let types = {}; - let numTypes = 0; - - for (let i = 0; i < selections.length; ++i) { - ids.push(selections[i].id); - let currentSelectedType = selections[i].properties.type; - if (types[currentSelectedType] === undefined) { - types[currentSelectedType] = 0; - numTypes += 1; - } - types[currentSelectedType]++; - } - - let type = "Multiple"; - if (numTypes === 1) { - type = selections[0].properties.type; - } - - resetProperties(); - showGroupsForType(type); - - let typeProperty = properties["type"]; - typeProperty.elSpan.innerHTML = typeProperty.data.icons[type]; - typeProperty.elSpan.style.display = "inline-block"; - disableProperties(); } else { - selectedEntityProperties = selections[0].properties; - - if (lastEntityID !== selectedEntityProperties.id && lastEntityID !== null) { - if (editor !== null) { - saveUserData(); - } - if (materialEditor !== null) { - saveMaterialData(); - } - } - - let hasSelectedEntityChanged = lastEntityID !== selectedEntityProperties.id; - if (!isPropertiesToolUpdate && !hasSelectedEntityChanged && document.hasFocus()) { // in case the selection has not changed and we still have focus on the properties page, // we will ignore the event. return; } + if (hasSelectedEntityChanged) { + let elServerScriptStatusOuter = document.getElementById('div-property-serverScriptStatus'); + elServerScriptStatusOuter.style.display = multipleSelections ? "none" : null; + if (!multipleSelections) { + resetServerScriptStatus(); + } + } - let doSelectElement = !hasSelectedEntityChanged; + const doSelectElement = !hasSelectedEntityChanged; - // the event bridge and json parsing handle our avatar id string differently. - lastEntityID = selectedEntityProperties.id; + // Get unique entity types, and convert the types Sphere and Box to Shape + const shapeTypes = ["Sphere", "Box"]; + const entityTypes = [...new Set(currentSelections.map(a => + shapeTypes.includes(a.properties.type) ? "Shape" : a.properties.type))]; - showGroupsForType(selectedEntityProperties.type); + showGroupsForTypes(entityTypes); - if (selectedEntityProperties.locked) { + const lockedMultiValue = getMultiplePropertyValue('locked'); + + if (lockedMultiValue.isMultiDiffValue || lockedMultiValue.value) { disableProperties(); - getPropertyInputElement("locked").removeAttribute('disabled'); + getPropertyInputElement('locked').removeAttribute('disabled'); } else { enableProperties(); disableSaveUserDataButton(); disableSaveMaterialDataButton() } - for (let propertyID in properties) { - let property = properties[propertyID]; - let propertyData = property.data; - let propertyName = property.name; - let propertyValue = getPropertyValue(propertyName); + const certificateIDMultiValue = getMultiplePropertyValue('certificateID'); + const hasCertifiedInSelection = certificateIDMultiValue.isMultiDiffValue || certificateIDMultiValue.value !== ""; - let isSubProperty = propertyData.subPropertyOf !== undefined; - if (propertyValue === undefined && !isSubProperty) { - continue; + Object.entries(properties).forEach(function([propertyID, property]) { + const propertyData = property.data; + const propertyName = property.name; + let propertyMultiValue = getMultiplePropertyValue(propertyName); + let isMultiDiffValue = propertyMultiValue.isMultiDiffValue; + let propertyValue = propertyMultiValue.value; + + const isSubProperty = propertyData.subPropertyOf !== undefined; + if (propertyValue === undefined && !isMultiDiffValue && !isSubProperty) { + return; } - if (propertyData.hideIfCertified) { - let shouldHide = selectedEntityProperties.certificateID !== ""; - if (shouldHide) { - propertyValue = "** Certified **"; - property.elInput.disabled = true; + if (propertyData.hideIfCertified && hasCertifiedInSelection) { + propertyValue = "** Certified **"; + property.elInput.disabled = true; + } + + if (propertyName === "type") { + propertyValue = entityTypes.length > 1 ? "Multiple" : propertyMultiValue.values[0]; + } + + if (!isMultiDiffValue) { + let isPropertyNotNumber = false; + switch (propertyData.type) { + case 'number': + case 'number-draggable': + isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; + break; + case 'rect': + case 'vec3': + case 'vec2': + isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; + break; + case 'color': + isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; + break; + } + if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { + propertyMultiValue = getMultiplePropertyValue(propertyData.fallbackProperty); + propertyValue = propertyMultiValue.value; + isMultiDiffValue = propertyMultiValue.value; } - } - - let isPropertyNotNumber = false; - switch (propertyData.type) { - case 'number': - case 'number-draggable': - isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; - break; - case 'rect': - case 'vec3': - case 'vec2': - isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; - break; - case 'color': - isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; - break; - } - if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { - propertyValue = getPropertyValue(propertyData.fallbackProperty); } switch (propertyData.type) { case 'string': { - property.elInput.value = propertyValue; - break; - } - case 'bool': { - let inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; - if (isSubProperty) { - let propertyValue = selectedEntityProperties[propertyData.subPropertyOf]; - let subProperties = propertyValue.split(","); - let subPropertyValue = subProperties.indexOf(propertyName) > -1; - property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; + if (isMultiDiffValue) { + if (propertyData.readOnly && propertyData.multiDisplayMode + && propertyData.multiDisplayMode === PROPERTY_MULTI_DISPLAY_MODE.COMMA_SEPARATED_VALUES) { + property.elInput.value = propertyMultiValue.values.join(", "); + } else { + property.elInput.classList.add('multi-diff'); + property.elInput.value = ""; + } } else { - property.elInput.checked = inverse ? !propertyValue : propertyValue; + property.elInput.classList.remove('multi-diff'); + property.elInput.value = propertyValue; } break; } + case 'bool': { + const inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; + if (isSubProperty) { + + let subPropertyMultiValue = getMultiplePropertyValue(propertyData.subPropertyOf); + let propertyValue = subPropertyMultiValue.value; + isMultiDiffValue = subPropertyMultiValue.isMultiDiffValue; + if (isMultiDiffValue) { + property.elInput.checked = false; + property.elInput.classList.add('multi-diff'); + } else { + let subProperties = propertyValue.split(","); + let subPropertyValue = subProperties.indexOf(propertyName) > -1; + property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; + property.elInput.classList.remove('multi-diff'); + } + + } else { + if (isMultiDiffValue) { + property.elInput.checked = false; + property.elInput.classList.add('multi-diff'); + } else { + property.elInput.checked = inverse ? !propertyValue : propertyValue; + property.elInput.classList.remove('multi-diff'); + } + } + + break; + } case 'number': { - property.elInput.value = propertyValue; + if (isMultiDiffValue) { + property.elInput.value = ""; + property.elInput.classList.add('multi-diff'); + } else { + property.elInput.value = propertyValue; + property.elInput.classList.remove('multi-diff'); + } break; } case 'number-draggable': { @@ -3473,7 +3624,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { if (propertyData.round !== undefined) { value = Math.round(value.round) / propertyData.round; } - property.elNumber.setValue(value); + property.elNumber.setValue(value, isMultiDiffValue); break; } case 'rect': @@ -3484,53 +3635,51 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { break; case 'vec3': case 'vec2': { - let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - let valueX = propertyValue.x / multiplier; - let valueY = propertyValue.y / multiplier; - let valueZ = propertyValue.z / multiplier; - if (propertyData.round !== undefined) { - valueX = Math.round(valueX * propertyData.round) / propertyData.round; - valueY = Math.round(valueY * propertyData.round) / propertyData.round; - valueZ = Math.round(valueZ * propertyData.round) / propertyData.round; - } - if (propertyData.decimals !== undefined) { - property.elNumberX.setValue(valueX.toFixed(propertyData.decimals)); - property.elNumberY.setValue(valueY.toFixed(propertyData.decimals)); + if (isMultiDiffValue) { + property.elNumberX.setValue(0, true); + property.elNumberY.setValue(0, true); if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(valueZ.toFixed(propertyData.decimals)); + property.elNumberZ.setValue(0, true); } } else { - property.elNumberX.setValue(valueX); - property.elNumberY.setValue(valueY); + property.elNumberX.setValue(applyNumberPropertyModifiers(propertyValue.x, propertyData), false); + property.elNumberY.setValue(applyNumberPropertyModifiers(propertyValue.y, propertyData), false); if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(valueZ); + property.elNumberZ.setValue(applyNumberPropertyModifiers(propertyValue.z, propertyData), false); } } break; } case 'color': { - property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," + - propertyValue.green + "," + - propertyValue.blue + ")"; + let displayColor = isMultiDiffValue ? propertyMultiValue.values[0] : propertyValue; + property.elColorPicker.style.backgroundColor = "rgb(" + displayColor.red + "," + + displayColor.green + "," + + displayColor.blue + ")"; if (hasSelectedEntityChanged && $(property.elColorPicker).attr('active') === 'true') { // Set the color picker inactive before setting the color, // otherwise an update will be sent directly after setting it here. $(property.elColorPicker).attr('active', 'false'); colorPickers['#' + property.elementID].colpickSetColor({ - "r": propertyValue.red, - "g": propertyValue.green, - "b": propertyValue.blue + "r": displayColor.red, + "g": displayColor.green, + "b": displayColor.blue }); $(property.elColorPicker).attr('active', 'true'); } - property.elNumberR.setValue(propertyValue.red); - property.elNumberG.setValue(propertyValue.green); - property.elNumberB.setValue(propertyValue.blue); + property.elNumberR.setValue(displayColor.red); + property.elNumberG.setValue(displayColor.green); + property.elNumberB.setValue(displayColor.blue); break; } case 'dropdown': { - property.elInput.value = propertyValue; + if (isMultiDiffValue) { + property.elInput.classList.add('multi-diff'); + property.elInput.value = ""; + } else { + property.elInput.classList.remove('multi-diff'); + property.elInput.value = propertyValue; + } setDropdownText(property.elInput); break; } @@ -3565,22 +3714,20 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { showPropertyElement(propertyToShow, show); } } - } + }); updateVisibleSpaceModeProperties(); + let userDataMultiValue = getMultiplePropertyValue("userData"); + let json = null; - try { - json = JSON.parse(selectedEntityProperties.userData); - } catch (e) { - // normal text - deleteJSONEditor(); - getPropertyInputElement("userData").value = selectedEntityProperties.userData; - showUserDataTextArea(); - showNewJSONEditorButton(); - hideSaveUserDataButton(); - hideUserDataSaved(); + if (!userDataMultiValue.isMultiDiffValue) { + try { + json = JSON.parse(userDataMultiValue.value); + } catch (e) { + + } } if (json !== null) { if (editor === null) { @@ -3591,19 +3738,34 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { hideUserDataTextArea(); hideNewJSONEditorButton(); hideUserDataSaved(); + } else { + // normal text + deleteJSONEditor(); + + if (userDataMultiValue.isMultiDiffValue) { + // FIXME: set multiValue property + getPropertyInputElement("userData").value = ""; + } else { + // FIXME: unset multiValue property + getPropertyInputElement("userData").value = userDataMultiValue.value; + } + + showUserDataTextArea(); + showNewJSONEditorButton(); + hideSaveUserDataButton(); + hideUserDataSaved(); } + let materialDataMultiValue = getMultiplePropertyValue("materialData"); + + let materialJson = null; - try { - materialJson = JSON.parse(selectedEntityProperties.materialData); - } catch (e) { - // normal text - deleteJSONMaterialEditor(); - getPropertyInputElement("materialData").value = selectedEntityProperties.materialData; - showMaterialDataTextArea(); - showNewJSONMaterialEditorButton(); - hideSaveMaterialDataButton(); - hideMaterialDataSaved(); + if (!materialDataMultiValue.isMultiDiffValue) { + try { + materialJson = JSON.parse(materialDataMultiValue.value); + } catch (e) { + + } } if (materialJson !== null) { if (materialEditor === null) { @@ -3614,6 +3776,20 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { hideMaterialDataTextArea(); hideNewJSONMaterialEditorButton(); hideMaterialDataSaved(); + } else { + // normal text + deleteJSONMaterialEditor(); + if (materialDataMultiValue.isMultiDiffValue) { + // FIXME: set multiValue property + getPropertyInputElement("materialData").value = ""; + } else { + // FIXME: unset multiValue property + getPropertyInputElement("materialData").value = materialDataMultiValue.value; + } + showMaterialDataTextArea(); + showNewJSONMaterialEditorButton(); + hideSaveMaterialDataButton(); + hideMaterialDataSaved(); } if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { @@ -3789,7 +3965,7 @@ function loaded() { if (window.EventBridge !== undefined) { EventBridge.scriptEventReceived.connect(function(data) { data = JSON.parse(data); - if (data.type === "server_script_status") { + if (data.type === "server_script_status" && selectedEntityIDs.size === 1) { let elServerScriptError = document.getElementById("property-serverScripts-error"); let elServerScriptStatus = document.getElementById("property-serverScripts-status"); elServerScriptError.value = data.errorInfo; @@ -3876,7 +4052,7 @@ function loaded() { // Server Script Error let elServerScripts = getPropertyInputElement("serverScripts"); - elDiv = document.createElement('div'); + let elDiv = document.createElement('div'); elDiv.className = "property"; let elServerScriptError = document.createElement('textarea'); let serverScriptErrorElementID = 'property-serverScripts-error'; @@ -3993,6 +4169,10 @@ function loaded() { dt.addEventListener("click", toggleDropdown, true); dl.appendChild(dt); + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + dl.appendChild(elMultiDiff); + let span = document.createElement("span"); span.setAttribute("value", options[selectedOption].value); span.textContent = options[selectedOption].firstChild.textContent; From 8b24cf51de49a07dc57c50a0f2c977d4f1585fc8 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 9 Apr 2019 21:05:40 +0200 Subject: [PATCH 25/85] enhance the multi editing experience --- scripts/system/html/css/edit-style.css | 20 +- scripts/system/html/js/draggableNumber.js | 20 +- scripts/system/html/js/entityProperties.js | 583 +++++++++++++-------- 3 files changed, 392 insertions(+), 231 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 6190469c98..f8247a24d5 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -263,15 +263,17 @@ input[type="text"] { } input.multi-diff:not(:focus) + span.multi-diff, -.draggable-number.multi-diff span.multi-diff, +textarea.multi-diff:not(:focus) + span.multi-diff, +.draggable-number.multi-diff>input:not(:focus)+span.multi-diff, dl>dt.multi-diff:not(:focus) + span.multi-diff { visibility: visible; position: absolute; display: inline-block; z-index: 2; + top: 7.5px; left: 20px; width: 50px; - height: 40%; + height: 13px; background-image: linear-gradient(transparent 0%, transparent 10%, #afafaf 10%, #afafaf 20%, transparent 20%, transparent 45%, #afafaf 45%, #afafaf 55%, transparent 55%, transparent 80%, #afafaf 80%, #afafaf 90%, transparent 90%, transparent 100%); background-repeat: no-repeat; pointer-events: none; @@ -290,10 +292,8 @@ input.multi-diff:not(:focus)::-webkit-input-placeholder, input.multi-diff:not(:f left: 10px; } -.text, .url { +.text, .url, .texture, .textarea { position: relative; - display: flex; - align-items: center; } input[type="search"] { @@ -484,6 +484,15 @@ input.multi-diff[type=checkbox] + label:hover { background-image: url(data:image/png;base64,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) } +.rgb.fstuple .color-picker.multi-diff:after { + width: 20px; + height: 20px; + content: ' '; + background: darkgray; + display: flex; + clip-path: polygon(0 0, 0 100%, 100% 100%); +} + .icon-input input { position: relative; padding-left: 36px; @@ -575,7 +584,6 @@ input.multi-diff[type=checkbox] + label:hover { div.section-header, hr { display: flex; flex-flow: row nowrap; - padding: 10px 16px; font-family: Raleway-Regular; font-size: 12px; diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 96280b82f6..b09723a173 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -72,7 +72,6 @@ DraggableNumber.prototype = { this.lastMouseEvent = event; } if (this.dragging && this.lastMouseEvent) { - let initialValue = this.elInput.value; let changeDelta = event.clientX - this.lastMouseEvent.clientX; if (changeDelta !== 0) { if (this.multiDiffModeEnabled) { @@ -136,14 +135,16 @@ DraggableNumber.prototype = { this.setMultiDiff(isMultiDiff); } - if (!isMultiDiff) { - if (newValue !== "" && this.decimals !== undefined) { - this.elInput.value = parseFloat(newValue).toFixed(this.decimals); - } else { - this.elInput.value = newValue; - } - this.elText.firstChild.data = this.elInput.value; + if (isNaN(newValue)) { + throw newValue + " is not a number"; } + + if (newValue !== "" && this.decimals !== undefined) { + this.elInput.value = parseFloat(newValue).toFixed(this.decimals); + } else { + this.elInput.value = newValue; + } + this.elText.firstChild.data = this.elInput.value; }, setMultiDiff: function(isMultiDiff) { @@ -181,6 +182,9 @@ DraggableNumber.prototype = { keyPress: function(event) { if (event.keyCode === ENTER_KEY) { + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } this.inputBlur(); } }, diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index df244d435a..1dba7bb861 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1645,6 +1645,33 @@ function mergeDeep(target, ...sources) { return mergeDeep(target, ...sources); } +function deepEqual(a, b) { + if (a === b) { + return true; + } + + if (typeof(a) !== "object" || typeof(b) !== "object") { + return false; + } + + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + + for (let property in a) { + if (!a.hasOwnProperty(property)) { + continue; + } + if (!b.hasOwnProperty(property)) { + return false; + } + if (!deepEqual(a[property], b[property])) { + return false; + } + } + return true; +} + function createElementFromHTML(htmlString) { let elTemplate = document.createElement('template'); elTemplate.innerHTML = htmlString.trim(); @@ -1759,41 +1786,43 @@ function resetProperties() { } case 'number-draggable': { if (propertyData.defaultValue !== undefined) { - property.elNumber.setValue(propertyData.defaultValue); + property.elNumber.setValue(propertyData.defaultValue, false); } else { - property.elNumber.setValue(""); + property.elNumber.setValue("", false); } break; } case 'rect': { - property.elNumberX.setValue(""); - property.elNumberY.setValue(""); - property.elNumberWidth.setValue(""); - property.elNumberHeight.setValue(""); + property.elNumberX.setValue("", false); + property.elNumberY.setValue("", false); + property.elNumberWidth.setValue("", false); + property.elNumberHeight.setValue("", false); break; } case 'vec3': case 'vec2': { - property.elNumberX.setValue(""); - property.elNumberY.setValue(""); + property.elNumberX.setValue("", false); + property.elNumberY.setValue("", false); if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(""); + property.elNumberZ.setValue("", false); } break; } case 'color': { property.elColorPicker.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - property.elNumberR.setValue(""); - property.elNumberG.setValue(""); - property.elNumberB.setValue(""); + property.elNumberR.setValue("", false); + property.elNumberG.setValue("", false); + property.elNumberB.setValue("", false); break; } case 'dropdown': { + property.elInput.classList.remove('multi-diff'); property.elInput.value = ""; setDropdownText(property.elInput); break; } case 'textarea': { + property.elInput.classList.remove('multi-diff'); property.elInput.value = ""; setTextareaScrolling(property.elInput); break; @@ -1803,6 +1832,7 @@ function resetProperties() { break; } case 'texture': { + property.elInput.classList.remove('multi-diff'); property.elInput.value = ""; property.elInput.imageLoad(property.elInput.value); break; @@ -1839,6 +1869,14 @@ function showGroupsForType(type) { showGroupsForTypes([type]); } +function getGroupsForTypes(types) { + return Object.keys(elGroups).filter((groupKey) => { + return types.map(type => GROUPS_PER_TYPE[type].includes(groupKey)).every(function (hasGroup) { + return hasGroup; + }); + }); +} + function showGroupsForTypes(types) { Object.entries(elGroups).forEach(([groupKey, elGroup]) => { if (types.map(type => GROUPS_PER_TYPE[type].includes(groupKey)).every(function (hasGroup) { return hasGroup; })) { @@ -1849,15 +1887,23 @@ function showGroupsForTypes(types) { }); } +const SUPPORTED_FALLBACK_TYPES = ['number', 'number-draggable', 'rect', 'vec3', 'vec2', 'color']; + function getMultiplePropertyValue(originalPropertyName) { // if this is a compound property name (i.e. animation.running) // then split it by . up to 3 times to find property value + + let propertyData = null; + if (properties[originalPropertyName] !== undefined) { + propertyData = properties[originalPropertyName].data; + } + let propertyValues = []; let splitPropertyName = originalPropertyName.split('.'); if (splitPropertyName.length > 1) { let propertyGroupName = splitPropertyName[PROPERTY_NAME_DIVISION.GROUP]; let propertyName = splitPropertyName[PROPERTY_NAME_DIVISION.PROPERTY]; - propertyValue = currentSelections.map(selection => { + propertyValues = currentSelections.map(selection => { let groupProperties = selection.properties[propertyGroupName]; if (groupProperties === undefined || groupProperties[propertyName] === undefined) { return undefined; @@ -1873,17 +1919,42 @@ function getMultiplePropertyValue(originalPropertyName) { propertyValues = currentSelections.map(selection => selection.properties[originalPropertyName]); } - let detailedValues = []; - propertyValues.forEach(function(propertyValue) { - if (typeof propertyValues === "object") { + if (propertyData !== null && propertyData.fallbackProperty !== undefined && + SUPPORTED_FALLBACK_TYPES.includes(propertyData.type)) { - } else { - detailedValues.push(propertyValue); + let fallbackMultiValue = null; + + for (let i = 0; i < propertyValues.length; ++i) { + let isPropertyNotNumber = false; + let propertyValue = propertyValues[i]; + if (propertyValue === undefined) { + continue; + } + switch (propertyData.type) { + case 'number': + case 'number-draggable': + isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; + break; + case 'rect': + case 'vec3': + case 'vec2': + isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; + break; + case 'color': + isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; + break; + } + if (isPropertyNotNumber) { + if (fallbackMultiValue === null) { + fallbackMultiValue = getMultiplePropertyValue(propertyData.fallbackProperty); + } + propertyValues[i] = fallbackMultiValue.values[i]; + } } - }); + } - const uniquePropertyValues = [...new Set(propertyValues)]; - const isMultiDiffValue = uniquePropertyValues.length > 1; + const firstValue = propertyValues[0]; + const isMultiDiffValue = !propertyValues.every((x) => deepEqual(firstValue, x)); if (isMultiDiffValue) { return { @@ -1894,12 +1965,73 @@ function getMultiplePropertyValue(originalPropertyName) { } return { - value: uniquePropertyValues[0], + value: propertyValues[0], values: propertyValues, isMultiDiffValue: false }; } + +function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { + let detailedValues = {}; + // Fixed numbers can't be easily averaged since they're strings, so lets keep an array of unmodified numbers + let unmodifiedValues = {}; + const DEFAULT_KEY = 0; + let uniqueKeys = new Set([]); + multiplePropertyValue.values.forEach(function(propertyValue) { + if (typeof propertyValue === "object") { + Object.entries(propertyValue).forEach(function([key, value]) { + if (!uniqueKeys.has(key)) { + uniqueKeys.add(key); + detailedValues[key] = []; + unmodifiedValues[key] = []; + } + detailedValues[key].push(applyInputNumberPropertyModifiers(value, propertyData)); + unmodifiedValues[key].push(value); + }); + } else { + if (!uniqueKeys.has(DEFAULT_KEY)) { + uniqueKeys.add(DEFAULT_KEY); + detailedValues[DEFAULT_KEY] = []; + unmodifiedValues[DEFAULT_KEY] = []; + } + detailedValues[DEFAULT_KEY].push(applyInputNumberPropertyModifiers(propertyValue, propertyData)); + unmodifiedValues[DEFAULT_KEY].push(propertyValue); + } + }); + let keys = [...uniqueKeys]; + + let subPropertyDiff = {}; + Object.entries(detailedValues).forEach(function([key, value]) { + subPropertyDiff[key] = [...new Set(value)].length > 1; + }); + + let averagePerSubProperty = {}; + Object.entries(unmodifiedValues).forEach(function([key, value]) { + let average = value.reduce((a, b) => a + b) / value.length; + averagePerSubProperty[key] = applyInputNumberPropertyModifiers(average, propertyData); + }); + + return { + keys, + subPropertyDiff, + averagePerSubProperty + }; +} + +function getDetailedSubPropertyMVPDiff(multiplePropertyValue, subPropertyName) { + let isChecked = false; + let checkedValues = multiplePropertyValue.values.map((value) => value.split(",").includes(subPropertyName)); + let isMultiDiff = !checkedValues.every(value => value === checkedValues[0]); + if (!isMultiDiff) { + isChecked = checkedValues[0]; + } + return { + isChecked, + isMultiDiff + } +} + function updateVisibleSpaceModeProperties() { for (let propertyID in properties) { if (properties.hasOwnProperty(propertyID)) { @@ -1985,11 +2117,6 @@ function updateMultiDiffProperties(propertiesMapToUpdate, onlyUpdateEntity) { propertiesMap: propertiesMapToUpdate, onlyUpdateEntities: onlyUpdateEntity })); - console.log(JSON.stringify({ - type: "update", - propertiesMap: propertiesMapToUpdate, - onlyUpdateEntities: onlyUpdateEntity - })) } function createEmitTextPropertyUpdateFunction(property) { @@ -2020,7 +2147,7 @@ function createDragEndFunction(property) { let updateObjects = []; const selectedEntityIDsArray = [...selectedEntityIDs]; - for (var i = 0; i < selectedEntityIDsArray.length; ++i) { + for (let i = 0; i < selectedEntityIDsArray.length; ++i) { let entityID = selectedEntityIDsArray[i]; updateObjects.push({ entityIDs: [entityID], @@ -2039,53 +2166,46 @@ function createDragEndFunction(property) { function createEmitNumberPropertyUpdateFunction(property) { return function() { - let multiplier = property.data.multiplier; - if (multiplier === undefined) { - multiplier = 1; - } - let value = parseFloat(this.value) * multiplier; + let value = parseFloat(applyOutputNumberPropertyModifiers(parseFloat(this.value), property.data)); updateProperty(property.name, value, property.isParticleProperty); }; } -function createEmitVec2PropertyUpdateFunction(property) { - return function () { - let multiplier = property.data.multiplier; - if (multiplier === undefined) { - multiplier = 1; - } - let newValue = { - x: property.elNumberX.elInput.value * multiplier, - y: property.elNumberY.elInput.value * multiplier - }; - updateProperty(property.name, newValue, property.isParticleProperty); - }; -} - -function createEmitVec3PropertyUpdateFunction(property) { +function createEmitNumberSubPropertyUpdateFunction(property, subProperty) { return function() { - let multiplier = property.data.multiplier; - if (multiplier === undefined) { - multiplier = 1; - } - let newValue = { - x: property.elNumberX.elInput.value * multiplier, - y: property.elNumberY.elInput.value * multiplier, - z: property.elNumberZ.elInput.value * multiplier - }; - updateProperty(property.name, newValue, property.isParticleProperty); - }; -} + let propertyMultiValue = getMultiplePropertyValue(property.name); + let value = parseFloat(applyOutputNumberPropertyModifiers(parseFloat(this.value), property.data)); -function createEmitRectPropertyUpdateFunction(property) { - return function() { - let newValue = { - x: property.elNumberX.elInput.value, - y: property.elNumberY.elInput.value, - width: property.elNumberWidth.elInput.value, - height: property.elNumberHeight.elInput.value, - }; - updateProperty(property.name, newValue, property.isParticleProperty); + if (propertyMultiValue.isMultiDiffValue) { + let updateObjects = []; + const selectedEntityIDsArray = [...selectedEntityIDs]; + + for (let i = 0; i < selectedEntityIDsArray.length; ++i) { + let entityID = selectedEntityIDsArray[i]; + + let propertyObject = propertyMultiValue.values[i]; + propertyObject[subProperty] = value; + + let updateObject = createPropertyUpdateObject(property.name, propertyObject); + updateObjects.push({ + entityIDs: [entityID], + properties: updateObject, + }); + + mergeDeep(currentSelections[i].properties, updateObject); + } + + // only update the entity property value itself if in the middle of dragging + // prevent undo command push, saving new property values, and property update + // callback until drag is complete (additional update sent via dragEnd callback) + let onlyUpdateEntity = properties[property.name] && properties[property.name].dragging === true; + updateMultiDiffProperties(updateObjects, onlyUpdateEntity); + console.log('updateMultiDiffProperties'); + } else { + let propertyValue = propertyMultiValue.value; + propertyValue[subProperty] = value; + updateProperty(property.name, propertyValue, property.isParticleProperty); + } }; } @@ -2105,16 +2225,34 @@ function emitColorPropertyUpdate(propertyName, red, green, blue, isParticlePrope updateProperty(propertyName, newValue, isParticleProperty); } -function updateCheckedSubProperty(propertyName, propertyValue, subPropertyElement, subPropertyString, isParticleProperty) { - if (subPropertyElement.checked) { - if (propertyValue.indexOf(subPropertyString)) { - propertyValue += subPropertyString + ','; - } - } else { - // We've unchecked, so remove - propertyValue = propertyValue.replace(subPropertyString + ",", ""); +function toggleBooleanCSV(inputCSV, property, enable) { + let values = inputCSV.split(","); + if (enable && !values.includes(property)) { + values.push(property); + } else if (!enable && values.includes(property)) { + values = values.filter(value => value !== property); + } + return values.join(","); +} + +function updateCheckedSubProperty(propertyName, propertyMultiValue, subPropertyElement, subPropertyString, isParticleProperty) { + if (propertyMultiValue.isMultiDiffValue) { + let updateObjects = []; + const selectedEntityIDsArray = [...selectedEntityIDs]; + + for (let i = 0; i < selectedEntityIDsArray.length; ++i) { + let newValue = toggleBooleanCSV(propertyMultiValue.values[i], subPropertyString, subPropertyElement.checked); + updateObjects.push({ + entityIDs: [selectedEntityIDsArray[i]], + properties: createPropertyUpdateObject(propertyName, newValue), + }); + } + + updateMultiDiffProperties(updateObjects); + } else { + updateProperty(propertyName, toggleBooleanCSV(propertyMultiValue.value, subPropertyString, subPropertyElement.checked), + isParticleProperty); } - updateProperty(propertyName, propertyValue, isParticleProperty); } /** @@ -2181,7 +2319,7 @@ function createBoolProperty(property, elProperty) { let subPropertyMultiValue = getMultiplePropertyValue(subPropertyOf); updateCheckedSubProperty(subPropertyOf, - subPropertyMultiValue.value, + subPropertyMultiValue, elInput, propertyName, property.isParticleProperty); }); } else { @@ -2245,26 +2383,14 @@ function updateNumberMinMax(property) { } } -function createNumberDraggableProperty(property, elProperty) { - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className += " draggable-number-container"; - - let dragStartFunction = createDragStartFunction(property); - let dragEndFunction = createDragEndFunction(property); - let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, - propertyData.decimals, dragStartFunction, dragEndFunction); - - let defaultValue = propertyData.defaultValue; - if (defaultValue !== undefined) { - elDraggableNumber.elInput.value = defaultValue; - } - - let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); - elDraggableNumber.setValueChangeFunction(valueChangeFunction); - - elDraggableNumber.setMultiDiffDragFunction((changedDelta) => { +/** + * + * @param {object} property - property update on drag + * @param {string} [subProperty] - subProperty to update on drag (e.g. enter 'x' to just update position.x) + * @returns {Function} + */ +function createMultiDiffDragFunction(property, subProperty) { + return function(changedDelta) { let propertyMultiValue = getMultiplePropertyValue(property.name); if (!propertyMultiValue.isMultiDiffValue) { console.log("setMultiDiffDragFunction is only supposed to be called in MultiDiff mode."); @@ -2274,26 +2400,58 @@ function createNumberDraggableProperty(property, elProperty) { let multiplier = property.data.multiplier !== undefined ? property.data.multiplier : 1; let applyDelta = changedDelta * multiplier; - console.log(applyDelta); if (selectedEntityIDs.size !== propertyMultiValue.values.length) { console.log("selectedEntityIDs and propertyMultiValue got out of sync."); return; } - let updateObjects = {}; + let updateObjects = []; const selectedEntityIDsArray = [...selectedEntityIDs]; - for (var i = 0; i < selectedEntityIDsArray.length; ++i) { + for (let i = 0; i < selectedEntityIDsArray.length; ++i) { let entityID = selectedEntityIDsArray[i]; - let updatedValue = propertyMultiValue.values[i] + applyDelta; - // FIXME: handle min/max per value? - updateObjects[entityID] = createPropertyUpdateObject(property.name, updatedValue); + + let updatedValue; + if (subProperty !== undefined) { + let objectToUpdate = propertyMultiValue.values[i]; + objectToUpdate[subProperty] += applyDelta; + updatedValue = objectToUpdate; + } else { + updatedValue = propertyMultiValue.values[i] + applyDelta; + } + let propertiesUpdate = createPropertyUpdateObject(property.name, updatedValue); + updateObjects.push({ + entityIDs: [entityID], + properties: propertiesUpdate + }); // We need to store these so that we can send a full update on the dragEnd - mergeDeep(currentSelections[i].properties, updateObjects[entityID]); + mergeDeep(currentSelections[i].properties, propertiesUpdate); } updateMultiDiffProperties(updateObjects, true); - }); + } +} + +function createNumberDraggableProperty(property, elProperty) { + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className += " draggable-number-container"; + + let dragStartFunction = createDragStartFunction(property); + let dragEndFunction = createDragEndFunction(property); + let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, + propertyData.decimals, dragStartFunction, dragEndFunction); + + let defaultValue = propertyData.defaultValue; + if (defaultValue !== undefined) { + elDraggableNumber.elInput.value = defaultValue; + } + + let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); + elDraggableNumber.setValueChangeFunction(valueChangeFunction); + + elDraggableNumber.setMultiDiffDragFunction(createMultiDiffDragFunction(property)); elDraggableNumber.elInput.setAttribute("id", elementID); elProperty.appendChild(elDraggableNumber.elDiv); @@ -2334,11 +2492,15 @@ function createRectProperty(property, elProperty) { elWidthHeightRow.appendChild(elNumberWidth.elDiv); elWidthHeightRow.appendChild(elNumberHeight.elDiv); - let valueChangeFunction = createEmitRectPropertyUpdateFunction(property); - elNumberX.setValueChangeFunction(valueChangeFunction); - elNumberY.setValueChangeFunction(valueChangeFunction); - elNumberWidth.setValueChangeFunction(valueChangeFunction); - elNumberHeight.setValueChangeFunction(valueChangeFunction); + elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); + elNumberWidth.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'width')); + elNumberHeight.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'height')); + + elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); + elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); + elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'width')); + elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'height')); let elResult = []; elResult[RECT_ELEMENTS.X_NUMBER] = elNumberX; @@ -2368,11 +2530,14 @@ function createVec3Property(property, elProperty) { elProperty.appendChild(elNumberX.elDiv); elProperty.appendChild(elNumberY.elDiv); elProperty.appendChild(elNumberZ.elDiv); - - let valueChangeFunction = createEmitVec3PropertyUpdateFunction(property); - elNumberX.setValueChangeFunction(valueChangeFunction); - elNumberY.setValueChangeFunction(valueChangeFunction); - elNumberZ.setValueChangeFunction(valueChangeFunction); + + elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); + elNumberZ.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'z')); + + elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); + elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); + elNumberZ.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'z')); let elResult = []; elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; @@ -2395,10 +2560,12 @@ function createVec2Property(property, elProperty) { let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); elProperty.appendChild(elNumberX.elDiv); elProperty.appendChild(elNumberY.elDiv); - - let valueChangeFunction = createEmitVec2PropertyUpdateFunction(property); - elNumberX.setValueChangeFunction(valueChangeFunction); - elNumberY.setValueChangeFunction(valueChangeFunction); + + elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); + + elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); + elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); let elResult = []; elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; @@ -2462,7 +2629,6 @@ function createColorProperty(property, elProperty) { color: '000000', submit: false, // We don't want to have a submission button onShow: function(colpick) { - console.log("Showing"); // The original color preview within the picker needs to be updated on show because // prior to the picker being shown we don't have access to the selections' starting color. colorPickers[colorPickerID].colpickSetColor({ @@ -2546,7 +2712,6 @@ function createTextareaProperty(property, elProperty) { function createIconProperty(property, elProperty) { let elementID = property.elementID; - let propertyData = property.data; elProperty.className = "value"; @@ -2574,6 +2739,7 @@ function createTextureProperty(property, elProperty) { elInput.setAttribute("type", "text"); let imageLoad = function(url) { + elDiv.style.display = null; if (url.slice(0, 5).toLowerCase() === "atp:/") { elImage.src = ""; elImage.style.display = "none"; @@ -2595,12 +2761,18 @@ function createTextureProperty(property, elProperty) { } }; elInput.imageLoad = imageLoad; + elInput.setMultipleValues = function() { + elDiv.style.display = "none"; + }; elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); elInput.addEventListener('change', function(ev) { imageLoad(ev.target.value); }); elProperty.appendChild(elInput); + let elMultiDiff = document.createElement('span'); + elMultiDiff.className = "multi-diff"; + elProperty.appendChild(elMultiDiff); elProperty.appendChild(elDiv); let elResult = []; @@ -2630,7 +2802,7 @@ function createDynamicMultiselectProperty(property, elProperty) { let elDivOptions = document.createElement('div'); elDivOptions.setAttribute("id", elementID + "-options"); - elDivOptions.style = "overflow-y:scroll;max-height:160px;" + elDivOptions.style = "overflow-y:scroll;max-height:160px;"; let elDivButtons = document.createElement('div'); elDivButtons.setAttribute("id", elDivOptions.getAttribute("id") + "-buttons"); @@ -2883,6 +3055,7 @@ function clearUserData() { } function newJSONEditor() { + getPropertyInputElement("userData").classList.remove('multi-diff'); deleteJSONEditor(); createJSONEditor(); let data = {}; @@ -2949,9 +3122,6 @@ function createJSONEditor() { mode: 'tree', modes: ['code', 'tree'], name: 'userData', - onModeChange: function() { - $('.jsoneditor-poweredBy').remove(); - }, onError: function(e) { alert('JSON editor:' + e); }, @@ -3061,6 +3231,7 @@ function clearMaterialData() { } function newJSONMaterialEditor() { + getPropertyInputElement("materialData").classList.remove('multi-diff'); deleteJSONMaterialEditor(); createJSONMaterialEditor(); let data = {}; @@ -3387,7 +3558,7 @@ function sendMaterialTargetProperty() { if (elInput.checked) { let targetID = elInput.getAttribute("targetID"); if (elInput.getAttribute("isMaterialName") === "true") { - materialTargetList.push("mat::" + targetID); + materialTargetList.push(MATERIAL_PREFIX_STRING + targetID); } else { materialTargetList.push(targetID); } @@ -3419,7 +3590,7 @@ function materialTargetPropertyUpdate(propertyValue) { let targetID = elInput.getAttribute("targetID"); let materialTargetName = targetID; if (elInput.getAttribute("isMaterialName") === "true") { - materialTargetName = "mat::" + targetID; + materialTargetName = MATERIAL_PREFIX_STRING + targetID; } elInput.checked = materialTargets.indexOf(materialTargetName) >= 0; } @@ -3427,10 +3598,8 @@ function materialTargetPropertyUpdate(propertyValue) { elDivOptions.propertyValue = propertyValue; } - -function applyNumberPropertyModifiers(number, propertyData) { - const multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - let result = number / multiplier; +function roundAndFixNumber(number, propertyData) { + let result = number; if (propertyData.round !== undefined) { result = Math.round(result * propertyData.round) / propertyData.round; } @@ -3440,6 +3609,16 @@ function applyNumberPropertyModifiers(number, propertyData) { return result; } +function applyInputNumberPropertyModifiers(number, propertyData) { + const multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + return roundAndFixNumber(number / multiplier, propertyData); +} + +function applyOutputNumberPropertyModifiers(number, propertyData) { + const multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + return roundAndFixNumber(number * multiplier, propertyData); +} + const isSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); @@ -3503,6 +3682,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { const entityTypes = [...new Set(currentSelections.map(a => shapeTypes.includes(a.properties.type) ? "Shape" : a.properties.type))]; + const shownGroups = getGroupsForTypes(entityTypes); showGroupsForTypes(entityTypes); const lockedMultiValue = getMultiplePropertyValue('locked'); @@ -3531,6 +3711,15 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { return; } + if (!shownGroups.includes(property.group_id)) { + const WANT_DEBUG_SHOW_HIDDEN_FROM_GROUPS = false; + if (WANT_DEBUG_SHOW_HIDDEN_FROM_GROUPS) { + console.log("Skipping property " + property.data.label + " [" + property.name + + "] from hidden group " + property.group_id); + } + return; + } + if (propertyData.hideIfCertified && hasCertifiedInSelection) { propertyValue = "** Certified **"; property.elInput.disabled = true; @@ -3540,29 +3729,6 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { propertyValue = entityTypes.length > 1 ? "Multiple" : propertyMultiValue.values[0]; } - if (!isMultiDiffValue) { - let isPropertyNotNumber = false; - switch (propertyData.type) { - case 'number': - case 'number-draggable': - isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; - break; - case 'rect': - case 'vec3': - case 'vec2': - isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; - break; - case 'color': - isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; - break; - } - if (isPropertyNotNumber && propertyData.fallbackProperty !== undefined) { - propertyMultiValue = getMultiplePropertyValue(propertyData.fallbackProperty); - propertyValue = propertyMultiValue.value; - isMultiDiffValue = propertyMultiValue.value; - } - } - switch (propertyData.type) { case 'string': { if (isMultiDiffValue) { @@ -3582,13 +3748,13 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { case 'bool': { const inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; if (isSubProperty) { - let subPropertyMultiValue = getMultiplePropertyValue(propertyData.subPropertyOf); let propertyValue = subPropertyMultiValue.value; isMultiDiffValue = subPropertyMultiValue.isMultiDiffValue; if (isMultiDiffValue) { - property.elInput.checked = false; - property.elInput.classList.add('multi-diff'); + let detailedSubProperty = getDetailedSubPropertyMVPDiff(subPropertyMultiValue, propertyName); + property.elInput.checked = detailedSubProperty.isChecked; + property.elInput.classList.toggle('multi-diff', detailedSubProperty.isMultiDiff); } else { let subProperties = propertyValue.split(","); let subPropertyValue = subProperties.indexOf(propertyName) > -1; @@ -3599,11 +3765,10 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } else { if (isMultiDiffValue) { property.elInput.checked = false; - property.elInput.classList.add('multi-diff'); } else { property.elInput.checked = inverse ? !propertyValue : propertyValue; - property.elInput.classList.remove('multi-diff'); } + property.elInput.classList.toggle('multi-diff', isMultiDiffValue); } break; @@ -3619,42 +3784,35 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { break; } case 'number-draggable': { - let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - let value = propertyValue / multiplier; - if (propertyData.round !== undefined) { - value = Math.round(value.round) / propertyData.round; - } - property.elNumber.setValue(value, isMultiDiffValue); + let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); + property.elNumber.setValue(detailedNumberDiff.averagePerSubProperty[0], detailedNumberDiff.subPropertyDiff[0]); break; } - case 'rect': - property.elNumberX.setValue(propertyValue.x); - property.elNumberY.setValue(propertyValue.y); - property.elNumberWidth.setValue(propertyValue.width); - property.elNumberHeight.setValue(propertyValue.height); + case 'rect': { + let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); + property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.subPropertyDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.subPropertyDiff.y); + property.elNumberWidth.setValue(detailedNumberDiff.averagePerSubProperty.width, detailedNumberDiff.subPropertyDiff.width); + property.elNumberHeight.setValue(detailedNumberDiff.averagePerSubProperty.height, detailedNumberDiff.subPropertyDiff.height); break; + } case 'vec3': case 'vec2': { - if (isMultiDiffValue) { - property.elNumberX.setValue(0, true); - property.elNumberY.setValue(0, true); - if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(0, true); - } - } else { - property.elNumberX.setValue(applyNumberPropertyModifiers(propertyValue.x, propertyData), false); - property.elNumberY.setValue(applyNumberPropertyModifiers(propertyValue.y, propertyData), false); - if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(applyNumberPropertyModifiers(propertyValue.z, propertyData), false); - } + let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); + property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.subPropertyDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.subPropertyDiff.y); + if (property.elNumberZ !== undefined) { + property.elNumberZ.setValue(detailedNumberDiff.averagePerSubProperty.z, detailedNumberDiff.subPropertyDiff.z); } break; } case 'color': { - let displayColor = isMultiDiffValue ? propertyMultiValue.values[0] : propertyValue; + let displayColor = propertyMultiValue.isMultiDiffValue ? propertyMultiValue.values[0] : propertyValue; property.elColorPicker.style.backgroundColor = "rgb(" + displayColor.red + "," + displayColor.green + "," + displayColor.blue + ")"; + property.elColorPicker.classList.toggle('multi-diff', propertyMultiValue.isMultiDiffValue); + if (hasSelectedEntityChanged && $(property.elColorPicker).attr('active') === 'true') { // Set the color picker inactive before setting the color, // otherwise an update will be sent directly after setting it here. @@ -3673,13 +3831,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { break; } case 'dropdown': { - if (isMultiDiffValue) { - property.elInput.classList.add('multi-diff'); - property.elInput.value = ""; - } else { - property.elInput.classList.remove('multi-diff'); - property.elInput.value = propertyValue; - } + property.elInput.classList.toggle('multi-diff', isMultiDiffValue); + property.elInput.value = isMultiDiffValue ? "" : propertyValue; setDropdownText(property.elInput); break; } @@ -3694,12 +3847,17 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { break; } case 'texture': { - property.elInput.value = propertyValue; - property.elInput.imageLoad(property.elInput.value); + property.elInput.value = isMultiDiffValue ? "" : propertyValue; + property.elInput.classList.toggle('multi-diff'); + if (isMultiDiffValue) { + property.elInput.setMultipleValues(); + } else { + property.elInput.imageLoad(property.elInput.value); + } break; } case 'dynamic-multiselect': { - if (property.data.propertyUpdate) { + if (!isMultiDiffValue && property.data.propertyUpdate) { property.data.propertyUpdate(propertyValue); } break; @@ -3718,9 +3876,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { updateVisibleSpaceModeProperties(); - let userDataMultiValue = getMultiplePropertyValue("userData"); - + let userDataTextArea = getPropertyInputElement("userData"); let json = null; if (!userDataMultiValue.isMultiDiffValue) { try { @@ -3733,6 +3890,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { if (editor === null) { createJSONEditor(); } + userDataTextArea.classList.remove('multi-diff'); setEditorJSON(json); showSaveUserDataButton(); hideUserDataTextArea(); @@ -3741,14 +3899,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } else { // normal text deleteJSONEditor(); - - if (userDataMultiValue.isMultiDiffValue) { - // FIXME: set multiValue property - getPropertyInputElement("userData").value = ""; - } else { - // FIXME: unset multiValue property - getPropertyInputElement("userData").value = userDataMultiValue.value; - } + userDataTextArea.classList.toggle('multi-diff', userDataMultiValue.isMultiDiffValue); + userDataTextArea.value = userDataMultiValue.isMultiDiffValue ? "" : userDataMultiValue.value; showUserDataTextArea(); showNewJSONEditorButton(); @@ -3757,8 +3909,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } let materialDataMultiValue = getMultiplePropertyValue("materialData"); - - + let materialDataTextArea = getPropertyInputElement("materialData"); let materialJson = null; if (!materialDataMultiValue.isMultiDiffValue) { try { @@ -3771,6 +3922,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { if (materialEditor === null) { createJSONMaterialEditor(); } + materialDataTextArea.classList.remove('multi-diff'); setMaterialEditorJSON(materialJson); showSaveMaterialDataButton(); hideMaterialDataTextArea(); @@ -3779,20 +3931,15 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } else { // normal text deleteJSONMaterialEditor(); - if (materialDataMultiValue.isMultiDiffValue) { - // FIXME: set multiValue property - getPropertyInputElement("materialData").value = ""; - } else { - // FIXME: unset multiValue property - getPropertyInputElement("materialData").value = materialDataMultiValue.value; - } + materialDataTextArea.classList.toggle('multi-diff', materialDataMultiValue.isMultiDiffValue); + materialDataTextArea.value = materialDataMultiValue.isMultiDiffValue ? "" : materialDataMultiValue.value; showMaterialDataTextArea(); showNewJSONMaterialEditorButton(); hideSaveMaterialDataButton(); hideMaterialDataSaved(); } - if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { + if (hasSelectedEntityChanged && selections.length === 1 && entityTypes[0] === "Material") { requestMaterialTarget(); } @@ -3912,6 +4059,7 @@ function loaded() { property.isParticleProperty = group.id.includes("particles"); property.elContainer = elContainer; property.spaceMode = propertySpaceMode; + property.group_id = group.id; let elLabel = createElementFromHTML(`
${innerPropertyData.label}
`); createAppTooltip.registerTooltipElement(elLabel, propertyID, propertyName); @@ -3930,6 +4078,7 @@ function loaded() { property.isParticleProperty = group.id.includes("particles"); property.elContainer = elContainer; property.spaceMode = propertySpaceMode; + property.group_id = group.id; if (property.type !== 'placeholder') { properties[propertyID] = property; @@ -4000,13 +4149,13 @@ function loaded() { if (propertyRange !== undefined) { let propertyData = properties[property].data; let multiplier = propertyData.multiplier; - if (propertyData.min === undefined && propertyRange.minimum != "") { + if (propertyData.min === undefined && propertyRange.minimum !== "") { propertyData.min = propertyRange.minimum; if (multiplier !== undefined) { propertyData.min /= multiplier; } } - if (propertyData.max === undefined && propertyRange.maximum != "") { + if (propertyData.max === undefined && propertyRange.maximum !== "") { propertyData.max = propertyRange.maximum; if (multiplier !== undefined) { propertyData.max /= multiplier; From c283c062c1e8f6bf05c62d642713558e6a90d715 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 12 Apr 2019 19:23:15 +0200 Subject: [PATCH 26/85] CR changes --- scripts/system/html/css/edit-style.css | 8 +- scripts/system/html/js/draggableNumber.js | 18 ++- scripts/system/html/js/entityProperties.js | 123 ++++++++++++--------- 3 files changed, 84 insertions(+), 65 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index f8247a24d5..470e57ad6d 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -272,7 +272,9 @@ dl>dt.multi-diff:not(:focus) + span.multi-diff { z-index: 2; top: 7.5px; left: 20px; - width: 50px; + max-width: 50px; + min-width: 10px; + width: 50%; height: 13px; background-image: linear-gradient(transparent 0%, transparent 10%, #afafaf 10%, #afafaf 20%, transparent 20%, transparent 45%, #afafaf 45%, #afafaf 55%, transparent 55%, transparent 80%, #afafaf 80%, #afafaf 90%, transparent 90%, transparent 100%); background-repeat: no-repeat; @@ -478,10 +480,10 @@ input[type=checkbox]:checked + label:hover { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEySURBVDhPnZLPSsNAEMa/XVPBCE0RhNy0OarP4Av4AD6JB0GwVBA8efBBxHsgh4CQswcRoUIpiIpVAm3zZ5M4szFSbQPBH3xkJvNNZskOer2eLIriKM/ze1JOcS1UHmdZduF5ngEKjr/fN4Z6+oKerwA2gxC4HAFPEWVLsAzgZAvYt3Q6Enw6jg7uBAaTFMNwhpnKdbXCkAJdy8ROu4XrXW2HTJIErHcFDD6nC02Mom8PwymeE2gvS0ZRBBaTlsOXEmdlrfLLOI7Bakrl/zWxCT8T/904f9QW/b06qtrCUdtFCqdjYs2Q2jAPX8c2XQd7Kr/wfV8vwIPs4Ga1ixe5Xrr/YFLTYfKIvWzM6ZtwXZdX7lxXG0L+sxXHcW5t254opRzawQ0S72+dPmjTroIgOP0CQSMt5LDn1T8AAAAASUVORK5CYII=); } input.multi-diff[type=checkbox] + label { - background-image: url(data:image/png;base64,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) + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFbSURBVDhPY2xoaGD68+dPMSMjY9L////VgTQjAw4AlH8PxLOPHj1azWxjY1MBVNsBFBfBpwkEgNKcQGwtJyfHyALkF4IE34gqM9zU9WT4wicG4mIA1l/fGIyOL2EQeP8EZEAiC5AQBUlcMQ5ieMXIwfDo9SeG73/+gRXDAAsTI4Pd9wdgTVAgw/Tv3z8GEP7Jwctw78M3DE0goPr6BoPludVgdTAM1wgCv//9B9PIQOPNDYaAGxtRNIEw03+gYhDGBtSBNgVc3wiWR8dM//4DTQBidKD++jqD//X1YDlsGMWpMKD26jqD79V1GM5DxihOZQWGntqrawy+V9ZiOA0dw21k/f6JwerzHQbvS2swTMeGGfPz8l8BLRP9KizDwP0WHk+EwGum/3//94M8y/nmEdZAwIb//vs7g/nk6ZPHzE3NvwITrxLDfwYhIAamZpz4PcM/hslXrl6pBwAmfz5iaAlAuAAAAABJRU5ErkJggg==); } input.multi-diff[type=checkbox] + label:hover { - background-image: url(data:image/png;base64,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) + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFPSURBVDhPjZJBSwJBHMXfrG6rWEkl1MFDGOihDn2JIIrqc3QJunbyFhUkRieD+hYepWteuxctXiJ1Q5xmdmZ3bWZTUHezfvAu/3lv3n+HRblcTrbb7fN+v/8eBMFgFpxz13Gcu3q9bqHb7V4M5/9GhatE3cIsy0o99YBKC3jliCWbBK43gK0MoDI9otfTB/vPBC9Uwu4xMC8IzSOSBsFxIYNqMTGcAIYQAlodD3j5/IqENIc5gqt1P/SNZKhaXR0a5E/5BEcrwH1xEHrGZbiuC604DpZ81AoiPJ/WROM4e4sSt3kaaRopNrg7z1FZdSLmcU2saqrX20lTXC5/RFabFmk2m+GLnBnbWJMOThJv4SV/QRqNBjNNM9UiGeQHdDiejZSSG5TSG71zjnVivyVOKlNLlEqlx+xCds7zvU31G6Z938dvEq4QjLMH27ZPvwHFVYQr3h7uHwAAAABJRU5ErkJggg==); } .rgb.fstuple .color-picker.multi-diff:after { diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index b09723a173..d76874d4d9 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -72,21 +72,17 @@ DraggableNumber.prototype = { this.lastMouseEvent = event; } if (this.dragging && this.lastMouseEvent) { - let changeDelta = event.clientX - this.lastMouseEvent.clientX; - if (changeDelta !== 0) { + let dragDelta = event.clientX - this.lastMouseEvent.clientX; + if (dragDelta !== 0) { if (this.multiDiffModeEnabled) { if (this.multiDiffDragFunction) { - this.multiDiffDragFunction(changeDelta * this.step); + this.multiDiffDragFunction(dragDelta * this.step); } } else { - while (changeDelta !== 0) { - if (changeDelta > 0) { - this.elInput.stepUp(); - --changeDelta; - } else { - this.elInput.stepDown(); - ++changeDelta; - } + if (dragDelta > 0) { + this.elInput.stepUp(dragDelta); + } else { + this.elInput.stepDown(-dragDelta); } this.inputChange(); if (this.valueChangeFunction) { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 1dba7bb861..6a93a84dca 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -14,21 +14,28 @@ const DEGREES_TO_RADIANS = Math.PI / 180.0; const NO_SELECTION = ","; -const PROPERTY_SPACE_MODE = { +const PROPERTY_SPACE_MODE = Object.freeze({ ALL: 0, LOCAL: 1, WORLD: 2 -}; +}); + +const PROPERTY_SELECTION_VISIBILITY = Object.freeze({ + SINGLE_SELECTION: 1, + MULTIPLE_SELECTIONS: 2, + MULTI_DIFF_SELECTIONS: 4, + ANY_SELECTIONS: 7, /* SINGLE_SELECTION | MULTIPLE_SELECTIONS | MULTI_DIFF_SELECTIONS */ +}); // Multiple-selection behavior -const PROPERTY_MULTI_DISPLAY_MODE = { +const PROPERTY_MULTI_DISPLAY_MODE = Object.freeze({ DEFAULT: 0, /** * Comma separated values * Limited for properties with type "string" or "textarea" and readOnly enabled */ COMMA_SEPARATED_VALUES: 1, -}; +}); const GROUPS = [ { @@ -719,6 +726,7 @@ const GROUPS = [ type: "dynamic-multiselect", propertyUpdate: materialTargetPropertyUpdate, propertyID: "parentMaterialName", + selectionVisibility: PROPERTY_SELECTION_VISIBILITY.SINGLE_SELECTION, }, { label: "Priority", @@ -1351,6 +1359,7 @@ const GROUPS = [ type: "placeholder", indentedLabel: true, propertyID: "serverScriptStatus", + selectionVisibility: PROPERTY_SELECTION_VISIBILITY.SINGLE_SELECTION, }, { label: "Lifetime", @@ -1604,7 +1613,6 @@ let createAppTooltip = new CreateAppTooltip(); let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; - // mergeDeep function from https://stackoverflow.com/a/34749873 /** * Simple object check. @@ -1678,6 +1686,10 @@ function createElementFromHTML(htmlString) { return elTemplate.content.firstChild; } +function isFlagSet(value, flag) { + return (value & flag) === flag; +} + /** * GENERAL PROPERTY/GROUP FUNCTIONS */ @@ -1759,8 +1771,11 @@ function disableProperties() { } function showPropertyElement(propertyID, show) { - let elProperty = properties[propertyID].elContainer; - elProperty.style.display = show ? "" : "none"; + setPropertyVisibility(properties[propertyID], show); +} + +function setPropertyVisibility(property, visible) { + property.elContainer.style.display = visible ? null : "none"; } function resetProperties() { @@ -2001,9 +2016,9 @@ function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { }); let keys = [...uniqueKeys]; - let subPropertyDiff = {}; + let childPropertyDiff = {}; Object.entries(detailedValues).forEach(function([key, value]) { - subPropertyDiff[key] = [...new Set(value)].length > 1; + childPropertyDiff[key] = [...new Set(value)].length > 1; }); let averagePerSubProperty = {}; @@ -2014,12 +2029,12 @@ function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { return { keys, - subPropertyDiff, + childPropertyDiff, averagePerSubProperty }; } -function getDetailedSubPropertyMVPDiff(multiplePropertyValue, subPropertyName) { +function getDetailedSubPropertyMPVDiff(multiplePropertyValue, subPropertyName) { let isChecked = false; let checkedValues = multiplePropertyValue.values.map((value) => value.split(",").includes(subPropertyName)); let isMultiDiff = !checkedValues.every(value => value === checkedValues[0]); @@ -2171,7 +2186,7 @@ function createEmitNumberPropertyUpdateFunction(property) { }; } -function createEmitNumberSubPropertyUpdateFunction(property, subProperty) { +function createEmitNumberChildPropertyUpdateFunction(property, childProperty) { return function() { let propertyMultiValue = getMultiplePropertyValue(property.name); let value = parseFloat(applyOutputNumberPropertyModifiers(parseFloat(this.value), property.data)); @@ -2184,7 +2199,7 @@ function createEmitNumberSubPropertyUpdateFunction(property, subProperty) { let entityID = selectedEntityIDsArray[i]; let propertyObject = propertyMultiValue.values[i]; - propertyObject[subProperty] = value; + propertyObject[childProperty] = value; let updateObject = createPropertyUpdateObject(property.name, propertyObject); updateObjects.push({ @@ -2200,10 +2215,9 @@ function createEmitNumberSubPropertyUpdateFunction(property, subProperty) { // callback until drag is complete (additional update sent via dragEnd callback) let onlyUpdateEntity = properties[property.name] && properties[property.name].dragging === true; updateMultiDiffProperties(updateObjects, onlyUpdateEntity); - console.log('updateMultiDiffProperties'); } else { let propertyValue = propertyMultiValue.value; - propertyValue[subProperty] = value; + propertyValue[childProperty] = value; updateProperty(property.name, propertyValue, property.isParticleProperty); } }; @@ -2386,11 +2400,11 @@ function updateNumberMinMax(property) { /** * * @param {object} property - property update on drag - * @param {string} [subProperty] - subProperty to update on drag (e.g. enter 'x' to just update position.x) + * @param {string} [childProperty] - childProperty to update on drag (e.g. enter 'x' to just update position.x) * @returns {Function} */ -function createMultiDiffDragFunction(property, subProperty) { - return function(changedDelta) { +function createMultiDiffDragFunction(property, childProperty) { + return function(dragDelta) { let propertyMultiValue = getMultiplePropertyValue(property.name); if (!propertyMultiValue.isMultiDiffValue) { console.log("setMultiDiffDragFunction is only supposed to be called in MultiDiff mode."); @@ -2399,7 +2413,7 @@ function createMultiDiffDragFunction(property, subProperty) { let multiplier = property.data.multiplier !== undefined ? property.data.multiplier : 1; - let applyDelta = changedDelta * multiplier; + let applyDelta = dragDelta * multiplier; if (selectedEntityIDs.size !== propertyMultiValue.values.length) { console.log("selectedEntityIDs and propertyMultiValue got out of sync."); @@ -2412,9 +2426,9 @@ function createMultiDiffDragFunction(property, subProperty) { let entityID = selectedEntityIDsArray[i]; let updatedValue; - if (subProperty !== undefined) { + if (childProperty !== undefined) { let objectToUpdate = propertyMultiValue.values[i]; - objectToUpdate[subProperty] += applyDelta; + objectToUpdate[childProperty] += applyDelta; updatedValue = objectToUpdate; } else { updatedValue = propertyMultiValue.values[i] + applyDelta; @@ -2492,10 +2506,10 @@ function createRectProperty(property, elProperty) { elWidthHeightRow.appendChild(elNumberWidth.elDiv); elWidthHeightRow.appendChild(elNumberHeight.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); - elNumberWidth.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'width')); - elNumberHeight.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'height')); + elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); + elNumberWidth.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'width')); + elNumberHeight.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'height')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -2531,9 +2545,9 @@ function createVec3Property(property, elProperty) { elProperty.appendChild(elNumberY.elDiv); elProperty.appendChild(elNumberZ.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); - elNumberZ.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'z')); + elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); + elNumberZ.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'z')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -2561,8 +2575,8 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elNumberX.elDiv); elProperty.appendChild(elNumberY.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberSubPropertyUpdateFunction(property, 'y')); + elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -3619,7 +3633,7 @@ function applyOutputNumberPropertyModifiers(number, propertyData) { return roundAndFixNumber(number * multiplier, propertyData); } -const isSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); +const areSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { @@ -3627,7 +3641,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { currentSelections = selections; selectedEntityIDs = new Set(selections.map(selection => selection.id)); const multipleSelections = currentSelections.length > 1; - const hasSelectedEntityChanged = !isSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs); + const hasSelectedEntityChanged = !areSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs); // FIXME: do we really want to save userData/materialData here instead of saving it on the blur event of the json editor? if (hasSelectedEntityChanged) { @@ -3667,9 +3681,8 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { // we will ignore the event. return; } + if (hasSelectedEntityChanged) { - let elServerScriptStatusOuter = document.getElementById('div-property-serverScriptStatus'); - elServerScriptStatusOuter.style.display = multipleSelections ? "none" : null; if (!multipleSelections) { resetServerScriptStatus(); } @@ -3706,6 +3719,19 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { let isMultiDiffValue = propertyMultiValue.isMultiDiffValue; let propertyValue = propertyMultiValue.value; + if (propertyData.selectionVisibility !== undefined) { + let visibility = propertyData.selectionVisibility; + let propertyVisible = true; + if (!multipleSelections) { + propertyVisible = isFlagSet(visibility, PROPERTY_SELECTION_VISIBILITY.SINGLE_SELECTION); + } else if (isMultiDiffValue) { + propertyVisible = isFlagSet(visibility, PROPERTY_SELECTION_VISIBILITY.MULTI_DIFF_SELECTIONS); + } else { + propertyVisible = isFlagSet(visibility, PROPERTY_SELECTION_VISIBILITY.MULTIPLE_SELECTIONS); + } + setPropertyVisibility(property, propertyVisible); + } + const isSubProperty = propertyData.subPropertyOf !== undefined; if (propertyValue === undefined && !isMultiDiffValue && !isSubProperty) { return; @@ -3752,7 +3778,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { let propertyValue = subPropertyMultiValue.value; isMultiDiffValue = subPropertyMultiValue.isMultiDiffValue; if (isMultiDiffValue) { - let detailedSubProperty = getDetailedSubPropertyMVPDiff(subPropertyMultiValue, propertyName); + let detailedSubProperty = getDetailedSubPropertyMPVDiff(subPropertyMultiValue, propertyName); property.elInput.checked = detailedSubProperty.isChecked; property.elInput.classList.toggle('multi-diff', detailedSubProperty.isMultiDiff); } else { @@ -3774,35 +3800,30 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { break; } case 'number': { - if (isMultiDiffValue) { - property.elInput.value = ""; - property.elInput.classList.add('multi-diff'); - } else { - property.elInput.value = propertyValue; - property.elInput.classList.remove('multi-diff'); - } + property.elInput.value = isMultiDiffValue ? "" : propertyValue; + property.elInput.classList.toggle('multi-diff', isMultiDiffValue); break; } case 'number-draggable': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumber.setValue(detailedNumberDiff.averagePerSubProperty[0], detailedNumberDiff.subPropertyDiff[0]); + property.elNumber.setValue(detailedNumberDiff.averagePerSubProperty[0], detailedNumberDiff.childPropertyDiff[0]); break; } case 'rect': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.subPropertyDiff.x); - property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.subPropertyDiff.y); - property.elNumberWidth.setValue(detailedNumberDiff.averagePerSubProperty.width, detailedNumberDiff.subPropertyDiff.width); - property.elNumberHeight.setValue(detailedNumberDiff.averagePerSubProperty.height, detailedNumberDiff.subPropertyDiff.height); + property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.childPropertyDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.childPropertyDiff.y); + property.elNumberWidth.setValue(detailedNumberDiff.averagePerSubProperty.width, detailedNumberDiff.childPropertyDiff.width); + property.elNumberHeight.setValue(detailedNumberDiff.averagePerSubProperty.height, detailedNumberDiff.childPropertyDiff.height); break; } case 'vec3': case 'vec2': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.subPropertyDiff.x); - property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.subPropertyDiff.y); + property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.childPropertyDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.childPropertyDiff.y); if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(detailedNumberDiff.averagePerSubProperty.z, detailedNumberDiff.subPropertyDiff.z); + property.elNumberZ.setValue(detailedNumberDiff.averagePerSubProperty.z, detailedNumberDiff.childPropertyDiff.z); } break; } @@ -3848,7 +3869,7 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } case 'texture': { property.elInput.value = isMultiDiffValue ? "" : propertyValue; - property.elInput.classList.toggle('multi-diff'); + property.elInput.classList.toggle('multi-diff', isMultiDiffValue); if (isMultiDiffValue) { property.elInput.setMultipleValues(); } else { From 00a024fde2bbcf7a2eb45c6655e3f8aecc60cc1a Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 12 Apr 2019 20:06:32 +0200 Subject: [PATCH 27/85] fixing materialTargets, after the rebase --- scripts/system/html/js/entityProperties.js | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 6a93a84dca..42afbd5849 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1902,6 +1902,13 @@ function showGroupsForTypes(types) { }); } +function getFirstSelectedID() { + if (selectedEntityIDs.size === 0) { + return null; + } + return selectedEntityIDs.values().next().value; +} + const SUPPORTED_FALLBACK_TYPES = ['number', 'number-draggable', 'rect', 'vec3', 'vec2', 'color']; function getMultiplePropertyValue(originalPropertyName) { @@ -2992,7 +2999,7 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI */ function parentIDChanged() { - if (selectedEntityProperties.type === "Material") { + if (currentSelections.length === 1 && currentSelections[0].type === "Material") { requestMaterialTarget(); } } @@ -3478,7 +3485,10 @@ function setTextareaScrolling(element) { */ function requestMaterialTarget() { - EventBridge.emitWebEvent(JSON.stringify({ type: 'materialTargetRequest', entityID: selectedEntityProperties.id })); + EventBridge.emitWebEvent(JSON.stringify({ + type: 'materialTargetRequest', + entityID: getFirstSelectedID(), + })); } function setMaterialTargetData(materialTargetData) { @@ -3643,16 +3653,6 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { const multipleSelections = currentSelections.length > 1; const hasSelectedEntityChanged = !areSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs); - // FIXME: do we really want to save userData/materialData here instead of saving it on the blur event of the json editor? - if (hasSelectedEntityChanged) { - if (editor !== null) { - saveUserData(previouslySelectedEntityIDs); - } - if (materialEditor !== null) { - saveMaterialData(previouslySelectedEntityIDs); - } - } - if (selections.length === 0) { deleteJSONEditor(); deleteJSONMaterialEditor(); @@ -4200,7 +4200,7 @@ function loaded() { } } } else if (data.type === 'materialTargetReply') { - if (data.entityID === selectedEntityProperties.id) { + if (data.entityID === getFirstSelectedID()) { setMaterialTargetData(data.materialTargetData); } } From e7e9715e106949a7a2a7bd0c91b0d92d0f4fe633 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 15 Apr 2019 09:16:59 -0700 Subject: [PATCH 28/85] merging up --- .../utilities/lib/jet/qml/TaskPropView.qml | 47 +++++++++++-------- .../utilities/lib/prop/PropGroup.qml | 18 ++++--- .../utilities/lib/prop/style/Global.qml | 46 +++++++++--------- .../utilities/lib/prop/style/PiCanvasIcon.qml | 10 +++- scripts/developer/utilities/render/luci2.js | 21 ++++----- 5 files changed, 81 insertions(+), 61 deletions(-) diff --git a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml index af4cbb1c9a..be3cda66b3 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml @@ -27,33 +27,40 @@ Prop.PropGroup { property var jobPath: "" property alias label: root.label + property var showProps: true + property var showSubs: true + function populatePropItems() { var propsModel = [] var props = Jet.job_propKeys(rootConfig.getConfig(jobPath)); console.log(JSON.stringify(props)); - for (var p in props) { - propsModel.push({"object": rootConfig.getConfig(jobPath), "property":props[p] }) + if (showProps) { + for (var p in props) { + propsModel.push({"object": rootConfig.getConfig(jobPath), "property":props[p] }) + } + root.updatePropItems(propsModel); } - root.updatePropItems(propsModel); - - Jet.task_traverse(rootConfig.getConfig(jobPath), - function(job, depth, index) { - var component = Qt.createComponent("./TaskPropView.qml"); - component.createObject(root.propItemsPanel, { - "label": job.objectName, - "rootConfig": root.rootConfig, - "jobPath": root.jobPath + '.' + job.objectName - }) - /* var component = Qt.createComponent("../../prop/PropItem.qml"); - component.createObject(root.propItemsPanel, { - "label": root.jobPath + '.' + job.objectName + ' num=' + index, - })*/ - // propsModel.push({"type": "printLabel", "label": root.jobPath + '.' + job.objectName + ' num=' + index }) - - return (depth < 1); - }, 0) + if (showSubs) { + Jet.task_traverse(rootConfig.getConfig(jobPath), + function(job, depth, index) { + var component = Qt.createComponent("./TaskPropView.qml"); + component.createObject(root.propItemsPanel, { + "label": job.objectName, + "rootConfig": root.rootConfig, + "jobPath": root.jobPath + '.' + job.objectName, + "showProps": root.showProps, + "showSubs": root.showSubs + }) + /* var component = Qt.createComponent("../../prop/PropItem.qml"); + component.createObject(root.propItemsPanel, { + "label": root.jobPath + '.' + job.objectName + ' num=' + index, + })*/ + // propsModel.push({"type": "printLabel", "label": root.jobPath + '.' + job.objectName + ' num=' + index }) + return (depth < 1); + }, 0) + } } Component.onCompleted: { diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index f4b1ba02d6..4c37eecc80 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -18,7 +18,7 @@ Item { property alias isUnfold: headerFolderIcon.icon property alias propItemsPanel: propItemsContainer - property alias headerContainer: headerContainer + default property alias extHeader: headerContainer.data // Header Item Item { @@ -39,7 +39,9 @@ Item { id: headerFolderIcon anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - + fillColor: global.colorOrangeAccent + filled: root.propItemsPanel.height > 4 + MouseArea{ id: mousearea anchors.fill: parent @@ -80,6 +82,7 @@ Item { border.width: global.valueBorderWidth radius: global.valueBorderRadius + anchors.margins: 0 anchors.left: parent.left anchors.right: parent.right anchors.top: header.bottom @@ -87,9 +90,11 @@ Item { Column { id: propItemsContainer - // anchors.top: header.bottom anchors.left: parent.left - anchors.right: parent.right + anchors.right: parent.right + anchors.leftMargin: 0 + anchors.rightMargin: 0 + clip: true // Where the propItems are added @@ -97,8 +102,9 @@ Item { } height: header.height + isUnfold * propItemsContainer.height - anchors.leftMargin: global.horizontalMargin - anchors.rightMargin: global.horizontalMargin +// anchors.leftMargin: global.horizontalMargin +// anchors.rightMargin: global.horizontalMargin + anchors.margins: 0 anchors.left: parent.left anchors.right: parent.right diff --git a/scripts/developer/utilities/lib/prop/style/Global.qml b/scripts/developer/utilities/lib/prop/style/Global.qml index 6066a4f99b..e348338df1 100644 --- a/scripts/developer/utilities/lib/prop/style/Global.qml +++ b/scripts/developer/utilities/lib/prop/style/Global.qml @@ -18,32 +18,36 @@ Item { HifiConstants { id: hifi } id: root - property real lineHeight: 32 - property real slimHeight: 24 + readonly property real lineHeight: 32 + readonly property real slimHeight: 24 - property real horizontalMargin: 4 + readonly property real horizontalMargin: 4 - property var color: hifi.colors.baseGray - property var colorBackHighlight: hifi.colors.baseGrayHighlight - property var colorBorderLight: hifi.colors.lightGray - property var colorBorderHighight: hifi.colors.blueHighlight + readonly property color color: hifi.colors.baseGray + readonly property color colorBackHighlight: hifi.colors.baseGrayHighlight + readonly property color colorBorderLight: hifi.colors.lightGray + readonly property color colorBorderHighight: hifi.colors.blueHighlight - property real fontSize: 12 - property var fontFamily: "Raleway" - property var fontWeight: Font.DemiBold - property var fontColor: hifi.colors.faintGray + readonly property color colorOrangeAccent: "#FF6309" + readonly property color colorRedAccent: "#C62147" + readonly property color colorGreenHighlight: "#1ac567" - property var splitterRightWidthScale: 0.45 - property real splitterWidth: 8 + readonly property real fontSize: 12 + readonly property var fontFamily: "Raleway" + readonly property var fontWeight: Font.DemiBold + readonly property color fontColor: hifi.colors.faintGray - property real iconWidth: fontSize - property real iconHeight: fontSize + readonly property var splitterRightWidthScale: 0.45 + readonly property real splitterWidth: 8 + + readonly property real iconWidth: fontSize + readonly property real iconHeight: fontSize - property var labelTextAlign: Text.AlignRight - property var labelTextElide: Text.ElideMiddle + readonly property var labelTextAlign: Text.AlignRight + readonly property var labelTextElide: Text.ElideMiddle - property var valueAreaWidthScale: 0.3 * (1.0 - splitterRightWidthScale) - property var valueTextAlign: Text.AlignHCenter - property real valueBorderWidth: 1 - property real valueBorderRadius: 2 + readonly property var valueAreaWidthScale: 0.3 * (1.0 - splitterRightWidthScale) + readonly property var valueTextAlign: Text.AlignHCenter + readonly property real valueBorderWidth: 1 + readonly property real valueBorderRadius: 2 } \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml b/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml index 6a805ea4c6..b8e80f1c3b 100644 --- a/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml +++ b/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml @@ -18,6 +18,7 @@ Canvas { height: global.iconHeight property var icon: 0 + property var filled: true onIconChanged: function () { requestPaint() } property var fillColor: global.colorBorderHighight @@ -43,8 +44,13 @@ Canvas { context.lineTo(width / 2, 0); } context.closePath(); - context.fillStyle = fillColor; - context.fill(); + if (filled) { + context.fillStyle = fillColor; + context.fill(); + } else { + context.strokeStyle = fillColor; + context.stroke(); + } }} } } \ No newline at end of file diff --git a/scripts/developer/utilities/render/luci2.js b/scripts/developer/utilities/render/luci2.js index 4aea49a059..d6bb005795 100644 --- a/scripts/developer/utilities/render/luci2.js +++ b/scripts/developer/utilities/render/luci2.js @@ -1,13 +1,10 @@ -function openEngineTaskView() { +function openView() { // Set up the qml ui - var qml = Script.resolvePath('luci.qml'); - var window = new OverlayWindow({ - title: 'luci qml', - source: qml, - width: 300, - height: 400 - }); - window.setPosition(200, 50); - //window.closed.connect(function() { Script.stop(); }); - } - openEngineTaskView(); \ No newline at end of file + var window = Desktop.createWindow(Script.resolvePath('luci.qml'), { + title: this.title, + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: 300, y: 400} + }); + //window.closed.connect(function() { Script.stop(); }); +} +openView(); \ No newline at end of file From ef7c34eaddc3bd9bd9bc5c86e81ed33ef220a091 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 15 Apr 2019 19:04:24 +0200 Subject: [PATCH 29/85] addressed more CR feedback --- scripts/system/edit.js | 10 +- scripts/system/html/js/draggableNumber.js | 3 +- scripts/system/html/js/entityProperties.js | 147 +++++++-------------- scripts/system/html/js/utils.js | 67 ++++++++++ 4 files changed, 120 insertions(+), 107 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index dbb429e86a..104648d7c4 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2365,12 +2365,10 @@ var PropertiesTool = function (opts) { if (data.properties || data.propertiesMap) { var propertiesMap = data.propertiesMap; if (propertiesMap === undefined) { - var updateEntityIDs = data.ids !== undefined ? data.ids : selectionManager.selections; - propertiesMap = []; - propertiesMap.push({ - entityIDs: updateEntityIDs, - properties: data.properties - }); + propertiesMap = [{ + entityIDs: data.ids, + properties: data.properties, + }]; } var sendListUpdate = false; diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index d76874d4d9..30e8204703 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -132,7 +132,8 @@ DraggableNumber.prototype = { } if (isNaN(newValue)) { - throw newValue + " is not a number"; + console.error("DraggableNumber.setValue() > " + newValue + " is not a number."); + return; } if (newValue !== "" && this.decimals !== undefined) { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 42afbd5849..fe8a08d53d 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1613,73 +1613,6 @@ let createAppTooltip = new CreateAppTooltip(); let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; -// mergeDeep function from https://stackoverflow.com/a/34749873 -/** - * Simple object check. - * @param item - * @returns {boolean} - */ -function isObject(item) { - return (item && typeof item === 'object' && !Array.isArray(item)); -} - -/** - * Deep merge two objects. - * @param target - * @param sources - */ -function mergeDeep(target, ...sources) { - if (!sources.length) { - return target; - } - const source = sources.shift(); - - if (isObject(target) && isObject(source)) { - for (const key in source) { - if (!source.hasOwnProperty(key)) { - continue; - } - if (isObject(source[key])) { - if (!target[key]) { - Object.assign(target, { [key]: {} }); - } - mergeDeep(target[key], source[key]); - } else { - Object.assign(target, { [key]: source[key] }); - } - } - } - - return mergeDeep(target, ...sources); -} - -function deepEqual(a, b) { - if (a === b) { - return true; - } - - if (typeof(a) !== "object" || typeof(b) !== "object") { - return false; - } - - if (Object.keys(a).length !== Object.keys(b).length) { - return false; - } - - for (let property in a) { - if (!a.hasOwnProperty(property)) { - continue; - } - if (!b.hasOwnProperty(property)) { - return false; - } - if (!deepEqual(a[property], b[property])) { - return false; - } - } - return true; -} - function createElementFromHTML(htmlString) { let elTemplate = document.createElement('template'); elTemplate.innerHTML = htmlString.trim(); @@ -1909,6 +1842,15 @@ function getFirstSelectedID() { return selectedEntityIDs.values().next().value; } +/** + * Returns true when the user is currently dragging the numeric slider control of the property + * @param propertyName - name of property + * @returns {boolean} currentlyDragging + */ +function isCurrentlyDraggingProperty(propertyName) { + return properties[propertyName] && properties[propertyName].dragging === true; +} + const SUPPORTED_FALLBACK_TYPES = ['number', 'number-draggable', 'rect', 'vec3', 'vec2', 'color']; function getMultiplePropertyValue(originalPropertyName) { @@ -1993,7 +1935,12 @@ function getMultiplePropertyValue(originalPropertyName) { }; } - +/** + * Retrieve more detailed info for differing Numeric MultiplePropertyValue + * @param multiplePropertyValue - input multiplePropertyValue + * @param propertyData + * @returns {{keys: *[], propertyComponentDiff, averagePerPropertyComponent}} + */ function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { let detailedValues = {}; // Fixed numbers can't be easily averaged since they're strings, so lets keep an array of unmodified numbers @@ -2023,21 +1970,21 @@ function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { }); let keys = [...uniqueKeys]; - let childPropertyDiff = {}; + let propertyComponentDiff = {}; Object.entries(detailedValues).forEach(function([key, value]) { - childPropertyDiff[key] = [...new Set(value)].length > 1; + propertyComponentDiff[key] = [...new Set(value)].length > 1; }); - let averagePerSubProperty = {}; + let averagePerPropertyComponent = {}; Object.entries(unmodifiedValues).forEach(function([key, value]) { let average = value.reduce((a, b) => a + b) / value.length; - averagePerSubProperty[key] = applyInputNumberPropertyModifiers(average, propertyData); + averagePerPropertyComponent[key] = applyInputNumberPropertyModifiers(average, propertyData); }); return { keys, - childPropertyDiff, - averagePerSubProperty + propertyComponentDiff, + averagePerPropertyComponent, }; } @@ -2108,7 +2055,7 @@ function updateProperty(originalPropertyName, propertyValue, isParticleProperty) // only update the entity property value itself if in the middle of dragging // prevent undo command push, saving new property values, and property update // callback until drag is complete (additional update sent via dragEnd callback) - let onlyUpdateEntity = properties[originalPropertyName] && properties[originalPropertyName].dragging === true; + let onlyUpdateEntity = isCurrentlyDraggingProperty(originalPropertyName); updateProperties(propertyUpdate, onlyUpdateEntity); } } @@ -2193,7 +2140,7 @@ function createEmitNumberPropertyUpdateFunction(property) { }; } -function createEmitNumberChildPropertyUpdateFunction(property, childProperty) { +function createEmitNumberPropertyComponentUpdateFunction(property, propertyComponent) { return function() { let propertyMultiValue = getMultiplePropertyValue(property.name); let value = parseFloat(applyOutputNumberPropertyModifiers(parseFloat(this.value), property.data)); @@ -2206,7 +2153,7 @@ function createEmitNumberChildPropertyUpdateFunction(property, childProperty) { let entityID = selectedEntityIDsArray[i]; let propertyObject = propertyMultiValue.values[i]; - propertyObject[childProperty] = value; + propertyObject[propertyComponent] = value; let updateObject = createPropertyUpdateObject(property.name, propertyObject); updateObjects.push({ @@ -2220,11 +2167,11 @@ function createEmitNumberChildPropertyUpdateFunction(property, childProperty) { // only update the entity property value itself if in the middle of dragging // prevent undo command push, saving new property values, and property update // callback until drag is complete (additional update sent via dragEnd callback) - let onlyUpdateEntity = properties[property.name] && properties[property.name].dragging === true; + let onlyUpdateEntity = isCurrentlyDraggingProperty(property.name); updateMultiDiffProperties(updateObjects, onlyUpdateEntity); } else { let propertyValue = propertyMultiValue.value; - propertyValue[childProperty] = value; + propertyValue[propertyComponent] = value; updateProperty(property.name, propertyValue, property.isParticleProperty); } }; @@ -2407,10 +2354,10 @@ function updateNumberMinMax(property) { /** * * @param {object} property - property update on drag - * @param {string} [childProperty] - childProperty to update on drag (e.g. enter 'x' to just update position.x) + * @param {string} [propertyComponent] - propertyComponent to update on drag (e.g. enter 'x' to just update position.x) * @returns {Function} */ -function createMultiDiffDragFunction(property, childProperty) { +function createMultiDiffDragFunction(property, propertyComponent) { return function(dragDelta) { let propertyMultiValue = getMultiplePropertyValue(property.name); if (!propertyMultiValue.isMultiDiffValue) { @@ -2433,9 +2380,9 @@ function createMultiDiffDragFunction(property, childProperty) { let entityID = selectedEntityIDsArray[i]; let updatedValue; - if (childProperty !== undefined) { + if (propertyComponent !== undefined) { let objectToUpdate = propertyMultiValue.values[i]; - objectToUpdate[childProperty] += applyDelta; + objectToUpdate[propertyComponent] += applyDelta; updatedValue = objectToUpdate; } else { updatedValue = propertyMultiValue.values[i] + applyDelta; @@ -2513,10 +2460,10 @@ function createRectProperty(property, elProperty) { elWidthHeightRow.appendChild(elNumberWidth.elDiv); elWidthHeightRow.appendChild(elNumberHeight.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); - elNumberWidth.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'width')); - elNumberHeight.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'height')); + elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); + elNumberWidth.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'width')); + elNumberHeight.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'height')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -2552,9 +2499,9 @@ function createVec3Property(property, elProperty) { elProperty.appendChild(elNumberY.elDiv); elProperty.appendChild(elNumberZ.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); - elNumberZ.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'z')); + elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); + elNumberZ.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'z')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -2582,8 +2529,8 @@ function createVec2Property(property, elProperty) { elProperty.appendChild(elNumberX.elDiv); elProperty.appendChild(elNumberY.elDiv); - elNumberX.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberChildPropertyUpdateFunction(property, 'y')); + elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); + elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); @@ -3806,24 +3753,24 @@ function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { } case 'number-draggable': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumber.setValue(detailedNumberDiff.averagePerSubProperty[0], detailedNumberDiff.childPropertyDiff[0]); + property.elNumber.setValue(detailedNumberDiff.averagePerPropertyComponent[0], detailedNumberDiff.propertyComponentDiff[0]); break; } case 'rect': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.childPropertyDiff.x); - property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.childPropertyDiff.y); - property.elNumberWidth.setValue(detailedNumberDiff.averagePerSubProperty.width, detailedNumberDiff.childPropertyDiff.width); - property.elNumberHeight.setValue(detailedNumberDiff.averagePerSubProperty.height, detailedNumberDiff.childPropertyDiff.height); + property.elNumberX.setValue(detailedNumberDiff.averagePerPropertyComponent.x, detailedNumberDiff.propertyComponentDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerPropertyComponent.y, detailedNumberDiff.propertyComponentDiff.y); + property.elNumberWidth.setValue(detailedNumberDiff.averagePerPropertyComponent.width, detailedNumberDiff.propertyComponentDiff.width); + property.elNumberHeight.setValue(detailedNumberDiff.averagePerPropertyComponent.height, detailedNumberDiff.propertyComponentDiff.height); break; } case 'vec3': case 'vec2': { let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumberX.setValue(detailedNumberDiff.averagePerSubProperty.x, detailedNumberDiff.childPropertyDiff.x); - property.elNumberY.setValue(detailedNumberDiff.averagePerSubProperty.y, detailedNumberDiff.childPropertyDiff.y); + property.elNumberX.setValue(detailedNumberDiff.averagePerPropertyComponent.x, detailedNumberDiff.propertyComponentDiff.x); + property.elNumberY.setValue(detailedNumberDiff.averagePerPropertyComponent.y, detailedNumberDiff.propertyComponentDiff.y); if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(detailedNumberDiff.averagePerSubProperty.z, detailedNumberDiff.childPropertyDiff.z); + property.elNumberZ.setValue(detailedNumberDiff.averagePerPropertyComponent.z, detailedNumberDiff.propertyComponentDiff.z); } break; } diff --git a/scripts/system/html/js/utils.js b/scripts/system/html/js/utils.js index d61b4d1762..9556856089 100644 --- a/scripts/system/html/js/utils.js +++ b/scripts/system/html/js/utils.js @@ -25,3 +25,70 @@ function disableDragDrop() { event.preventDefault(); }, false); } + +// mergeDeep function from https://stackoverflow.com/a/34749873 +/** + * Simple object check. + * @param item + * @returns {boolean} + */ +function mergeDeepIsObject(item) { + return (item && typeof item === 'object' && !Array.isArray(item)); +} + +/** + * Deep merge two objects. + * @param target + * @param sources + */ +function mergeDeep(target, ...sources) { + if (!sources.length) { + return target; + } + const source = sources.shift(); + + if (mergeDeepIsObject(target) && mergeDeepIsObject(source)) { + for (const key in source) { + if (!source.hasOwnProperty(key)) { + continue; + } + if (mergeDeepIsObject(source[key])) { + if (!target[key]) { + Object.assign(target, { [key]: {} }); + } + mergeDeep(target[key], source[key]); + } else { + Object.assign(target, { [key]: source[key] }); + } + } + } + + return mergeDeep(target, ...sources); +} + +function deepEqual(a, b) { + if (a === b) { + return true; + } + + if (typeof(a) !== "object" || typeof(b) !== "object") { + return false; + } + + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + + for (let property in a) { + if (!a.hasOwnProperty(property)) { + continue; + } + if (!b.hasOwnProperty(property)) { + return false; + } + if (!deepEqual(a[property], b[property])) { + return false; + } + } + return true; +} From 249df3dce49117dbf3b41f1a78f1d13a7ef54312 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 16 Apr 2019 07:53:55 +1200 Subject: [PATCH 30/85] Update Record app per Marketplace certification requirements --- .../marketplace/record/assets}/avatar-record-a.svg | 0 .../marketplace/record/assets}/avatar-record-i.svg | 0 unpublishedScripts/marketplace/record/record.app.json | 4 ++++ unpublishedScripts/marketplace/record/record.js | 4 ++-- 4 files changed, 6 insertions(+), 2 deletions(-) rename {interface/resources/icons/tablet-icons => unpublishedScripts/marketplace/record/assets}/avatar-record-a.svg (100%) rename {interface/resources/icons/tablet-icons => unpublishedScripts/marketplace/record/assets}/avatar-record-i.svg (100%) create mode 100644 unpublishedScripts/marketplace/record/record.app.json diff --git a/interface/resources/icons/tablet-icons/avatar-record-a.svg b/unpublishedScripts/marketplace/record/assets/avatar-record-a.svg similarity index 100% rename from interface/resources/icons/tablet-icons/avatar-record-a.svg rename to unpublishedScripts/marketplace/record/assets/avatar-record-a.svg diff --git a/interface/resources/icons/tablet-icons/avatar-record-i.svg b/unpublishedScripts/marketplace/record/assets/avatar-record-i.svg similarity index 100% rename from interface/resources/icons/tablet-icons/avatar-record-i.svg rename to unpublishedScripts/marketplace/record/assets/avatar-record-i.svg diff --git a/unpublishedScripts/marketplace/record/record.app.json b/unpublishedScripts/marketplace/record/record.app.json new file mode 100644 index 0000000000..3b2860db01 --- /dev/null +++ b/unpublishedScripts/marketplace/record/record.app.json @@ -0,0 +1,4 @@ +{ + "scriptURL": "http://mpassets.highfidelity.com/b3da90d4-390f-4f2c-ac78-6c8e074c93d7-v1/record.js", + "homeURL": "http://mpassets.highfidelity.com/b3da90d4-390f-4f2c-ac78-6c8e074c93d7-v1/html/record.html" +} diff --git a/unpublishedScripts/marketplace/record/record.js b/unpublishedScripts/marketplace/record/record.js index 4786356fee..18bcfbf5a1 100644 --- a/unpublishedScripts/marketplace/record/record.js +++ b/unpublishedScripts/marketplace/record/record.js @@ -13,8 +13,8 @@ (function () { var APP_NAME = "RECORD", - APP_ICON_INACTIVE = "icons/tablet-icons/avatar-record-i.svg", - APP_ICON_ACTIVE = "icons/tablet-icons/avatar-record-a.svg", + APP_ICON_INACTIVE = Script.resolvePath("assets/avatar-record-i.svg"), + APP_ICON_ACTIVE = Script.resolvePath("assets/avatar-record-a.svg"), APP_URL = Script.resolvePath("html/record.html"), isDialogDisplayed = false, tablet, From f2b747a68da13937ff85af386fc6c130f95890db Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 16 Apr 2019 18:15:00 -0700 Subject: [PATCH 31/85] Adding indentation of the folder in the header and refining background color --- .../utilities/lib/jet/qml/TaskPropView.qml | 6 ++++-- .../utilities/lib/prop/PropGroup.qml | 19 ++++++++++++++++--- .../utilities/lib/prop/style/Global.qml | 1 + 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml index be3cda66b3..9f64f12291 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml @@ -26,6 +26,7 @@ Prop.PropGroup { property var rootConfig : Render property var jobPath: "" property alias label: root.label + property alias indentDepth: root.indentDepth property var showProps: true property var showSubs: true @@ -33,7 +34,7 @@ Prop.PropGroup { function populatePropItems() { var propsModel = [] var props = Jet.job_propKeys(rootConfig.getConfig(jobPath)); - console.log(JSON.stringify(props)); + // console.log(JSON.stringify(props)); if (showProps) { for (var p in props) { propsModel.push({"object": rootConfig.getConfig(jobPath), "property":props[p] }) @@ -50,7 +51,8 @@ Prop.PropGroup { "rootConfig": root.rootConfig, "jobPath": root.jobPath + '.' + job.objectName, "showProps": root.showProps, - "showSubs": root.showSubs + "showSubs": root.showSubs, + "indentDepth": root.indentDepth + 1, }) /* var component = Qt.createComponent("../../prop/PropItem.qml"); component.createObject(root.propItemsPanel, { diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 4c37eecc80..a4e192308d 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -17,20 +17,33 @@ Item { property var label: "group" property alias isUnfold: headerFolderIcon.icon + property var indentDepth: 0 + property alias propItemsPanel: propItemsContainer default property alias extHeader: headerContainer.data // Header Item - Item { + Rectangle { id: header height: global.slimHeight anchors.left: parent.left anchors.right: parent.right - // First in the header, the folder button / indicator + color: global.colorBackShadow // header of group is darker + + // First in the header, some indentation spacer + Item { + id: indentSpacer + width: (headerFolderIcon.width * root.indentDepth) + global.horizontalMargin // Must be non-zero + height: parent.height + + anchors.verticalCenter: parent.verticalCenter + } + + // Second, the folder button / indicator Item { id: headerFolder - anchors.left: header.left + anchors.left: indentSpacer.right width: headerFolderIcon.width * 2 anchors.verticalCenter: header.verticalCenter height: parent.height diff --git a/scripts/developer/utilities/lib/prop/style/Global.qml b/scripts/developer/utilities/lib/prop/style/Global.qml index e348338df1..4cdee70244 100644 --- a/scripts/developer/utilities/lib/prop/style/Global.qml +++ b/scripts/developer/utilities/lib/prop/style/Global.qml @@ -24,6 +24,7 @@ Item { readonly property real horizontalMargin: 4 readonly property color color: hifi.colors.baseGray + readonly property color colorBackShadow: hifi.colors.baseGrayShadow readonly property color colorBackHighlight: hifi.colors.baseGrayHighlight readonly property color colorBorderLight: hifi.colors.lightGray readonly property color colorBorderHighight: hifi.colors.blueHighlight From 4ce7d75720ee92015037f6fd0b10edb2d0ea5a61 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 16 Apr 2019 23:38:40 -0700 Subject: [PATCH 32/85] FInding a path --- .../utilities/lib/jet/qml/TaskPropView.qml | 30 +++++++++++++++++++ .../utilities/lib/prop/PropGroup.qml | 27 ++++++++--------- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml index 9f64f12291..2f7ee71a5f 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml @@ -30,6 +30,36 @@ Prop.PropGroup { property var showProps: true property var showSubs: true + property var jobEnabled: true + + // Panel Header Data Component + panelHeaderData: Component { + Item { + id: header + Prop.PropLabel { + text: root.label + horizontalAlignment: Text.AlignHCenter + anchors.left: parent.left + anchors.right: enabledIcon.left + anchors.verticalCenter: parent.verticalCenter + } + Prop.PropCanvasIcon { + id: enabledIcon + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + fillColor: global.colorOrangeAccent + filled: jobEnabled + + MouseArea{ + id: mousearea + anchors.fill: parent + onClicked: { + root.jobEnabled = !root.jobEnabled + } + } + } + } + } function populatePropItems() { var propsModel = [] diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index a4e192308d..6836e85e25 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -20,7 +20,16 @@ Item { property var indentDepth: 0 property alias propItemsPanel: propItemsContainer - default property alias extHeader: headerContainer.data + + // Panel Header Data Component + property Component panelHeaderData: defaultPanelHeaderData + Component { // default is a Label + id: defaultPanelHeaderData + PropLabel { + text: root.label + horizontalAlignment: Text.AlignHCenter + } + } // Header Item Rectangle { @@ -67,22 +76,12 @@ Item { // Next the header container // by default containing a Label showing the root.label - Item { - id: headerContainer + Loader { + sourceComponent: panelHeaderData anchors.left: headerFolder.right anchors.right: header.right anchors.verticalCenter: header.verticalCenter height: parent.height - - PropLabel { - id: labelControl - anchors.left: headerContainer.left - anchors.right: headerContainer.right - anchors.verticalCenter: headerContainer.verticalCenter - text: root.label - horizontalAlignment: Text.AlignHCenter - } - } } @@ -115,8 +114,6 @@ Item { } height: header.height + isUnfold * propItemsContainer.height -// anchors.leftMargin: global.horizontalMargin -// anchors.rightMargin: global.horizontalMargin anchors.margins: 0 anchors.left: parent.left anchors.right: parent.right From ae20a5d439379e771acdec2b484d43a11ae6f3bc Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 17 Apr 2019 09:47:33 +0200 Subject: [PATCH 33/85] Fixed crash in debugShadow.js --- libraries/render-utils/src/RenderDeferredTask.cpp | 2 +- libraries/render-utils/src/RenderShadowTask.cpp | 2 +- libraries/render-utils/src/RenderShadowTask.h | 3 ++- libraries/task/src/task/Task.h | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index ea2b05a6fa..aae3237784 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -364,7 +364,7 @@ void RenderDeferredTaskDebug::build(JobModel& task, const render::Varying& input sprintf(jobName, "DrawShadowFrustum%d", i); task.addJob(jobName, shadowFrustum, glm::vec3(0.0f, tint, 1.0f)); if (!renderShadowTaskOut.isNull()) { - const auto& shadowCascadeSceneBBoxes = renderShadowTaskOut; + const auto& shadowCascadeSceneBBoxes = renderShadowTaskOut.get(); const auto shadowBBox = shadowCascadeSceneBBoxes[ExtractFrustums::SHADOW_CASCADE0_FRUSTUM + i]; sprintf(jobName, "DrawShadowBBox%d", i); task.addJob(jobName, shadowBBox, glm::vec3(1.0f, tint, 0.0f)); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index a261deefb8..8e6efeab9f 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -90,7 +90,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende #endif }; - render::VaryingArray cascadeSceneBBoxes; + CascadeBoxes cascadeSceneBBoxes; for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { char jobName[64]; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 4dc6f3073f..82b3ebac4f 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -52,8 +52,9 @@ class RenderShadowTask { public: // There is one AABox per shadow cascade + using CascadeBoxes = render::VaryingArray; using Input = render::VaryingSet2; - using Output = render::VaryingSet2, LightStage::ShadowFramePointer>; + using Output = render::VaryingSet2; using Config = RenderShadowTaskConfig; using JobModel = render::Task::ModelIO; diff --git a/libraries/task/src/task/Task.h b/libraries/task/src/task/Task.h index 632e8a222e..b74986235e 100644 --- a/libraries/task/src/task/Task.h +++ b/libraries/task/src/task/Task.h @@ -152,6 +152,7 @@ public: template static std::shared_ptr create(const std::string& name, const Varying& input, A&&... args) { + assert(input.canCast()); return std::make_shared(name, input, std::make_shared(), std::forward(args)...); } From 5d7b149a9ec51fc750b5f4271e449105f42fc440 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 17 Apr 2019 11:12:59 +0200 Subject: [PATCH 34/85] Switched to clamped tangent for shadow slope bias --- libraries/render-utils/src/RenderShadowTask.cpp | 2 +- libraries/render-utils/src/RenderShadowTask.h | 4 ++-- libraries/render-utils/src/Shadow.slh | 15 ++++++++------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 8e6efeab9f..bf564c1c10 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -336,7 +336,7 @@ void RenderShadowSetup::setConstantBias(int cascadeIndex, float value) { } void RenderShadowSetup::setSlopeBias(int cascadeIndex, float value) { - _bias[cascadeIndex]._slope = value * value * value * 0.01f; + _bias[cascadeIndex]._slope = value * value * value * 0.001f; } void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, const Input& input, Output& output) { diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 82b3ebac4f..1cfa786e43 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -95,8 +95,8 @@ public: float constantBias3{ 0.2f }; float slopeBias0{ 0.6f }; float slopeBias1{ 0.6f }; - float slopeBias2{ 0.7f }; - float slopeBias3{ 0.82f }; + float slopeBias2{ 0.65f }; + float slopeBias3{ 0.7f }; signals: void dirty(); diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index b503844554..94bec86b34 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -90,8 +90,8 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float oneMinusNdotL) { - float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * oneMinusNdotL; +float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float slopeNdotL) { + float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * slopeNdotL; return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias); } @@ -104,7 +104,8 @@ float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDe vec3 cascadeMix; bvec4 isPixelOnCascade; int cascadeIndex; - float oneMinusNdotL = 1.0 - clamp(dot(worldLightDir, worldNormal), 0.0, 1.0); + float NdotL = clamp(dot(worldLightDir, worldNormal), 0.0, 1.0); + float slopeNdotL = min(2.0, sqrt(1.0-NdotL*NdotL) / NdotL); for (cascadeIndex=0 ; cascadeIndex Date: Wed, 17 Apr 2019 12:35:24 +0200 Subject: [PATCH 35/85] Added opacity mask to shadow casters --- .../src/graphics/MaterialTextures.slh | 6 ++++ .../render-utils/src/MeshPartPayload.cpp | 5 +++ .../render-utils/src/RenderPipelines.cpp | 35 ++++++++++++++++++- libraries/render-utils/src/RenderPipelines.h | 1 + .../src/deformed_model_shadow.slv | 15 ++++++++ .../src/deformed_model_shadow_dq.slv | 15 ++++++++ .../render-utils/src/model_normal_map.slf | 2 +- libraries/render-utils/src/model_shadow.slf | 18 ++++++++++ libraries/render-utils/src/model_shadow.slv | 13 +++++++ 9 files changed, 108 insertions(+), 2 deletions(-) diff --git a/libraries/graphics/src/graphics/MaterialTextures.slh b/libraries/graphics/src/graphics/MaterialTextures.slh index c725aae9bb..16c7f0e211 100644 --- a/libraries/graphics/src/graphics/MaterialTextures.slh +++ b/libraries/graphics/src/graphics/MaterialTextures.slh @@ -149,6 +149,12 @@ float fetchScatteringMap(vec2 uv) { <@endfunc@> +<@func fetchMaterialAlbedoTextureCoord0(matKey, texcoord0, albedo)@> + if (getTexMapArray()._materialParams.y != 1.0 && clamp(<$texcoord0$>, vec2(0.0), vec2(1.0)) != <$texcoord0$>) { + discard; + } + vec4 <$albedo$> = fetchAlbedoMap(<$texcoord0$>); +<@endfunc@> <@func fetchMaterialTexturesCoord0(matKey, texcoord0, albedo, roughness, normal, metallic, emissive, scattering)@> if (getTexMapArray()._materialParams.y != 1.0 && clamp(<$texcoord0$>, vec2(0.0), vec2(1.0)) != <$texcoord0$>) { diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 2634784f3a..9470f11d59 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -437,6 +437,11 @@ void ModelMeshPartPayload::render(RenderArgs* args) { if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { RenderPipelines::bindMaterials(_drawMaterials, batch, args->_enableTexturing); args->_details._materialSwitches++; + } else { + // We might have an opacity mask so we need to bind the albedo texture and material which might hold + // the alpha mask channel + RenderPipelines::bindMaterialsOpacityMask(_drawMaterials, batch, args->_enableTexturing); + args->_details._materialSwitches++; } // Draw! diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index c4a7368f39..ba0a079c7f 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -730,6 +730,8 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial multiMaterial.setInitialized(); } +static gpu::TextureTablePointer defaultMaterialTextures = std::make_shared(); + void RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures) { if (multiMaterial.shouldUpdate()) { updateMultiMaterial(multiMaterial); @@ -737,7 +739,6 @@ void RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: auto textureCache = DependencyManager::get(); - static gpu::TextureTablePointer defaultMaterialTextures = std::make_shared(); static std::once_flag once; std::call_once(once, [textureCache] { defaultMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); @@ -763,3 +764,35 @@ void RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: batch.setResourceTextureTable(defaultMaterialTextures); } } + +static gpu::BufferView defaultMaterialSchema; + +void RenderPipelines::bindMaterialsOpacityMask(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures) { + if (multiMaterial.shouldUpdate()) { + updateMultiMaterial(multiMaterial); + } + + if (multiMaterial.getMaterialKey().isOpacityMaskMap()) { + auto textureCache = DependencyManager::get(); + + static std::once_flag once; + std::call_once(once, [textureCache] { + defaultMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); + }); + + auto& schemaBuffer = multiMaterial.getSchemaBuffer(); + batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer); + + if (enableTextures) { + batch.setResourceTextureTable(multiMaterial.getTextureTable()); + } else { + batch.setResourceTextureTable(defaultMaterialTextures); + } + } else { + if (defaultMaterialSchema._buffer == nullptr) { + graphics::MultiMaterial::Schema schema; + defaultMaterialSchema = gpu::BufferView(std::make_shared(sizeof(schema), (const gpu::Byte*) &schema, sizeof(schema))); + } + batch.setUniformBuffer(gr::Buffer::Material, defaultMaterialSchema); + } +} diff --git a/libraries/render-utils/src/RenderPipelines.h b/libraries/render-utils/src/RenderPipelines.h index 0f3d1160ef..4a99b21e74 100644 --- a/libraries/render-utils/src/RenderPipelines.h +++ b/libraries/render-utils/src/RenderPipelines.h @@ -18,6 +18,7 @@ public: static void bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures); static void updateMultiMaterial(graphics::MultiMaterial& multiMaterial); static void bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures); + static void bindMaterialsOpacityMask(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures); }; diff --git a/libraries/render-utils/src/deformed_model_shadow.slv b/libraries/render-utils/src/deformed_model_shadow.slv index cc6fb42f98..827fc69b32 100644 --- a/libraries/render-utils/src/deformed_model_shadow.slv +++ b/libraries/render-utils/src/deformed_model_shadow.slv @@ -18,9 +18,15 @@ <$declareMeshDeformer(_SCRIBE_NULL, _SCRIBE_NULL, 1, _SCRIBE_NULL, 1)$> <$declareMeshDeformerActivation(1, 1)$> +<@include graphics/Material.slh@> +<@include graphics/MaterialTextures.slh@> + +<$declareMaterialTexMapArrayBuffer()$> + <@include render-utils/ShaderConstants.h@> layout(location=RENDER_UTILS_ATTR_POSITION_WS) out vec4 _positionWS; +layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; void main(void) { vec4 deformedPosition = vec4(0.0, 0.0, 0.0, 0.0); @@ -33,5 +39,14 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, deformedPosition, gl_Position)$> <$transformModelToWorldPos(obj, deformedPosition, _positionWS)$> + + Material mat = getMaterial(); + BITFIELD matKey = getMaterialKey(mat); + _texCoord01 = vec4(0.0, 0.0, 0.0, 0.0); + // If we have an opacity mask than we need the first tex coord + if ((matKey & OPACITY_MASK_MAP_BIT) != 0) { + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _positionWS, _texCoord01.xy)$> + } } diff --git a/libraries/render-utils/src/deformed_model_shadow_dq.slv b/libraries/render-utils/src/deformed_model_shadow_dq.slv index 84b5dfb33b..646fc12ce9 100644 --- a/libraries/render-utils/src/deformed_model_shadow_dq.slv +++ b/libraries/render-utils/src/deformed_model_shadow_dq.slv @@ -18,9 +18,15 @@ <$declareMeshDeformer(_SCRIBE_NULL, _SCRIBE_NULL, 1, 1, 1)$> <$declareMeshDeformerActivation(1, 1)$> +<@include graphics/Material.slh@> +<@include graphics/MaterialTextures.slh@> + +<$declareMaterialTexMapArrayBuffer()$> + <@include render-utils/ShaderConstants.h@> layout(location=RENDER_UTILS_ATTR_POSITION_WS) out vec4 _positionWS; +layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; void main(void) { vec4 deformedPosition = vec4(0.0, 0.0, 0.0, 0.0); @@ -33,4 +39,13 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, deformedPosition, gl_Position)$> <$transformModelToWorldPos(obj, deformedPosition, _positionWS)$> + + Material mat = getMaterial(); + BITFIELD matKey = getMaterialKey(mat); + _texCoord01 = vec4(0.0, 0.0, 0.0, 0.0); + // If we have an opacity mask than we need the first tex coord + if ((matKey & OPACITY_MASK_MAP_BIT) != 0) { + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _positionWS, _texCoord01.xy)$> + } } diff --git a/libraries/render-utils/src/model_normal_map.slf b/libraries/render-utils/src/model_normal_map.slf index 5fbc81e35b..695fadbca8 100644 --- a/libraries/render-utils/src/model_normal_map.slf +++ b/libraries/render-utils/src/model_normal_map.slf @@ -32,7 +32,7 @@ void main(void) { <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> float opacity = 1.0; - <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)&>; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; <$discardTransparent(opacity)$>; vec3 albedo = getMaterialAlbedo(mat); diff --git a/libraries/render-utils/src/model_shadow.slf b/libraries/render-utils/src/model_shadow.slf index 862fcd0cf6..956ece4923 100644 --- a/libraries/render-utils/src/model_shadow.slf +++ b/libraries/render-utils/src/model_shadow.slf @@ -9,10 +9,28 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +<@include graphics/Material.slh@> +<@include graphics/MaterialTextures.slh@> +<@include render-utils/ShaderConstants.h@> + +<$declareMaterialTextures(ALBEDO, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL)$> + +layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; +#define _texCoord0 _texCoord01.xy layout(location=0) out vec4 _fragColor; void main(void) { + Material mat = getMaterial(); + BITFIELD matKey = getMaterialKey(mat); + + if ((matKey & OPACITY_MASK_MAP_BIT) != 0) { + float opacity = 1.0; + <$fetchMaterialAlbedoTextureCoord0(matKey, _texCoord0, albedoTex)$> + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + } + // pass-through to set z-buffer _fragColor = vec4(1.0, 1.0, 1.0, 0.0); } diff --git a/libraries/render-utils/src/model_shadow.slv b/libraries/render-utils/src/model_shadow.slv index 8de77d7b1d..d455ea4ade 100644 --- a/libraries/render-utils/src/model_shadow.slv +++ b/libraries/render-utils/src/model_shadow.slv @@ -13,12 +13,16 @@ <@include gpu/Inputs.slh@> <@include gpu/Transform.slh@> +<@include graphics/Material.slh@> +<@include graphics/MaterialTextures.slh@> <$declareStandardTransform()$> +<$declareMaterialTexMapArrayBuffer()$> <@include render-utils/ShaderConstants.h@> layout(location=RENDER_UTILS_ATTR_POSITION_WS) out vec4 _positionWS; +layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; void main(void) { // standard transform @@ -26,4 +30,13 @@ void main(void) { TransformObject obj = getTransformObject(); <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> <$transformModelToWorldPos(obj, inPosition, _positionWS)$> + + Material mat = getMaterial(); + BITFIELD matKey = getMaterialKey(mat); + _texCoord01 = vec4(0.0, 0.0, 0.0, 0.0); + // If we have an opacity mask than we need the first tex coord + if ((matKey & OPACITY_MASK_MAP_BIT) != 0) { + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _positionWS, _texCoord01.xy)$> + } } From 54d7bde91a5524217bbebaa9ea0644ad3b544cdc Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 17 Apr 2019 10:56:55 -0700 Subject: [PATCH 36/85] grid ignorePickIntersection --- scripts/system/libraries/gridTool.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/libraries/gridTool.js b/scripts/system/libraries/gridTool.js index 1fd3cbfbaa..233ae3a3e4 100644 --- a/scripts/system/libraries/gridTool.js +++ b/scripts/system/libraries/gridTool.js @@ -25,7 +25,8 @@ Grid = function() { color: gridColor, alpha: gridAlpha, minorGridEvery: minorGridEvery, - majorGridEvery: majorGridEvery + majorGridEvery: majorGridEvery, + ignorePickIntersection: true }); that.visible = false; From e7d12dc4f88accac9da92bd3bec6fd1cc38fce7d Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 17 Apr 2019 15:43:05 -0700 Subject: [PATCH 37/85] fix snapshots and preview --- interface/src/Application.cpp | 22 ++++-- interface/src/Application.h | 4 + interface/src/graphics/GraphicsEngine.cpp | 4 +- interface/src/ui/Snapshot.cpp | 74 ++++++++++--------- interface/src/ui/Snapshot.h | 2 + interface/src/ui/SnapshotAnimated.cpp | 30 ++++---- interface/src/ui/SnapshotAnimated.h | 3 +- .../src/display-plugins/NullDisplayPlugin.cpp | 10 +-- .../src/display-plugins/NullDisplayPlugin.h | 2 - .../display-plugins/OpenGLDisplayPlugin.cpp | 23 +++--- .../src/display-plugins/OpenGLDisplayPlugin.h | 5 +- .../display-plugins/hmd/HmdDisplayPlugin.cpp | 2 +- libraries/gpu/src/gpu/Frame.h | 1 + libraries/plugins/src/plugins/DisplayPlugin.h | 4 - 14 files changed, 101 insertions(+), 85 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bc0feefb9a..7ad077151e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8428,6 +8428,17 @@ bool Application::takeSnapshotOperators(std::queue& snapshotOp return !snapshotOperators.empty(); } +void Application::addSecondarySnapshotOperator(const SecondarySnapshotOperator& snapshotOperator) { + std::lock_guard lock(_snapshotMutex); + _secondarySnapshotOperators.push(snapshotOperator); +} + +bool Application::takeSecondarySnapshotOperators(std::queue& snapshotOperators) { + std::lock_guard lock(_snapshotMutex); + _secondarySnapshotOperators.swap(snapshotOperators); + return !snapshotOperators.empty(); +} + void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { addSnapshotOperator({ [notify, includeAnimated, aspectRatio, filename](const QImage& snapshot) { QString path = DependencyManager::get()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation()); @@ -8439,16 +8450,17 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa emit DependencyManager::get()->stillSnapshotTaken(path, notify); } } else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) { - // Get an animated GIF snapshot and save it - SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get()); + qApp->postLambdaEvent([path, aspectRatio] { + // Get an animated GIF snapshot and save it + SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, DependencyManager::get()); + }); } }, aspectRatio }); } void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) { - postLambdaEvent([notify, filename, this] { - QString snapshotPath = DependencyManager::get()->saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, - TestScriptingInterface::getInstance()->getTestResultsLocation()); + addSecondarySnapshotOperator([notify, filename](const QImage& snapshot) { + QString snapshotPath = DependencyManager::get()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation()); emit DependencyManager::get()->stillSnapshotTaken(snapshotPath, notify); }); diff --git a/interface/src/Application.h b/interface/src/Application.h index b2e59e97d4..0004add3b1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -345,8 +345,11 @@ public: #endif using SnapshotOperator = std::pair, float>; + using SecondarySnapshotOperator = std::function; void addSnapshotOperator(const SnapshotOperator& snapshotOperator); bool takeSnapshotOperators(std::queue& snapshotOperators); + void addSecondarySnapshotOperator(const SecondarySnapshotOperator& snapshotOperator); + bool takeSecondarySnapshotOperators(std::queue& snapshotOperators); signals: void svoImportRequested(const QString& url); @@ -794,6 +797,7 @@ private: SharedSoundPointer _sampleSound; std::mutex _snapshotMutex; std::queue _snapshotOperators; + std::queue _secondarySnapshotOperators; DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin; QString _autoSwitchDisplayModeSupportedHMDPluginName; diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp index 1842c20e4e..09471a9570 100644 --- a/interface/src/graphics/GraphicsEngine.cpp +++ b/interface/src/graphics/GraphicsEngine.cpp @@ -245,6 +245,7 @@ void GraphicsEngine::render_performFrame() { } std::queue snapshotOperators; + std::queue secondarySnapshotOperators; if (!_programsCompiled.load()) { gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) { batch.setFramebuffer(finalFramebuffer); @@ -273,12 +274,12 @@ void GraphicsEngine::render_performFrame() { renderArgs._hudOperator = displayPlugin->getHUDOperator(); renderArgs._hudTexture = qApp->getApplicationOverlay().getOverlayTexture(); renderArgs._takingSnapshot = qApp->takeSnapshotOperators(snapshotOperators); + qApp->takeSecondarySnapshotOperators(secondarySnapshotOperators); renderArgs._blitFramebuffer = finalFramebuffer; render_runRenderFrame(&renderArgs); } } - qDebug() << "boop" << renderArgs._takingSnapshot << snapshotOperators.size(); auto frame = getGPUContext()->endFrame(); frame->frameIndex = _renderFrameCount; frame->framebuffer = finalFramebuffer; @@ -289,6 +290,7 @@ void GraphicsEngine::render_performFrame() { } }; frame->snapshotOperators = snapshotOperators; + frame->secondarySnapshotOperators = secondarySnapshotOperators; // deliver final scene rendering commands to the display plugin { PROFILE_RANGE(render, "/pluginOutput"); diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 60c039ce1f..94523fa6dd 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -159,47 +159,57 @@ void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, secondaryCameraRenderConfig->setOrientation(CAMERA_ORIENTATION_DOWN); _snapshotIndex = 0; + _taking360Snapshot = true; _snapshotTimer.start(SNAPSHOT_360_TIMER_INTERVAL); } void Snapshot::takeNextSnapshot() { - SecondaryCameraJobConfig* config = - static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); + if (_taking360Snapshot) { + if (!_waitingOnSnapshot) { + _waitingOnSnapshot = true; + qApp->addSecondarySnapshotOperator([this](const QImage& snapshot) { + // Order is: + // 0. Down + // 1. Front + // 2. Left + // 3. Back + // 4. Right + // 5. Up + if (_snapshotIndex < 6) { + _imageArray[_snapshotIndex] = snapshot; + } - // Order is: - // 0. Down - // 1. Front - // 2. Left - // 3. Back - // 4. Right - // 5. Up - if (_snapshotIndex < 6) { - _imageArray[_snapshotIndex] = qApp->getActiveDisplayPlugin()->getSecondaryCameraScreenshot(); - } + SecondaryCameraJobConfig* config = static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); + if (_snapshotIndex == 0) { + // Setup for Front Image capture + config->setOrientation(CAMERA_ORIENTATION_FRONT); + } else if (_snapshotIndex == 1) { + // Setup for Left Image capture + config->setOrientation(CAMERA_ORIENTATION_LEFT); + } else if (_snapshotIndex == 2) { + // Setup for Back Image capture + config->setOrientation(CAMERA_ORIENTATION_BACK); + } else if (_snapshotIndex == 3) { + // Setup for Right Image capture + config->setOrientation(CAMERA_ORIENTATION_RIGHT); + } else if (_snapshotIndex == 4) { + // Setup for Up Image capture + config->setOrientation(CAMERA_ORIENTATION_UP); + } else if (_snapshotIndex == 5) { + _taking360Snapshot = false; + } - if (_snapshotIndex == 0) { - // Setup for Front Image capture - config->setOrientation(CAMERA_ORIENTATION_FRONT); - } else if (_snapshotIndex == 1) { - // Setup for Left Image capture - config->setOrientation(CAMERA_ORIENTATION_LEFT); - } else if (_snapshotIndex == 2) { - // Setup for Back Image capture - config->setOrientation(CAMERA_ORIENTATION_BACK); - } else if (_snapshotIndex == 3) { - // Setup for Right Image capture - config->setOrientation(CAMERA_ORIENTATION_RIGHT); - } else if (_snapshotIndex == 4) { - // Setup for Up Image capture - config->setOrientation(CAMERA_ORIENTATION_UP); - } else if (_snapshotIndex > 5) { + _waitingOnSnapshot = false; + _snapshotIndex++; + }); + } + } else { _snapshotTimer.stop(); // Reset secondary camera render config - static_cast( - qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping")) - ->setCurve(1); + SecondaryCameraJobConfig* config = static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); + static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))->setCurve(1); config->resetSizeSpectatorCamera(qApp->getWindow()->geometry().width(), qApp->getWindow()->geometry().height()); config->setProperty("attachedEntityId", _oldAttachedEntityId); config->setProperty("vFoV", _oldvFoV); @@ -217,8 +227,6 @@ void Snapshot::takeNextSnapshot() { QtConcurrent::run([this]() { convertToEquirectangular(); }); } } - - _snapshotIndex++; } void Snapshot::convertToCubemap() { diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 77bdfd4ac1..f13f4cd587 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -97,6 +97,8 @@ private: bool _cubemapOutputFormat; QTimer _snapshotTimer; qint16 _snapshotIndex; + bool _waitingOnSnapshot { false }; + bool _taking360Snapshot { false }; bool _oldEnabled; QVariant _oldAttachedEntityId; QVariant _oldOrientation; diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index 1c5a25b68f..03ec86e342 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -27,7 +27,6 @@ QString SnapshotAnimated::snapshotAnimatedPath; QString SnapshotAnimated::snapshotStillPath; QVector SnapshotAnimated::snapshotAnimatedFrameVector; QVector SnapshotAnimated::snapshotAnimatedFrameDelayVector; -Application* SnapshotAnimated::app; float SnapshotAnimated::aspectRatio; QSharedPointer SnapshotAnimated::snapshotAnimatedDM; GifWriter SnapshotAnimated::snapshotAnimatedGifWriter; @@ -36,12 +35,11 @@ GifWriter SnapshotAnimated::snapshotAnimatedGifWriter; Setting::Handle SnapshotAnimated::alsoTakeAnimatedSnapshot("alsoTakeAnimatedSnapshot", true); Setting::Handle SnapshotAnimated::snapshotAnimatedDuration("snapshotAnimatedDuration", SNAPSNOT_ANIMATED_DURATION_SECS); -void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer dm) { +void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, QSharedPointer dm) { // If we're not in the middle of capturing an animated snapshot... if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) { SnapshotAnimated::snapshotAnimatedTimer = new QTimer(); SnapshotAnimated::aspectRatio = aspectRatio; - SnapshotAnimated::app = app; SnapshotAnimated::snapshotAnimatedDM = dm; // Define the output location of the still and animated snapshots. SnapshotAnimated::snapshotStillPath = pathStill; @@ -62,7 +60,7 @@ void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio void SnapshotAnimated::captureFrames() { if (SnapshotAnimated::snapshotAnimatedTimerRunning) { - SnapshotAnimated::app->addSnapshotOperator({ [](const QImage& snapshot) { + qApp->addSnapshotOperator({ [](const QImage& snapshot) { // Get a screenshot from the display, then scale the screenshot down, // then convert it to the image format the GIF library needs, // then save all that to the QImage named "frame" @@ -86,21 +84,21 @@ void SnapshotAnimated::captureFrames() { // If that was the last frame... if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) { SnapshotAnimated::snapshotAnimatedTimerRunning = false; - - // Notify the user that we're processing the snapshot - // This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called. - emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath); - - // Kick off the thread that'll pack the frames into the GIF - QtConcurrent::run(processFrames); - // Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE - // that the slot will not be called again in the future. - // See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html - SnapshotAnimated::snapshotAnimatedTimer->stop(); - delete SnapshotAnimated::snapshotAnimatedTimer; } } }, SnapshotAnimated::aspectRatio }); + } else { + // Notify the user that we're processing the snapshot + // This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called. + emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath); + + // Kick off the thread that'll pack the frames into the GIF + QtConcurrent::run(processFrames); + // Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE + // that the slot will not be called again in the future. + // See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html + SnapshotAnimated::snapshotAnimatedTimer->stop(); + delete SnapshotAnimated::snapshotAnimatedTimer; } } diff --git a/interface/src/ui/SnapshotAnimated.h b/interface/src/ui/SnapshotAnimated.h index 87ce533fc3..15734f57c8 100644 --- a/interface/src/ui/SnapshotAnimated.h +++ b/interface/src/ui/SnapshotAnimated.h @@ -42,7 +42,6 @@ private: static QVector snapshotAnimatedFrameVector; static QVector snapshotAnimatedFrameDelayVector; static QSharedPointer snapshotAnimatedDM; - static Application* app; static float aspectRatio; static GifWriter snapshotAnimatedGifWriter; @@ -51,7 +50,7 @@ private: static void processFrames(); static void clearTempVariables(); public: - static void saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer dm); + static void saveSnapshotAnimated(QString pathStill, float aspectRatio, QSharedPointer dm); static bool isAlreadyTakingSnapshotAnimated() { return snapshotAnimatedFirstFrameTimestamp != 0; }; static Setting::Handle alsoTakeAnimatedSnapshot; static Setting::Handle snapshotAnimatedDuration; diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index 47a213cf71..fcd695bed8 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -25,12 +25,4 @@ void NullDisplayPlugin::submitFrame(const gpu::FramePointer& frame) { if (frame) { _gpuContext->consumeFrameUpdates(frame); } -} - -QImage NullDisplayPlugin::getScreenshot(float aspectRatio) const { - return QImage(); -} - -QImage NullDisplayPlugin::getSecondaryCameraScreenshot() const { - return QImage(); -} +} \ No newline at end of file diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index e4ff1b8b37..7aa763bab9 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -17,8 +17,6 @@ public: glm::uvec2 getRecommendedRenderSize() const override; void submitFrame(const gpu::FramePointer& newFrame) override; - QImage getScreenshot(float aspectRatio = 0.0f) const override; - QImage getSecondaryCameraScreenshot() const override; void copyTextureToQuickFramebuffer(NetworkTexturePointer source, QOpenGLFramebufferObject* target, GLsync* fenceSync) override {}; void pluginUpdate() override {}; private: diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 1700ca9d54..f9b6deb846 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -732,6 +732,15 @@ void OpenGLDisplayPlugin::present() { } } + { // If we have any secondary camera snapshots this frame, handle them + PROFILE_RANGE_EX(render, "secondarySnapshotOperators", 0x00ff00ff, frameId) + while (!_currentFrame->secondarySnapshotOperators.empty()) { + auto& snapshotOperator = _currentFrame->secondarySnapshotOperators.front(); + snapshotOperator(getSecondaryCameraScreenshot()); + _currentFrame->secondarySnapshotOperators.pop(); + } + } + // Take the composite framebuffer and send it to the output device { PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId) @@ -796,7 +805,7 @@ bool OpenGLDisplayPlugin::setDisplayTexture(const QString& name) { return !!_displayTexture; } -QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { +QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) { auto size = _compositeFramebuffer->getSize(); if (isHmd()) { size.x /= 2; @@ -812,24 +821,18 @@ QImage OpenGLDisplayPlugin::getScreenshot(float aspectRatio) const { corner.x = round((size.x - bestSize.x) / 2.0f); corner.y = round((size.y - bestSize.y) / 2.0f); } - auto glBackend = const_cast(*this).getGLBackend(); QImage screenshot(bestSize.x, bestSize.y, QImage::Format_ARGB32); - withOtherThreadContext([&] { - glBackend->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot); - }); + getGLBackend()->downloadFramebuffer(_compositeFramebuffer, ivec4(corner, bestSize), screenshot); return screenshot.mirrored(false, true); } -QImage OpenGLDisplayPlugin::getSecondaryCameraScreenshot() const { +QImage OpenGLDisplayPlugin::getSecondaryCameraScreenshot() { auto textureCache = DependencyManager::get(); auto secondaryCameraFramebuffer = textureCache->getSpectatorCameraFramebuffer(); gpu::Vec4i region(0, 0, secondaryCameraFramebuffer->getWidth(), secondaryCameraFramebuffer->getHeight()); - auto glBackend = const_cast(*this).getGLBackend(); QImage screenshot(region.z, region.w, QImage::Format_ARGB32); - withOtherThreadContext([&] { - glBackend->downloadFramebuffer(secondaryCameraFramebuffer, region, screenshot); - }); + getGLBackend()->downloadFramebuffer(secondaryCameraFramebuffer, region, screenshot); return screenshot.mirrored(false, true); } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 49a38ecb4c..e56804c151 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -60,8 +60,6 @@ public: virtual bool setDisplayTexture(const QString& name) override; virtual bool onDisplayTextureReset() { return false; }; - QImage getScreenshot(float aspectRatio = 0.0f) const override; - QImage getSecondaryCameraScreenshot() const override; float presentRate() const override; @@ -185,5 +183,8 @@ protected: // be serialized through this mutex mutable Mutex _presentMutex; float _hudAlpha{ 1.0f }; + + QImage getScreenshot(float aspectRatio); + QImage getSecondaryCameraScreenshot(); }; diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 321bcc3fd2..874454b391 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -199,7 +199,7 @@ void HmdDisplayPlugin::internalPresent() { float newWidth = sourceSize.x - shiftLeftBy; // Experimentally adjusted the region presented in preview to avoid seeing the masked pixels and recenter the center... - static float SCALE_WIDTH = 0.9f; + static float SCALE_WIDTH = 0.8f; static float SCALE_OFFSET = 2.0f; newWidth *= SCALE_WIDTH; shiftLeftBy *= SCALE_OFFSET; diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index 94c0919fdb..541b9393d3 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -43,6 +43,7 @@ namespace gpu { FramebufferRecycler framebufferRecycler; std::queue, float>> snapshotOperators; + std::queue> secondarySnapshotOperators; protected: friend class Deserializer; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 9db8fc995c..c88d76e661 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -173,10 +173,6 @@ public: return QRect(0, 0, recommendedSize.x, recommendedSize.y); } - // Fetch the most recently displayed image as a QImage - virtual QImage getScreenshot(float aspectRatio = 0.0f) const = 0; - virtual QImage getSecondaryCameraScreenshot() const = 0; - // will query the underlying hmd api to compute the most recent head pose virtual bool beginFrameRender(uint32_t frameIndex) { return true; } From 9873c10353486c861dd1c484e9a4b6e63655cc4b Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Wed, 17 Apr 2019 17:58:48 -0700 Subject: [PATCH 38/85] Better canvas ICon qml and activation of the jobs --- .../utilities/lib/jet/qml/TaskPropView.qml | 33 +++++--- .../utilities/lib/prop/PropGroup.qml | 9 +-- .../utilities/lib/prop/style/PiCanvasIcon.qml | 80 +++++++++++++++---- 3 files changed, 85 insertions(+), 37 deletions(-) diff --git a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml index 2f7ee71a5f..e53932a9f9 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml @@ -32,31 +32,40 @@ Prop.PropGroup { property var showSubs: true property var jobEnabled: true + property var toggleJobActivation: function() { + console.log("the button has been pressed and jobEnabled is " + jobEnabled ) + jobEnabled = !jobEnabled; + rootConfig.getConfig(jobPath).enabled = jobEnabled; + } + // Panel Header Data Component panelHeaderData: Component { Item { id: header Prop.PropLabel { text: root.label - horizontalAlignment: Text.AlignHCenter + //horizontalAlignment: Text.AlignHCenter anchors.left: parent.left anchors.right: enabledIcon.left anchors.verticalCenter: parent.verticalCenter - } + } Prop.PropCanvasIcon { id: enabledIcon + anchors.right: enabledIcon2.left + anchors.verticalCenter: parent.verticalCenter + filled: root.jobEnabled + fillColor: (root.jobEnabled ? global.colorGreenHighlight : global.colorOrangeAccent) + icon: 5 + iconMouseArea.onClicked: { toggleJobActivation() } + } + Prop.PropCanvasIcon { + id: enabledIcon2 anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - fillColor: global.colorOrangeAccent - filled: jobEnabled - - MouseArea{ - id: mousearea - anchors.fill: parent - onClicked: { - root.jobEnabled = !root.jobEnabled - } - } + filled: root.jobEnabled + fillColor: (root.jobEnabled ? global.colorGreenHighlight : global.colorOrangeAccent) + icon: 7 + iconMouseArea.onClicked: { toggleJobActivation() } } } } diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 6836e85e25..38f19389aa 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -63,14 +63,7 @@ Item { anchors.verticalCenter: parent.verticalCenter fillColor: global.colorOrangeAccent filled: root.propItemsPanel.height > 4 - - MouseArea{ - id: mousearea - anchors.fill: parent - onClicked: { - root.isUnfold = !root.isUnfold - } - } + iconMouseArea.onClicked: { root.isUnfold = !root.isUnfold } } } diff --git a/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml b/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml index b8e80f1c3b..3feed342b8 100644 --- a/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml +++ b/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml @@ -18,39 +18,85 @@ Canvas { height: global.iconHeight property var icon: 0 + + onIconChanged: function () { //console.log("Icon changed to: " + icon ); + requestPaint() + } + property var filled: true - onIconChanged: function () { requestPaint() } + onFilledChanged: function () { //console.log("Filled changed to: " + filled ); + requestPaint() + } + property var fillColor: global.colorBorderHighight + onFillColorChanged: function () { //console.log("fillColor changed to: " + filled ); + requestPaint() + } + + property alias iconMouseArea: mousearea contextType: "2d" onPaint: { context.reset(); switch (icon) { - case 0: - case 1: - case 2: - default: { - if ((icon % 3) == 0) { + case false: + case 0:{ // Right Arrow context.moveTo(width * 0.25, 0); context.lineTo(width * 0.25, height); context.lineTo(width, height / 2); - } else if ((icon % 3) == 1) { + context.closePath(); + } break; + case true: + case 1:{ // Up Arrow context.moveTo(0, height * 0.25); context.lineTo(width, height * 0.25); context.lineTo(width / 2, height); - } else { + context.closePath(); + } break; + case 2:{ // Down Arrow context.moveTo(0, height * 0.75); context.lineTo(width, height* 0.75); context.lineTo(width / 2, 0); + context.closePath(); + } break; + case 3:{ // Left Arrow + context.moveTo(width * 0.75, 0); + context.lineTo(width * 0.75, height); + context.lineTo(0, height / 2); + context.closePath(); + } break; + case 4:{ // Square + context.moveTo(0,0); + context.lineTo(0, height); + context.lineTo(width, height); + context.lineTo(width, 0); + context.closePath(); + } break; + case 5:{ // Small Square + context.moveTo(width * 0.25, height * 0.25); + context.lineTo(width * 0.25, height * 0.75); + context.lineTo(width * 0.75, height * 0.75); + context.lineTo(width * 0.75, height * 0.25); + context.closePath(); + } break; + default: {// Down Arrow + /* context.moveTo(0, height * 0.25); + context.lineTo(width, height * 0.25); + context.lineTo(width / 2, height); + context.closePath();*/ } - context.closePath(); - if (filled) { - context.fillStyle = fillColor; - context.fill(); - } else { - context.strokeStyle = fillColor; - context.stroke(); - } - }} + } + if (filled) { + context.fillStyle = fillColor; + context.fill(); + } else { + context.strokeStyle = fillColor; + context.stroke(); + } + } + + MouseArea{ + id: mousearea + anchors.fill: parent } } \ No newline at end of file From 3b1751f2c98072c4de2dfd50b3b9ef4c194149b5 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 17 Apr 2019 20:18:17 -0700 Subject: [PATCH 39/85] adding proper logic for shield icon behavior --- interface/resources/qml/BubbleIcon.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/resources/qml/BubbleIcon.qml b/interface/resources/qml/BubbleIcon.qml index f4e99f136c..b5a7ddb2d8 100644 --- a/interface/resources/qml/BubbleIcon.qml +++ b/interface/resources/qml/BubbleIcon.qml @@ -23,15 +23,15 @@ Rectangle { property bool ignoreRadiusEnabled: AvatarInputs.ignoreRadiusEnabled; function updateOpacity() { - if (ignoreRadiusEnabled) { - bubbleRect.opacity = 1.0; - } else { - bubbleRect.opacity = 0.7; - } + var rectOpacity = ignoreRadiusEnabled ? 1.0 : (mouseArea.containsMouse ? 1.0 : 0.7); + bubbleRect.opacity = rectOpacity; } Component.onCompleted: { updateOpacity(); + AvatarInputs.ignoreRadiusEnabledChanged.connect(function() { + ignoreRadiusEnabled = AvatarInputs.ignoreRadiusEnabled; + }); } onIgnoreRadiusEnabledChanged: { @@ -74,10 +74,10 @@ Rectangle { } drag.target: dragTarget; onContainsMouseChanged: { - var rectOpacity = (ignoreRadiusEnabled && containsMouse) ? 1.0 : (containsMouse ? 1.0 : 0.7); if (containsMouse) { Tablet.playSound(TabletEnums.ButtonHover); } + var rectOpacity = ignoreRadiusEnabled ? 1.0 : (mouseArea.containsMouse ? 1.0 : 0.7); bubbleRect.opacity = rectOpacity; } } From 3d78592bb84ddc417bfde7f8acb83e98dd32eaed Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 18 Apr 2019 00:19:19 -0700 Subject: [PATCH 40/85] Adding more style widgets andbringing luci1 into the panels of luci2 --- .../developer/utilities/lib/prop/PropBool.qml | 4 +- .../utilities/lib/prop/PropGroup.qml | 30 +++- scripts/developer/utilities/lib/prop/qmldir | 2 + .../utilities/lib/prop/style/PiCheckBox.qml | 25 ++++ .../lib/prop/style/PiFolderPanel.qml | 133 ++++++++++++++++++ .../utilities/render/deferredLighting.qml | 71 +--------- scripts/developer/utilities/render/luci.qml | 32 ++--- .../utilities/render/luci/ShadingModel.qml | 105 ++++++++++++++ .../developer/utilities/render/luci/qmldir | 2 + 9 files changed, 306 insertions(+), 98 deletions(-) create mode 100644 scripts/developer/utilities/lib/prop/style/PiCheckBox.qml create mode 100644 scripts/developer/utilities/lib/prop/style/PiFolderPanel.qml create mode 100644 scripts/developer/utilities/render/luci/ShadingModel.qml create mode 100644 scripts/developer/utilities/render/luci/qmldir diff --git a/scripts/developer/utilities/lib/prop/PropBool.qml b/scripts/developer/utilities/lib/prop/PropBool.qml index d8e4bad589..1d50ec7dd3 100644 --- a/scripts/developer/utilities/lib/prop/PropBool.qml +++ b/scripts/developer/utilities/lib/prop/PropBool.qml @@ -9,7 +9,6 @@ // import QtQuick 2.7 -import controlsUit 1.0 as HifiControls PropItem { Global { id: global } @@ -21,13 +20,12 @@ PropItem { valueVar = root.valueVarGetter(); } - HifiControls.CheckBox { + PropCheckBox { id: checkboxControl anchors.left: root.splitter.right anchors.verticalCenter: root.verticalCenter width: root.width * global.valueAreaWidthScale - height: global.slimHeight onCheckedChanged: { root.valueVarSetter(checked); } } diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 38f19389aa..2f15e60a0c 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -10,19 +10,35 @@ import QtQuick 2.7 -Item { +//Item { +PropFolderPanel { Global { id: global } id: root - property var label: "group" + // property var label: "group" - property alias isUnfold: headerFolderIcon.icon - property var indentDepth: 0 + // property alias isUnfold: headerFolderIcon.icon + // property var indentDepth: 0 - property alias propItemsPanel: propItemsContainer + //property alias propItemsPanel: _panelFrameData + // property alias propItemsPanel: propItemsContainer + //property var propItemsPanel: propItemsContainer + + panelFrameData: Component { // default is a column + id: groupPanelFrameData + Column { + id: propItemsContainer + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 0 + anchors.rightMargin: 0 + + clip: true + } + } // Panel Header Data Component - property Component panelHeaderData: defaultPanelHeaderData +/* property Component panelHeaderData: defaultPanelHeaderData Component { // default is a Label id: defaultPanelHeaderData PropLabel { @@ -110,7 +126,7 @@ Item { anchors.margins: 0 anchors.left: parent.left anchors.right: parent.right - +*/ // Prop Group is designed to author an array of ProItems, they are defined with an array of the tuplets describing each individual item: // [ ..., PropItemInfo, ...] diff --git a/scripts/developer/utilities/lib/prop/qmldir b/scripts/developer/utilities/lib/prop/qmldir index 2cd06d58ac..c67ab6a41a 100644 --- a/scripts/developer/utilities/lib/prop/qmldir +++ b/scripts/developer/utilities/lib/prop/qmldir @@ -5,6 +5,8 @@ PropLabel 1.0 style/PiLabel.qml PropSplitter 1.0 style/PiSplitter.qml PropComboBox 1.0 style/PiComboBox.qml PropCanvasIcon 1.0 style/PiCanvasIcon.qml +PropCheckBox 1.0 style/PiCheckBox.qml +PropFolderPanel 1.0 style/PiFolderPanel.qml PropItem 1.0 PropItem.qml PropScalar 1.0 PropScalar.qml diff --git a/scripts/developer/utilities/lib/prop/style/PiCheckBox.qml b/scripts/developer/utilities/lib/prop/style/PiCheckBox.qml new file mode 100644 index 0000000000..108a763be3 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/style/PiCheckBox.qml @@ -0,0 +1,25 @@ +// +// Prop/style/PiCheckBox.qml +// +// Created by Sam Gateau on 3/2/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import controlsUit 1.0 as HifiControls + +HifiControls.CheckBox { + Global { id: global } + + color: global.fontColor + + //anchors.left: root.splitter.right + //anchors.verticalCenter: root.verticalCenter + //width: root.width * global.valueAreaWidthScale + height: global.slimHeight + + onCheckedChanged: { root.valueVarSetter(checked); } +} \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/style/PiFolderPanel.qml b/scripts/developer/utilities/lib/prop/style/PiFolderPanel.qml new file mode 100644 index 0000000000..b80ab47fc6 --- /dev/null +++ b/scripts/developer/utilities/lib/prop/style/PiFolderPanel.qml @@ -0,0 +1,133 @@ +// +// Prop/style/PiFoldedPanel.qml +// +// Created by Sam Gateau on 4/17/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +Item { + Global { id: global } + id: root + + property var label: "panel" + + property alias isUnfold: headerFolderIcon.icon + property var indentDepth: 0 + + // Panel Header Data Component + property Component panelHeaderData: defaultPanelHeaderData + Component { // default is a Label + id: defaultPanelHeaderData + PiLabel { + text: root.label + horizontalAlignment: Text.AlignHCenter + } + } + + // Panel Frame Data Component + property Component panelFrameData: defaultPanelFrameData + Component { // default is a column + id: defaultPanelFrameData + Column { + } + } + + //property alias panelFrameContent: frame._panelFrameData.data + + // Header Item + Rectangle { + id: header + height: global.slimHeight + anchors.left: parent.left + anchors.right: parent.right + + color: global.colorBackShadow // header of group is darker + + // First in the header, some indentation spacer + Item { + id: indentSpacer + width: (headerFolderIcon.width * root.indentDepth) + global.horizontalMargin // Must be non-zero + height: parent.height + + anchors.verticalCenter: parent.verticalCenter + } + + // Second, the folder button / indicator + Item { + id: headerFolder + anchors.left: indentSpacer.right + width: headerFolderIcon.width * 2 + anchors.verticalCenter: header.verticalCenter + height: parent.height + + PiCanvasIcon { + id: headerFolderIcon + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + fillColor: global.colorOrangeAccent + filled: root.frame.height > 4 + iconMouseArea.onClicked: { root.isUnfold = !root.isUnfold } + } + } + + // Next the header container + // by default containing a Label showing the root.label + Loader { + sourceComponent: panelHeaderData + anchors.left: headerFolder.right + anchors.right: header.right + anchors.verticalCenter: header.verticalCenter + height: parent.height + } + } + + // The Panel container + Rectangle { + id: frame + visible: root.isUnfold + + color: "transparent" + border.color: global.colorBorderLight + border.width: global.valueBorderWidth + radius: global.valueBorderRadius + + anchors.margins: 0 + anchors.left: parent.left + anchors.right: parent.right + anchors.top: header.bottom + anchors.bottom: root.bottom + + // Next the panel frame data + Loader { + sourceComponent: panelFrameData + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 0 + anchors.rightMargin: 0 + id: _panelFrameData + clip: true + } + + /* Column { + id: propItemsContainer + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 0 + anchors.rightMargin: 0 + + clip: true + + // Where the propItems are added + }*/ + } + + height: header.height + isUnfold * _panelFrameData.height + anchors.margins: 0 + anchors.left: parent.left + anchors.right: parent.right +} \ No newline at end of file diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index d147585212..63c2af1b0f 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -14,6 +14,7 @@ import QtQuick.Layouts 1.3 import stylesUit 1.0 import controlsUit 1.0 as HifiControls import "configSlider" +import "luci" Rectangle { HifiConstants { id: hifi;} @@ -32,76 +33,8 @@ Rectangle { HifiControls.Label { text: "Shading" } - Row { - anchors.left: parent.left - anchors.right: parent.right - - spacing: 5 - Column { - spacing: 5 - // padding: 10 - Repeater { - model: [ - "Unlit:LightingModel:enableUnlit", - "Emissive:LightingModel:enableEmissive", - "Lightmap:LightingModel:enableLightmap", - "Background:LightingModel:enableBackground", - "Haze:LightingModel:enableHaze", - "AO:LightingModel:enableAmbientOcclusion", - "Textures:LightingModel:enableMaterialTexturing" - ] - HifiControls.CheckBox { - boxSize: 20 - text: modelData.split(":")[0] - checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } - } - } - } + ShadingModel { - - Column { - spacing: 5 - Repeater { - model: [ - "Obscurance:LightingModel:enableObscurance", - "Scattering:LightingModel:enableScattering", - "Diffuse:LightingModel:enableDiffuse", - "Specular:LightingModel:enableSpecular", - "Albedo:LightingModel:enableAlbedo", - "Wireframe:LightingModel:enableWireframe", - "Skinning:LightingModel:enableSkinning", - "Blendshape:LightingModel:enableBlendshape" - ] - HifiControls.CheckBox { - boxSize: 20 - text: modelData.split(":")[0] - checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } - } - } - } - - Column { - spacing: 5 - Repeater { - model: [ - "Ambient:LightingModel:enableAmbientLight", - "Directional:LightingModel:enableDirectionalLight", - "Point:LightingModel:enablePointLight", - "Spot:LightingModel:enableSpotLight", - "Light Contour:LightingModel:showLightContour", - "Zone Stack:DrawZoneStack:enabled", - "Shadow:LightingModel:enableShadow" - ] - HifiControls.CheckBox { - boxSize: 20 - text: modelData.split(":")[0] - checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } - } - } - } } Separator {} Column { diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml index 8c68d654a1..7bce087fda 100644 --- a/scripts/developer/utilities/render/luci.qml +++ b/scripts/developer/utilities/render/luci.qml @@ -15,6 +15,7 @@ import controlsUit 1.0 as HifiControls import "../lib/prop" as Prop import "../lib/jet/qml" as Jet +import "luci" Rectangle { anchors.fill: parent @@ -31,7 +32,14 @@ Rectangle { Column { width: render.width - + Prop.PropFolderPanel { + id: "shadingModel" + label: "Shading Model" + panelFrameData: Component { + ShadingModel { + } + } + } /* Prop.PropEnum { label: "Tone Curve" object: render.mainViewTask.getConfig("ToneMapping") @@ -45,14 +53,6 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right } */ - Jet.TaskPropView { - id: "lightingModel" - jobPath: "RenderMainView.LightingModel" - label: "Le LightingModel" - - anchors.left: parent.left - anchors.right: parent.right - } Jet.TaskPropView { id: "theView" jobPath: "RenderMainView" @@ -69,20 +69,14 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right } - /* Jet.TaskPropView { - jobPath: "RenderMainView.ToneMapping" - label: "Le ToneMapping Job" + Jet.TaskPropView { + id: "le" + jobPath: "" + label: "Le Render Engine" anchors.left: parent.left anchors.right: parent.right } - Jet.TaskPropView { - jobPath: "RenderMainView.Antialiasing" - label: "Le Antialiasing Job" - - anchors.left: parent.left - anchors.right: parent.right - }*/ } } diff --git a/scripts/developer/utilities/render/luci/ShadingModel.qml b/scripts/developer/utilities/render/luci/ShadingModel.qml new file mode 100644 index 0000000000..2c16492e80 --- /dev/null +++ b/scripts/developer/utilities/render/luci/ShadingModel.qml @@ -0,0 +1,105 @@ +// +// ShadingModel.qml +// +// Created by Sam Gateau on 4/17/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls + +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 + +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls + +import "../../lib/prop" as Prop + + +Column { + + id: shadingModel; + + property var mainViewTask: Render.getConfig("RenderMainView") + + spacing: 5 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x + HifiControls.Label { + text: "Shading" + } + Row { + anchors.left: parent.left + anchors.right: parent.right + + spacing: 5 + Column { + spacing: 5 + Repeater { + model: [ + "Unlit:LightingModel:enableUnlit", + "Emissive:LightingModel:enableEmissive", + "Lightmap:LightingModel:enableLightmap", + "Background:LightingModel:enableBackground", + "Haze:LightingModel:enableHaze", + "AO:LightingModel:enableAmbientOcclusion", + "Textures:LightingModel:enableMaterialTexturing" + ] + Prop.PropCheckBox { + text: modelData.split(":")[0] + checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + } + } + } + + + Column { + spacing: 5 + Repeater { + model: [ + "Obscurance:LightingModel:enableObscurance", + "Scattering:LightingModel:enableScattering", + "Diffuse:LightingModel:enableDiffuse", + "Specular:LightingModel:enableSpecular", + "Albedo:LightingModel:enableAlbedo", + "Wireframe:LightingModel:enableWireframe", + "Skinning:LightingModel:enableSkinning", + "Blendshape:LightingModel:enableBlendshape" + ] + Prop.PropCheckBox { + text: modelData.split(":")[0] + checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + } + } + } + + Column { + spacing: 5 + Repeater { + model: [ + "Ambient:LightingModel:enableAmbientLight", + "Directional:LightingModel:enableDirectionalLight", + "Point:LightingModel:enablePointLight", + "Spot:LightingModel:enableSpotLight", + "Light Contour:LightingModel:showLightContour", + "Zone Stack:DrawZoneStack:enabled", + "Shadow:LightingModel:enableShadow" + ] + Prop.PropCheckBox { + text: modelData.split(":")[0] + checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + } + } + } + } +} diff --git a/scripts/developer/utilities/render/luci/qmldir b/scripts/developer/utilities/render/luci/qmldir new file mode 100644 index 0000000000..cf63b1c2b7 --- /dev/null +++ b/scripts/developer/utilities/render/luci/qmldir @@ -0,0 +1,2 @@ + +ShadingModel 1.0 ShadingModel.qml \ No newline at end of file From 3cb9c518eae445a44c9b5e49f6bd3e7cba38753a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 18 Apr 2019 11:06:12 +0200 Subject: [PATCH 41/85] Exposed max shadow distance and a shadow bias scale parameter in graphics::Light --- libraries/graphics/src/graphics/Light.cpp | 16 +++ libraries/graphics/src/graphics/Light.h | 11 +- libraries/render-utils/src/LightStage.cpp | 105 ++++++++++-------- libraries/render-utils/src/LightStage.h | 6 +- .../render-utils/src/RenderShadowTask.cpp | 8 +- 5 files changed, 87 insertions(+), 59 deletions(-) diff --git a/libraries/graphics/src/graphics/Light.cpp b/libraries/graphics/src/graphics/Light.cpp index 76d8a6030a..8a7281880e 100755 --- a/libraries/graphics/src/graphics/Light.cpp +++ b/libraries/graphics/src/graphics/Light.cpp @@ -73,6 +73,22 @@ bool Light::getCastShadows() const { return _castShadows; } +void Light::setShadowsMaxDistance(const float maxDistance) { + _shadowsMaxDistance = std::max(0.0f, maxDistance); +} + +float Light::getShadowsMaxDistance() const { + return _shadowsMaxDistance; +} + +void Light::setShadowsBiasScale(const float scale) { + _shadowsBiasScale = std::max(0.0f, scale); +} + +float Light::getShadowsBiasScale() const { + return _shadowsBiasScale; +} + void Light::setColor(const Color& color) { _lightSchemaBuffer.edit().irradiance.color = color; updateLightRadius(); diff --git a/libraries/graphics/src/graphics/Light.h b/libraries/graphics/src/graphics/Light.h index bb9fb3e5b9..824a9138c0 100755 --- a/libraries/graphics/src/graphics/Light.h +++ b/libraries/graphics/src/graphics/Light.h @@ -106,6 +106,12 @@ public: void setCastShadows(const bool castShadows); bool getCastShadows() const; + void setShadowsMaxDistance(const float maxDistance); + float getShadowsMaxDistance() const; + + void setShadowsBiasScale(const float scale); + float getShadowsBiasScale() const; + void setOrientation(const Quat& orientation); const glm::quat& getOrientation() const { return _transform.getRotation(); } @@ -192,10 +198,11 @@ protected: Type _type { SUN }; float _spotCos { -1.0f }; // stored here to be able to reset the spot angle when turning the type spot on/off - void updateLightRadius(); - + float _shadowsMaxDistance{ 40.0f }; + float _shadowsBiasScale{ 1.0f }; bool _castShadows{ false }; + void updateLightRadius(); }; typedef std::shared_ptr< Light > LightPointer; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 6913949286..d35bc21526 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -120,7 +120,7 @@ float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFru return far; } -LightStage::Shadow::Shadow(graphics::LightPointer light, float maxDistance, unsigned int cascadeCount) : +LightStage::Shadow::Shadow(graphics::LightPointer light, unsigned int cascadeCount) : _light{ light } { cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); Schema schema; @@ -149,70 +149,77 @@ LightStage::Shadow::Shadow(graphics::LightPointer light, float maxDistance, unsi cascade.framebuffer->setDepthBuffer(map, depthFormat, cascadeIndex); } - setMaxDistance(maxDistance); + if (light) { + setMaxDistance(light->getShadowsMaxDistance()); + } } void LightStage::Shadow::setLight(graphics::LightPointer light) { _light = light; + if (light) { + setMaxDistance(light->getShadowsMaxDistance()); + } } - void LightStage::Shadow::setMaxDistance(float value) { - // This overlaping factor isn't really used directly for blending of shadow cascades. It - // just there to be sure the cascades do overlap. The blending width used is relative - // to the UV space and is set in the Schema with invCascadeBlendWidth. - static const auto OVERLAP_FACTOR = 1.0f / 5.0f; + value = std::max(1e-3f, value); + if (value != _maxDistance) { + // This overlaping factor isn't really used directly for blending of shadow cascades. It's + // just there to be sure the cascades do overlap. The blending width used is relative + // to the UV space and is set in the Schema with invCascadeBlendWidth. + static const auto OVERLAP_FACTOR = 1.0f / 5.0f; - _maxDistance = std::max(0.0f, value); + _maxDistance = value; - if (_cascades.size() == 1) { - _cascades.front().setMinDistance(0.0f); - _cascades.front().setMaxDistance(_maxDistance); - } else { - // Distribute the cascades along that distance - // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? - static const auto LOW_MAX_DISTANCE = 2.0f; - static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions + if (_cascades.size() == 1) { + _cascades.front().setMinDistance(0.0f); + _cascades.front().setMaxDistance(_maxDistance); + } else { + // Distribute the cascades along that distance + // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? + static const auto LOW_MAX_DISTANCE = 2.0f; + static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions - // The max cascade distance is computed by multiplying the previous cascade's max distance by a certain - // factor. There is a "user" factor that is computed from a desired max resolution loss in the shadow - // and an optimal one based on the global min and max shadow distance, all cascades considered. The final - // distance is a gradual blend between the two - const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS); - const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1)); + // The max cascade distance is computed by multiplying the previous cascade's max distance by a certain + // factor. There is a "user" factor that is computed from a desired max resolution loss in the shadow + // and an optimal one based on the global min and max shadow distance, all cascades considered. The final + // distance is a gradual blend between the two + const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS); + const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1)); - float maxCascadeUserDistance = LOW_MAX_DISTANCE; - float maxCascadeOptimalDistance = LOW_MAX_DISTANCE; - float minCascadeDistance = 0.0f; + float maxCascadeUserDistance = LOW_MAX_DISTANCE; + float maxCascadeOptimalDistance = LOW_MAX_DISTANCE; + float minCascadeDistance = 0.0f; - for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { - float blendFactor = cascadeIndex / float(_cascades.size() - 1); - float maxCascadeDistance; + for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { + float blendFactor = cascadeIndex / float(_cascades.size() - 1); + float maxCascadeDistance; - if (cascadeIndex == size_t(_cascades.size() - 1)) { - maxCascadeDistance = _maxDistance; - } else { - maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor; + if (cascadeIndex == size_t(_cascades.size() - 1)) { + maxCascadeDistance = _maxDistance; + } else { + maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor; + } + + float shadowOverlapDistance = maxCascadeDistance * OVERLAP_FACTOR; + + _cascades[cascadeIndex].setMinDistance(minCascadeDistance); + _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance); + + // Compute distances for next cascade + minCascadeDistance = maxCascadeDistance; + maxCascadeUserDistance = maxCascadeUserDistance * userDistanceScale; + maxCascadeOptimalDistance = maxCascadeOptimalDistance * optimalDistanceScale; + maxCascadeUserDistance = std::min(maxCascadeUserDistance, _maxDistance); } - - float shadowOverlapDistance = maxCascadeDistance * OVERLAP_FACTOR; - - _cascades[cascadeIndex].setMinDistance(minCascadeDistance); - _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance); - - // Compute distances for next cascade - minCascadeDistance = maxCascadeDistance; - maxCascadeUserDistance = maxCascadeUserDistance * userDistanceScale; - maxCascadeOptimalDistance = maxCascadeOptimalDistance * optimalDistanceScale; - maxCascadeUserDistance = std::min(maxCascadeUserDistance, _maxDistance); } - } - // Update the buffer - const auto& lastCascade = _cascades.back(); - auto& schema = _schemaBuffer.edit(); - schema.maxDistance = _maxDistance; - schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance()); + // Update the buffer + const auto& lastCascade = _cascades.back(); + auto& schema = _schemaBuffer.edit(); + schema.maxDistance = _maxDistance; + schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance()); + } } void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 1fb1754862..4da66843cc 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -74,7 +74,7 @@ public: float left, float right, float bottom, float top, float viewMaxShadowDistance) const; }; - Shadow(graphics::LightPointer light, float maxDistance, unsigned int cascadeCount = 1); + Shadow(graphics::LightPointer light, unsigned int cascadeCount = 1); void setLight(graphics::LightPointer light); @@ -104,16 +104,14 @@ public: }; protected: - using Cascades = std::vector; static const glm::mat4 _biasMatrix; graphics::LightPointer _light; - float _maxDistance; + float _maxDistance{ 0.0f }; Cascades _cascades; - UniformBufferView _schemaBuffer = nullptr; }; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index bf564c1c10..5de89a11b5 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -34,7 +34,6 @@ #define SHADOW_FRUSTUM_NEAR 1.0f #define SHADOW_FRUSTUM_FAR 500.0f static const unsigned int SHADOW_CASCADE_COUNT{ 4 }; -static const float SHADOW_MAX_DISTANCE{ 40.0f }; using namespace render; @@ -367,7 +366,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, c output.edit2() = _cameraFrustum; if (!_globalShadowObject) { - _globalShadowObject = std::make_shared(graphics::LightPointer(), SHADOW_MAX_DISTANCE, SHADOW_CASCADE_COUNT); + _globalShadowObject = std::make_shared(currentKeyLight, SHADOW_CASCADE_COUNT); } _globalShadowObject->setLight(currentKeyLight); @@ -378,11 +377,12 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, c unsigned int cascadeIndex; // Adjust each cascade frustum + const auto biasScale = currentKeyLight->getShadowsBiasScale(); for (cascadeIndex = 0; cascadeIndex < _globalShadowObject->getCascadeCount(); ++cascadeIndex) { auto& bias = _bias[cascadeIndex]; _globalShadowObject->setKeylightCascadeFrustum(cascadeIndex, args->getViewFrustum(), - SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, - bias._constant, bias._slope); + SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, + bias._constant, bias._slope * biasScale); } _shadowFrameCache->pushShadow(_globalShadowObject); From 5baf2f21d84d5d3a481242e5800ad97ac2b1afad Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 18 Apr 2019 07:48:59 -0700 Subject: [PATCH 42/85] adding settingsLoaded bool check --- interface/src/scripting/Audio.cpp | 5 +++-- interface/src/scripting/Audio.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index caae946116..0a29152c7c 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -174,7 +174,7 @@ void Audio::setPTTDesktop(bool enabled) { _pttDesktop = enabled; } }); - if (!enabled) { + if (!enabled && _settingsLoaded) { // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. setMutedDesktop(true); } else { @@ -202,7 +202,7 @@ void Audio::setPTTHMD(bool enabled) { _pttHMD = enabled; } }); - if (!enabled) { + if (!enabled && _settingsLoaded) { // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. setMutedHMD(false); } else { @@ -231,6 +231,7 @@ void Audio::loadData() { auto client = DependencyManager::get().data(); QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted()), Q_ARG(bool, false)); + _settingsLoaded = true; } bool Audio::getPTTHMD() const { diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 00da566b30..f7116ced3a 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -409,6 +409,7 @@ protected: private: + bool _settingsLoaded { false }; float _inputVolume { 1.0f }; float _inputLevel { 0.0f }; float _localInjectorGain { 0.0f }; // in dB From 0d914a695b0bc538bd5ac749a5cac6c4902eee93 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 18 Apr 2019 09:06:02 -0700 Subject: [PATCH 43/85] wip --- libraries/baking/src/ModelBaker.cpp | 1 + libraries/baking/src/ModelBaker.h | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index e58ec00afa..d5739c5e34 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -245,6 +245,7 @@ void ModelBaker::bakeSourceCopy() { baker.run(); _hfmModel = baker.getHFMModel(); + _materialMapping = baker.getMaterialMapping(); dracoMeshes = baker.getDracoMeshes(); dracoMaterialLists = baker.getDracoMaterialLists(); } diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h index d612a0a43a..37909fb6b4 100644 --- a/libraries/baking/src/ModelBaker.h +++ b/libraries/baking/src/ModelBaker.h @@ -87,6 +87,7 @@ private: bool _hasBeenBaked { false }; hfm::Model::Pointer _hfmModel; + MaterialMapping _materialMapping; QSharedPointer _materialBaker; }; From 386d392144c7cb5ffdbee118bc0ecf53852fc9a8 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 18 Apr 2019 19:05:40 +0200 Subject: [PATCH 44/85] The step arrows are actually clickable, they were missing multi-diff behavior before this change. < = > --- scripts/system/html/js/draggableNumber.js | 38 ++++++++++++++-------- scripts/system/html/js/entityProperties.js | 38 ++++++++++++---------- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/scripts/system/html/js/draggableNumber.js b/scripts/system/html/js/draggableNumber.js index 30e8204703..3c7b74290c 100644 --- a/scripts/system/html/js/draggableNumber.js +++ b/scripts/system/html/js/draggableNumber.js @@ -21,7 +21,7 @@ function DraggableNumber(min, max, step, decimals, dragStart, dragEnd) { this.initialMouseEvent = null; this.lastMouseEvent = null; this.valueChangeFunction = null; - this.multiDiffDragFunction = null; + this.multiDiffStepFunction = null; this.initialize(); } @@ -75,8 +75,8 @@ DraggableNumber.prototype = { let dragDelta = event.clientX - this.lastMouseEvent.clientX; if (dragDelta !== 0) { if (this.multiDiffModeEnabled) { - if (this.multiDiffDragFunction) { - this.multiDiffDragFunction(dragDelta * this.step); + if (this.multiDiffStepFunction) { + this.multiDiffStepFunction(dragDelta * this.step); } } else { if (dragDelta > 0) { @@ -108,20 +108,32 @@ DraggableNumber.prototype = { stepUp: function() { if (!this.isDisabled()) { - this.elInput.value = parseFloat(this.elInput.value) + this.step; - this.inputChange(); - if (this.valueChangeFunction) { - this.valueChangeFunction(); + if (this.multiDiffModeEnabled) { + if (this.multiDiffStepFunction) { + this.multiDiffStepFunction(this.step, true); + } + } else { + this.elInput.value = parseFloat(this.elInput.value) + this.step; + this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } } } }, stepDown: function() { if (!this.isDisabled()) { - this.elInput.value = parseFloat(this.elInput.value) - this.step; - this.inputChange(); - if (this.valueChangeFunction) { - this.valueChangeFunction(); + if (this.multiDiffModeEnabled) { + if (this.multiDiffStepFunction) { + this.multiDiffStepFunction(-this.step, true); + } + } else { + this.elInput.value = parseFloat(this.elInput.value) - this.step; + this.inputChange(); + if (this.valueChangeFunction) { + this.valueChangeFunction(); + } } } }, @@ -158,8 +170,8 @@ DraggableNumber.prototype = { this.elInput.addEventListener("change", this.valueChangeFunction); }, - setMultiDiffDragFunction: function(multiDiffDragFunction) { - this.multiDiffDragFunction = multiDiffDragFunction; + setMultiDiffStepFunction: function (multiDiffStepFunction) { + this.multiDiffStepFunction = multiDiffStepFunction; }, inputChange: function() { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index fe8a08d53d..4cee3c0bc7 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -2353,21 +2353,25 @@ function updateNumberMinMax(property) { /** * - * @param {object} property - property update on drag - * @param {string} [propertyComponent] - propertyComponent to update on drag (e.g. enter 'x' to just update position.x) + * @param {object} property - property update on step + * @param {string} [propertyComponent] - propertyComponent to update on step (e.g. enter 'x' to just update position.x) * @returns {Function} */ -function createMultiDiffDragFunction(property, propertyComponent) { - return function(dragDelta) { +function createMultiDiffStepFunction(property, propertyComponent) { + return function(step, shouldAddToUndoHistory) { + if (shouldAddToUndoHistory === undefined) { + shouldAddToUndoHistory = false; + } + let propertyMultiValue = getMultiplePropertyValue(property.name); if (!propertyMultiValue.isMultiDiffValue) { - console.log("setMultiDiffDragFunction is only supposed to be called in MultiDiff mode."); + console.log("setMultiDiffStepFunction is only supposed to be called in MultiDiff mode."); return; } let multiplier = property.data.multiplier !== undefined ? property.data.multiplier : 1; - let applyDelta = dragDelta * multiplier; + let applyDelta = step * multiplier; if (selectedEntityIDs.size !== propertyMultiValue.values.length) { console.log("selectedEntityIDs and propertyMultiValue got out of sync."); @@ -2396,7 +2400,7 @@ function createMultiDiffDragFunction(property, propertyComponent) { mergeDeep(currentSelections[i].properties, propertiesUpdate); } - updateMultiDiffProperties(updateObjects, true); + updateMultiDiffProperties(updateObjects, !shouldAddToUndoHistory); } } @@ -2419,7 +2423,7 @@ function createNumberDraggableProperty(property, elProperty) { let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); elDraggableNumber.setValueChangeFunction(valueChangeFunction); - elDraggableNumber.setMultiDiffDragFunction(createMultiDiffDragFunction(property)); + elDraggableNumber.setMultiDiffStepFunction(createMultiDiffStepFunction(property)); elDraggableNumber.elInput.setAttribute("id", elementID); elProperty.appendChild(elDraggableNumber.elDiv); @@ -2465,10 +2469,10 @@ function createRectProperty(property, elProperty) { elNumberWidth.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'width')); elNumberHeight.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'height')); - elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); - elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); - elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'width')); - elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'height')); + elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'x')); + elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'y')); + elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'width')); + elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'height')); let elResult = []; elResult[RECT_ELEMENTS.X_NUMBER] = elNumberX; @@ -2503,9 +2507,9 @@ function createVec3Property(property, elProperty) { elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); elNumberZ.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'z')); - elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); - elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); - elNumberZ.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'z')); + elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'x')); + elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'y')); + elNumberZ.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'z')); let elResult = []; elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; @@ -2532,8 +2536,8 @@ function createVec2Property(property, elProperty) { elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); - elNumberX.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'x')); - elNumberY.setMultiDiffDragFunction(createMultiDiffDragFunction(property, 'y')); + elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'x')); + elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'y')); let elResult = []; elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; From 39d03ce87a54bdc9579656efe2d814453de81fd6 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 18 Apr 2019 19:19:38 +0200 Subject: [PATCH 45/85] Removed magic number --- libraries/render-utils/src/LightStage.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index d35bc21526..524deaaad2 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -162,7 +162,9 @@ void LightStage::Shadow::setLight(graphics::LightPointer light) { } void LightStage::Shadow::setMaxDistance(float value) { - value = std::max(1e-3f, value); + static const auto MINIMUM_MAXDISTANCE = 1e-3f; + + value = std::max(MINIMUM_MAXDISTANCE, value); if (value != _maxDistance) { // This overlaping factor isn't really used directly for blending of shadow cascades. It's // just there to be sure the cascades do overlap. The blending width used is relative From 27f1255899a141713f87b9302b0052151728f586 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 18 Apr 2019 11:22:37 -0700 Subject: [PATCH 46/85] fix transparent textures on baked assets (cherry picked from commit b89dbf834625b59c02a025a373118b2ee1e51f36) --- .../src/model-networking/ModelCache.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 23b365dd03..75c63f99ca 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -498,6 +498,20 @@ bool Geometry::areTexturesLoaded() const { material->checkResetOpacityMap(); } + for (auto& materialMapping : _materialMapping) { + if (materialMapping.second) { + for (auto& materialPair : materialMapping.second->parsedMaterials.networkMaterials) { + if (materialPair.second) { + if (materialPair.second->isMissingTexture()) { + return false; + } + + materialPair.second->checkResetOpacityMap(); + } + } + } + } + _areTexturesLoaded = true; } return true; From da8f8e0873e9371212e36694798b87bdd9a529fe Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 18 Apr 2019 14:46:08 -0700 Subject: [PATCH 47/85] real fix --- interface/src/scripting/Audio.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 0a29152c7c..ce340f2665 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -174,9 +174,11 @@ void Audio::setPTTDesktop(bool enabled) { _pttDesktop = enabled; } }); - if (!enabled && _settingsLoaded) { - // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. - setMutedDesktop(true); + if (!enabled) { + if (_settingsLoaded) { + // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. + setMutedDesktop(true); + } } else { // Should be muted when not pushing to talk while PTT is enabled. setMutedDesktop(true); @@ -202,9 +204,11 @@ void Audio::setPTTHMD(bool enabled) { _pttHMD = enabled; } }); - if (!enabled && _settingsLoaded) { - // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. - setMutedHMD(false); + if (!enabled) { + if (_settingsLoaded) { + // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. + setMutedHMD(false); + } } else { // Should be muted when not pushing to talk while PTT is enabled. setMutedHMD(true); @@ -358,11 +362,6 @@ void Audio::onContextChanged() { changed = true; } }); - if (isHMD) { - setMuted(getMutedHMD()); - } else { - setMuted(getMutedDesktop()); - } if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); } From af3d981ad163be12a9d4d66ff7049c04c11067d1 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Thu, 18 Apr 2019 17:44:08 -0700 Subject: [PATCH 48/85] Bringing the Luci interface into the new Prop framework and moving some tools there too --- .../utilities/lib/jet/qml/TaskPropView.qml | 35 ++-- .../utilities/lib/prop/PropGroup.qml | 121 +----------- .../utilities/lib/prop/style/PiCanvasIcon.qml | 9 +- .../utilities/lib/prop/style/PiCheckBox.qml | 2 - .../lib/prop/style/PiFolderPanel.qml | 24 +-- .../utilities/render/antialiasing.js | 2 +- .../utilities/render/antialiasing.qml | 182 ------------------ .../utilities/render/deferredLighting.qml | 177 +---------------- scripts/developer/utilities/render/luci.qml | 60 +++--- .../utilities/render/luci/Antialiasing.qml | 176 +++++++++++++++++ .../utilities/render/luci/BoundingBoxes.qml | 84 ++++++++ .../utilities/render/luci/Framebuffer.qml | 69 +++++++ .../utilities/render/luci/ShadingModel.qml | 25 +-- .../utilities/render/luci/ToneMapping.qml | 40 ++++ .../developer/utilities/render/luci/qmldir | 6 +- 15 files changed, 449 insertions(+), 563 deletions(-) delete mode 100644 scripts/developer/utilities/render/antialiasing.qml create mode 100644 scripts/developer/utilities/render/luci/Antialiasing.qml create mode 100644 scripts/developer/utilities/render/luci/BoundingBoxes.qml create mode 100644 scripts/developer/utilities/render/luci/Framebuffer.qml create mode 100644 scripts/developer/utilities/render/luci/ToneMapping.qml diff --git a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml index e53932a9f9..0587352b4e 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml @@ -30,9 +30,10 @@ Prop.PropGroup { property var showProps: true property var showSubs: true - property var jobEnabled: true + property var jobEnabled: rootConfig.getConfig(jobPath).enabled + property var jobCpuTime: rootConfig.getConfig(jobPath).cpuRunTime.toPrecision(3) - property var toggleJobActivation: function() { + property var toggleJobActivation: function() { console.log("the button has been pressed and jobEnabled is " + jobEnabled ) jobEnabled = !jobEnabled; rootConfig.getConfig(jobPath).enabled = jobEnabled; @@ -44,29 +45,31 @@ Prop.PropGroup { id: header Prop.PropLabel { text: root.label - //horizontalAlignment: Text.AlignHCenter + horizontalAlignment: Text.AlignHCenter anchors.left: parent.left - anchors.right: enabledIcon.left + anchors.right: cpuTime.left anchors.verticalCenter: parent.verticalCenter - } + } + Prop.PropLabel { + id: cpuTime + visible: root.jobEnabled + width: 50 + text: jobCpuTime + horizontalAlignment: Text.AlignLeft + anchors.rightMargin: 5 + anchors.right:enabledIcon.right + anchors.verticalCenter: parent.verticalCenter + } Prop.PropCanvasIcon { id: enabledIcon - anchors.right: enabledIcon2.left + anchors.right:parent.right anchors.verticalCenter: parent.verticalCenter filled: root.jobEnabled fillColor: (root.jobEnabled ? global.colorGreenHighlight : global.colorOrangeAccent) icon: 5 iconMouseArea.onClicked: { toggleJobActivation() } } - Prop.PropCanvasIcon { - id: enabledIcon2 - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - filled: root.jobEnabled - fillColor: (root.jobEnabled ? global.colorGreenHighlight : global.colorOrangeAccent) - icon: 7 - iconMouseArea.onClicked: { toggleJobActivation() } - } + } } @@ -78,7 +81,7 @@ Prop.PropGroup { for (var p in props) { propsModel.push({"object": rootConfig.getConfig(jobPath), "property":props[p] }) } - root.updatePropItems(propsModel); + root.updatePropItems(root.propItemsPanel, propsModel); } if (showSubs) { diff --git a/scripts/developer/utilities/lib/prop/PropGroup.qml b/scripts/developer/utilities/lib/prop/PropGroup.qml index 2f15e60a0c..384b50ecad 100644 --- a/scripts/developer/utilities/lib/prop/PropGroup.qml +++ b/scripts/developer/utilities/lib/prop/PropGroup.qml @@ -10,123 +10,14 @@ import QtQuick 2.7 -//Item { +// PropGroup is mostly reusing the look and feel of the PropFolderPanel +// It is populated by calling "updatePropItems" +// or adding manually new Items to the "propItemsPanel" PropFolderPanel { Global { id: global } id: root - - // property var label: "group" - - // property alias isUnfold: headerFolderIcon.icon - // property var indentDepth: 0 - //property alias propItemsPanel: _panelFrameData - // property alias propItemsPanel: propItemsContainer - //property var propItemsPanel: propItemsContainer - - panelFrameData: Component { // default is a column - id: groupPanelFrameData - Column { - id: propItemsContainer - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: 0 - anchors.rightMargin: 0 - - clip: true - } - } - - // Panel Header Data Component -/* property Component panelHeaderData: defaultPanelHeaderData - Component { // default is a Label - id: defaultPanelHeaderData - PropLabel { - text: root.label - horizontalAlignment: Text.AlignHCenter - } - } - - // Header Item - Rectangle { - id: header - height: global.slimHeight - anchors.left: parent.left - anchors.right: parent.right - - color: global.colorBackShadow // header of group is darker - - // First in the header, some indentation spacer - Item { - id: indentSpacer - width: (headerFolderIcon.width * root.indentDepth) + global.horizontalMargin // Must be non-zero - height: parent.height - - anchors.verticalCenter: parent.verticalCenter - } - - // Second, the folder button / indicator - Item { - id: headerFolder - anchors.left: indentSpacer.right - width: headerFolderIcon.width * 2 - anchors.verticalCenter: header.verticalCenter - height: parent.height - - PropCanvasIcon { - id: headerFolderIcon - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - fillColor: global.colorOrangeAccent - filled: root.propItemsPanel.height > 4 - iconMouseArea.onClicked: { root.isUnfold = !root.isUnfold } - } - } - - // Next the header container - // by default containing a Label showing the root.label - Loader { - sourceComponent: panelHeaderData - anchors.left: headerFolder.right - anchors.right: header.right - anchors.verticalCenter: header.verticalCenter - height: parent.height - } - } - - // The Panel container - Rectangle { - visible: root.isUnfold - - color: "transparent" - border.color: global.colorBorderLight - border.width: global.valueBorderWidth - radius: global.valueBorderRadius - - anchors.margins: 0 - anchors.left: parent.left - anchors.right: parent.right - anchors.top: header.bottom - anchors.bottom: root.bottom - - Column { - id: propItemsContainer - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: 0 - anchors.rightMargin: 0 - - clip: true - - // Where the propItems are added - } - } - - height: header.height + isUnfold * propItemsContainer.height - anchors.margins: 0 - anchors.left: parent.left - anchors.right: parent.right -*/ + property alias propItemsPanel: root.panelFrameContent // Prop Group is designed to author an array of ProItems, they are defined with an array of the tuplets describing each individual item: // [ ..., PropItemInfo, ...] @@ -134,7 +25,8 @@ PropFolderPanel { // type: "PropXXXX", object: JSobject, property: "propName" // } // - function updatePropItems(propItemsModel) { + function updatePropItems(propItemsContainer, propItemsModel) { + root.hasContent = false for (var i = 0; i < propItemsModel.length; i++) { var proItem = propItemsModel[i]; // valid object @@ -191,6 +83,7 @@ PropFolderPanel { }) } break; } + root.hasContent = true } else { console.log('Invalid property: ' + JSON.stringify(proItem)); } diff --git a/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml b/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml index 3feed342b8..2fc3ed10ec 100644 --- a/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml +++ b/scripts/developer/utilities/lib/prop/style/PiCanvasIcon.qml @@ -79,12 +79,9 @@ Canvas { context.lineTo(width * 0.75, height * 0.25); context.closePath(); } break; - default: {// Down Arrow - /* context.moveTo(0, height * 0.25); - context.lineTo(width, height * 0.25); - context.lineTo(width / 2, height); - context.closePath();*/ - } + default: { + + } } if (filled) { context.fillStyle = fillColor; diff --git a/scripts/developer/utilities/lib/prop/style/PiCheckBox.qml b/scripts/developer/utilities/lib/prop/style/PiCheckBox.qml index 108a763be3..1e1f03669b 100644 --- a/scripts/developer/utilities/lib/prop/style/PiCheckBox.qml +++ b/scripts/developer/utilities/lib/prop/style/PiCheckBox.qml @@ -20,6 +20,4 @@ HifiControls.CheckBox { //anchors.verticalCenter: root.verticalCenter //width: root.width * global.valueAreaWidthScale height: global.slimHeight - - onCheckedChanged: { root.valueVarSetter(checked); } } \ No newline at end of file diff --git a/scripts/developer/utilities/lib/prop/style/PiFolderPanel.qml b/scripts/developer/utilities/lib/prop/style/PiFolderPanel.qml index b80ab47fc6..d66c0708d3 100644 --- a/scripts/developer/utilities/lib/prop/style/PiFolderPanel.qml +++ b/scripts/developer/utilities/lib/prop/style/PiFolderPanel.qml @@ -17,6 +17,7 @@ Item { property var label: "panel" property alias isUnfold: headerFolderIcon.icon + property alias hasContent: headerFolderIcon.filled property var indentDepth: 0 // Panel Header Data Component @@ -37,7 +38,7 @@ Item { } } - //property alias panelFrameContent: frame._panelFrameData.data + property alias panelFrameContent: _panelFrameData.item // Header Item Rectangle { @@ -45,13 +46,14 @@ Item { height: global.slimHeight anchors.left: parent.left anchors.right: parent.right + anchors.margins: 0 color: global.colorBackShadow // header of group is darker // First in the header, some indentation spacer Item { id: indentSpacer - width: (headerFolderIcon.width * root.indentDepth) + global.horizontalMargin // Must be non-zero + width: (headerFolderIcon.width * root.indentDepth) + global.horizontalMargin // Must be non-zero height: parent.height anchors.verticalCenter: parent.verticalCenter @@ -70,7 +72,6 @@ Item { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter fillColor: global.colorOrangeAccent - filled: root.frame.height > 4 iconMouseArea.onClicked: { root.isUnfold = !root.isUnfold } } } @@ -81,6 +82,7 @@ Item { sourceComponent: panelHeaderData anchors.left: headerFolder.right anchors.right: header.right + anchors.rightMargin: global.horizontalMargin * 2 anchors.verticalCenter: header.verticalCenter height: parent.height } @@ -104,29 +106,17 @@ Item { // Next the panel frame data Loader { + id: _panelFrameData sourceComponent: panelFrameData anchors.left: parent.left anchors.right: parent.right anchors.leftMargin: 0 anchors.rightMargin: 0 - id: _panelFrameData clip: true } - - /* Column { - id: propItemsContainer - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: 0 - anchors.rightMargin: 0 - - clip: true - - // Where the propItems are added - }*/ } - height: header.height + isUnfold * _panelFrameData.height + height: header.height + isUnfold * _panelFrameData.item.height anchors.margins: 0 anchors.left: parent.left anchors.right: parent.right diff --git a/scripts/developer/utilities/render/antialiasing.js b/scripts/developer/utilities/render/antialiasing.js index e915d75e93..5484565b36 100644 --- a/scripts/developer/utilities/render/antialiasing.js +++ b/scripts/developer/utilities/render/antialiasing.js @@ -13,7 +13,7 @@ (function() { var TABLET_BUTTON_NAME = "TAA"; - var QMLAPP_URL = Script.resolvePath("./antialiasing.qml"); + var QMLAPP_URL = Script.resolvePath("./luci/Antialiasing.qml"); var onLuciScreen = false; diff --git a/scripts/developer/utilities/render/antialiasing.qml b/scripts/developer/utilities/render/antialiasing.qml deleted file mode 100644 index 5abfd30935..0000000000 --- a/scripts/developer/utilities/render/antialiasing.qml +++ /dev/null @@ -1,182 +0,0 @@ -// -// Antialiasing.qml -// -// Created by Sam Gateau on 8/14/2017 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.3 - -import stylesUit 1.0 -import controlsUit 1.0 as HifiControls - -import "configSlider" -import "../lib/plotperf" - -Item { -Rectangle { - id: root; - - HifiConstants { id: hifi; } - color: hifi.colors.baseGray; - - Column { - id: antialiasing - spacing: 20 - padding: 10 - - Column{ - spacing: 10 - - Row { - spacing: 10 - id: fxaaOnOff - property bool debugFXAA: false - HifiControls.Button { - function getTheText() { - if (Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff) { - return "FXAA" - } else { - return "TAA" - } - } - text: getTheText() - onClicked: { - var onOff = !Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff; - if (onOff) { - Render.getConfig("RenderMainView.JitterCam").none(); - Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = true; - } else { - Render.getConfig("RenderMainView.JitterCam").play(); - Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = false; - } - - } - } - } - Separator {} - Row { - spacing: 10 - - HifiControls.Button { - text: { - var state = 2 - (Render.getConfig("RenderMainView.JitterCam").freeze * 1 - Render.getConfig("RenderMainView.JitterCam").stop * 2); - if (state === 2) { - return "Jitter" - } else if (state === 1) { - return "Paused at " + Render.getConfig("RenderMainView.JitterCam").index + "" - } else { - return "No Jitter" - } - } - onClicked: { Render.getConfig("RenderMainView.JitterCam").cycleStopPauseRun(); } - } - HifiControls.Button { - text: "<" - onClicked: { Render.getConfig("RenderMainView.JitterCam").prev(); } - } - HifiControls.Button { - text: ">" - onClicked: { Render.getConfig("RenderMainView.JitterCam").next(); } - } - } - Separator {} - HifiControls.CheckBox { - boxSize: 20 - text: "Constrain color" - checked: Render.getConfig("RenderMainView.Antialiasing")["constrainColor"] - onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["constrainColor"] = checked } - } - ConfigSlider { - label: qsTr("Covariance gamma") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") - property: "covarianceGamma" - max: 1.5 - min: 0.5 - height: 38 - } - Separator {} - HifiControls.CheckBox { - boxSize: 20 - text: "Feedback history color" - checked: Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"] - onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"] = checked } - } - - ConfigSlider { - label: qsTr("Source blend") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") - property: "blend" - max: 1.0 - min: 0.0 - height: 38 - } - - ConfigSlider { - label: qsTr("Post sharpen") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") - property: "sharpen" - max: 1.0 - min: 0.0 - } - Separator {} - Row { - - spacing: 10 - HifiControls.CheckBox { - boxSize: 20 - text: "Debug" - checked: Render.getConfig("RenderMainView.Antialiasing")["debug"] - onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["debug"] = checked } - } - HifiControls.CheckBox { - boxSize: 20 - text: "Show Debug Cursor" - checked: Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"] - onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"] = checked } - } - } - ConfigSlider { - label: qsTr("Debug Region <") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") - property: "debugX" - max: 1.0 - min: 0.0 - } - HifiControls.CheckBox { - boxSize: 20 - text: "Closest Fragment" - checked: Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"] - onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"] = checked } - } - ConfigSlider { - label: qsTr("Debug Velocity Threshold [pix]") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") - property: "debugShowVelocityThreshold" - max: 50 - min: 0.0 - height: 38 - } - ConfigSlider { - label: qsTr("Debug Orb Zoom") - integral: false - config: Render.getConfig("RenderMainView.Antialiasing") - property: "debugOrbZoom" - max: 32.0 - min: 1.0 - height: 38 - } - } - } -} -} diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index 63c2af1b0f..80ca8b09e1 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -29,57 +29,16 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.margins: hifi.dimensions.contentMargin.x - //padding: hifi.dimensions.contentMargin.x + + HifiControls.Label { text: "Shading" } - ShadingModel { + ShadingModel {} - } - Separator {} - Column { - anchors.left: parent.left - anchors.right: parent.right - spacing: 5 - Repeater { - model: [ "Tone Mapping Exposure:ToneMapping:exposure:5.0:-5.0" - ] - ConfigSlider { - label: qsTr(modelData.split(":")[0]) - integral: false - config: render.mainViewTask.getConfig(modelData.split(":")[1]) - property: modelData.split(":")[2] - max: modelData.split(":")[3] - min: modelData.split(":")[4] + Separator {} + ToneMapping {} - anchors.left: parent.left - anchors.right: parent.right - } - } - Item { - height: childrenRect.height - anchors.left: parent.left - anchors.right: parent.right - - HifiControls.Label { - text: "Tone Mapping Curve" - anchors.left: parent.left - } - - ComboBox { - anchors.right: parent.right - currentIndex: 1 - model: [ - "RGB", - "SRGB", - "Reinhard", - "Filmic", - ] - width: 200 - onCurrentIndexChanged: { render.mainViewTask.getConfig("ToneMapping")["curve"] = currentIndex; } - } - } - } Separator {} Column { anchors.left: parent.left @@ -102,133 +61,11 @@ Rectangle { } } Separator {} + Framebuffer {} - Item { - height: childrenRect.height - anchors.left: parent.left - anchors.right: parent.right - - id: framebuffer - - HifiControls.Label { - text: "Debug Framebuffer" - anchors.left: parent.left - } - - property var config: render.mainViewTask.getConfig("DebugDeferredBuffer") - - function setDebugMode(mode) { - framebuffer.config.enabled = (mode != 0); - framebuffer.config.mode = mode; - } - - ComboBox { - anchors.right: parent.right - currentIndex: 0 - model: ListModel { - id: cbItemsFramebuffer - ListElement { text: "Off"; color: "Yellow" } - ListElement { text: "Depth"; color: "Green" } - ListElement { text: "Albedo"; color: "Yellow" } - ListElement { text: "Normal"; color: "White" } - ListElement { text: "Roughness"; color: "White" } - ListElement { text: "Metallic"; color: "White" } - ListElement { text: "Emissive"; color: "White" } - ListElement { text: "Unlit"; color: "White" } - ListElement { text: "Occlusion"; color: "White" } - ListElement { text: "Lightmap"; color: "White" } - ListElement { text: "Scattering"; color: "White" } - ListElement { text: "Lighting"; color: "White" } - ListElement { text: "Shadow Cascade 0"; color: "White" } - ListElement { text: "Shadow Cascade 1"; color: "White" } - ListElement { text: "Shadow Cascade 2"; color: "White" } - ListElement { text: "Shadow Cascade 3"; color: "White" } - ListElement { text: "Shadow Cascade Indices"; color: "White" } - ListElement { text: "Linear Depth"; color: "White" } - ListElement { text: "Half Linear Depth"; color: "White" } - ListElement { text: "Half Normal"; color: "White" } - ListElement { text: "Mid Curvature"; color: "White" } - ListElement { text: "Mid Normal"; color: "White" } - ListElement { text: "Low Curvature"; color: "White" } - ListElement { text: "Low Normal"; color: "White" } - ListElement { text: "Curvature Occlusion"; color: "White" } - ListElement { text: "Debug Scattering"; color: "White" } - ListElement { text: "Ambient Occlusion"; color: "White" } - ListElement { text: "Ambient Occlusion Blurred"; color: "White" } - ListElement { text: "Ambient Occlusion Normal"; color: "White" } - ListElement { text: "Velocity"; color: "White" } - ListElement { text: "Custom"; color: "White" } - } - width: 200 - onCurrentIndexChanged: { framebuffer.setDebugMode(currentIndex) } - } - } - Separator {} - Row { - spacing: 5 - Column { - spacing: 5 + BoundingBoxes { - HifiControls.CheckBox { - boxSize: 20 - text: "Opaques" - checked: render.mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] - onCheckedChanged: { render.mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] = checked } - } - HifiControls.CheckBox { - boxSize: 20 - text: "Transparents" - checked: render.mainViewTask.getConfig("DrawTransparentBounds")["enabled"] - onCheckedChanged: { render.mainViewTask.getConfig("DrawTransparentBounds")["enabled"] = checked } - } - HifiControls.CheckBox { - boxSize: 20 - text: "Opaques in Front" - checked: render.mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"] - onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"] = checked } - } - HifiControls.CheckBox { - boxSize: 20 - text: "Transparents in Front" - checked: render.mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"] - onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"] = checked } - } - HifiControls.CheckBox { - boxSize: 20 - text: "Opaques in HUD" - checked: render.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] - onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] = checked } - } - - } - Column { - spacing: 5 - HifiControls.CheckBox { - boxSize: 20 - text: "Metas" - checked: render.mainViewTask.getConfig("DrawMetaBounds")["enabled"] - onCheckedChanged: { render.mainViewTask.getConfig("DrawMetaBounds")["enabled"] = checked } - } - HifiControls.CheckBox { - boxSize: 20 - text: "Lights" - checked: render.mainViewTask.getConfig("DrawLightBounds")["enabled"] - onCheckedChanged: { render.mainViewTask.getConfig("DrawLightBounds")["enabled"] = checked; } - } - HifiControls.CheckBox { - boxSize: 20 - text: "Zones" - checked: render.mainViewTask.getConfig("DrawZones")["enabled"] - onCheckedChanged: { render.mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; render.mainViewTask.getConfig("DrawZones")["enabled"] = checked; } - } - HifiControls.CheckBox { - boxSize: 20 - text: "Transparents in HUD" - checked: render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] - onCheckedChanged: { render.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] = checked } - } - } } Separator {} Row { diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml index 7bce087fda..6ceee9a8d0 100644 --- a/scripts/developer/utilities/render/luci.qml +++ b/scripts/developer/utilities/render/luci.qml @@ -33,53 +33,43 @@ Rectangle { Column { width: render.width Prop.PropFolderPanel { - id: "shadingModel" label: "Shading Model" panelFrameData: Component { - ShadingModel { - } + ShadingModel {} } } - /* Prop.PropEnum { - label: "Tone Curve" - object: render.mainViewTask.getConfig("ToneMapping") - property: "curve" - enums: [ - "RGB", - "SRGB", - "Reinhard", - "Filmic", - ] - anchors.left: parent.left - anchors.right: parent.right - } */ - Jet.TaskPropView { - id: "theView" - jobPath: "RenderMainView" - label: "Le Render Main View" - - anchors.left: parent.left - anchors.right: parent.right + Prop.PropFolderPanel { + label: "Bounding Boxes" + panelFrameData: Component { + BoundingBoxes {} + } } - Jet.TaskPropView { - id: "the" - jobPath: "RenderMainView.RenderDeferredTask" - label: "Le Render Deferred Job" - - anchors.left: parent.left - anchors.right: parent.right + Prop.PropFolderPanel { + label: "Framebuffer" + panelFrameData: Component { + Framebuffer {} + } + } + Prop.PropFolderPanel { + label: "Tone Mapping" + panelFrameData: Component { + ToneMapping {} + } + } + Prop.PropFolderPanel { + label: "Antialiasing" + panelFrameData: Component { + Antialiasing {} + } } Jet.TaskPropView { id: "le" jobPath: "" label: "Le Render Engine" - anchors.left: parent.left - anchors.right: parent.right + // anchors.left: parent.left + // anchors.right: parent.right } } } - - Component.onCompleted: { - } } \ No newline at end of file diff --git a/scripts/developer/utilities/render/luci/Antialiasing.qml b/scripts/developer/utilities/render/luci/Antialiasing.qml new file mode 100644 index 0000000000..e29bca43eb --- /dev/null +++ b/scripts/developer/utilities/render/luci/Antialiasing.qml @@ -0,0 +1,176 @@ +// +// Antialiasing.qml +// +// Created by Sam Gateau on 8/14/2017 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 + +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls + +import "../configSlider" +import "../../lib/plotperf" + + +Column{ + HifiConstants { id: hifi; } + + id: antialiasing + padding: 10 + anchors.left: parent.left + anchors.right: parent.right + + spacing: 10 + + Row { + spacing: 10 + id: fxaaOnOff + property bool debugFXAA: false + HifiControls.Button { + function getTheText() { + if (Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff) { + return "FXAA" + } else { + return "TAA" + } + } + text: getTheText() + onClicked: { + var onOff = !Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff; + if (onOff) { + Render.getConfig("RenderMainView.JitterCam").none(); + Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = true; + } else { + Render.getConfig("RenderMainView.JitterCam").play(); + Render.getConfig("RenderMainView.Antialiasing").fxaaOnOff = false; + } + + } + } + } + Separator {} + Row { + spacing: 10 + + HifiControls.Button { + text: { + var state = 2 - (Render.getConfig("RenderMainView.JitterCam").freeze * 1 - Render.getConfig("RenderMainView.JitterCam").stop * 2); + if (state === 2) { + return "Jitter" + } else if (state === 1) { + return "Paused at " + Render.getConfig("RenderMainView.JitterCam").index + "" + } else { + return "No Jitter" + } + } + onClicked: { Render.getConfig("RenderMainView.JitterCam").cycleStopPauseRun(); } + } + HifiControls.Button { + text: "<" + onClicked: { Render.getConfig("RenderMainView.JitterCam").prev(); } + } + HifiControls.Button { + text: ">" + onClicked: { Render.getConfig("RenderMainView.JitterCam").next(); } + } + } + Separator {} + HifiControls.CheckBox { + boxSize: 20 + text: "Constrain color" + checked: Render.getConfig("RenderMainView.Antialiasing")["constrainColor"] + onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["constrainColor"] = checked } + } + ConfigSlider { + label: qsTr("Covariance gamma") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "covarianceGamma" + max: 1.5 + min: 0.5 + height: 38 + } + Separator {} + HifiControls.CheckBox { + boxSize: 20 + text: "Feedback history color" + checked: Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"] + onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"] = checked } + } + + ConfigSlider { + label: qsTr("Source blend") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "blend" + max: 1.0 + min: 0.0 + height: 38 + } + + ConfigSlider { + label: qsTr("Post sharpen") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "sharpen" + max: 1.0 + min: 0.0 + } + Separator {} + Row { + + spacing: 10 + HifiControls.CheckBox { + boxSize: 20 + text: "Debug" + checked: Render.getConfig("RenderMainView.Antialiasing")["debug"] + onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["debug"] = checked } + } + HifiControls.CheckBox { + boxSize: 20 + text: "Show Debug Cursor" + checked: Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"] + onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"] = checked } + } + } + ConfigSlider { + label: qsTr("Debug Region <") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "debugX" + max: 1.0 + min: 0.0 + } + HifiControls.CheckBox { + boxSize: 20 + text: "Closest Fragment" + checked: Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"] + onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"] = checked } + } + ConfigSlider { + label: qsTr("Debug Velocity Threshold [pix]") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "debugShowVelocityThreshold" + max: 50 + min: 0.0 + height: 38 + } + ConfigSlider { + label: qsTr("Debug Orb Zoom") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "debugOrbZoom" + max: 32.0 + min: 1.0 + height: 38 + } +} + diff --git a/scripts/developer/utilities/render/luci/BoundingBoxes.qml b/scripts/developer/utilities/render/luci/BoundingBoxes.qml new file mode 100644 index 0000000000..636267729c --- /dev/null +++ b/scripts/developer/utilities/render/luci/BoundingBoxes.qml @@ -0,0 +1,84 @@ +// +// BoundingBoxes.qml +// +// Created by Sam Gateau on 4/18/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +import "../../lib/prop" as Prop + +Column { + + id: root; + + property var mainViewTask: Render.getConfig("RenderMainView") + + spacing: 5 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: hifi.dimensions.contentMargin.x + + Row { + anchors.left: parent.left + anchors.right: parent.right + + spacing: 5 + Column { + spacing: 5 + + Prop.PropCheckBox { + text: "Opaques" + checked: root.mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] + onCheckedChanged: { root.mainViewTask.getConfig("DrawOpaqueBounds")["enabled"] = checked } + } + Prop.PropCheckBox { + text: "Transparents" + checked: root.mainViewTask.getConfig("DrawTransparentBounds")["enabled"] + onCheckedChanged: { root.mainViewTask.getConfig("DrawTransparentBounds")["enabled"] = checked } + } + Prop.PropCheckBox { + text: "Metas" + checked: root.mainViewTask.getConfig("DrawMetaBounds")["enabled"] + onCheckedChanged: { root.mainViewTask.getConfig("DrawMetaBounds")["enabled"] = checked } + } + Prop.PropCheckBox { + text: "Lights" + checked: root.mainViewTask.getConfig("DrawLightBounds")["enabled"] + onCheckedChanged: { root.mainViewTask.getConfig("DrawLightBounds")["enabled"] = checked; } + } + Prop.PropCheckBox { + text: "Zones" + checked: root.mainViewTask.getConfig("DrawZones")["enabled"] + onCheckedChanged: { root.mainViewTask.getConfig("ZoneRenderer")["enabled"] = checked; root.mainViewTask.getConfig("DrawZones")["enabled"] = checked; } + } + } + Column { + spacing: 5 + Prop.PropCheckBox { + text: "Opaques in Front" + checked: root.mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"] + onCheckedChanged: { root.mainViewTask.getConfig("DrawOverlayInFrontOpaqueBounds")["enabled"] = checked } + } + Prop.PropCheckBox { + text: "Transparents in Front" + checked: root.mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"] + onCheckedChanged: { root.mainViewTask.getConfig("DrawOverlayInFrontTransparentBounds")["enabled"] = checked } + } + Prop.PropCheckBox { + text: "Opaques in HUD" + checked: root.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] + onCheckedChanged: { root.mainViewTask.getConfig("DrawOverlayHUDOpaqueBounds")["enabled"] = checked } + } + Prop.PropCheckBox { + text: "Transparents in HUD" + checked: root.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] + onCheckedChanged: { root.mainViewTask.getConfig("DrawOverlayHUDTransparentBounds")["enabled"] = checked } + } + } + } +} diff --git a/scripts/developer/utilities/render/luci/Framebuffer.qml b/scripts/developer/utilities/render/luci/Framebuffer.qml new file mode 100644 index 0000000000..89d5e59002 --- /dev/null +++ b/scripts/developer/utilities/render/luci/Framebuffer.qml @@ -0,0 +1,69 @@ +// +// Framebuffer.qml +// +// Created by Sam Gateau on 4/18/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +import "../../lib/prop" as Prop + +Column { + anchors.left: parent.left + anchors.right: parent.right + + id: framebuffer + + property var config: Render.getConfig("RenderMainView.DebugDeferredBuffer") + + function setDebugMode(mode) { + framebuffer.config.enabled = (mode != 0); + framebuffer.config.mode = mode; + } + + Prop.PropEnum { + label: "Debug Buffer" + object: config + property: "mode" + valueVar: 0 + enums: [ + "Off", + "Depth", + "Albedo", + "Normal", + "Roughness", + "Metallic", + "Emissive", + "Unlit", + "Occlusion", + "Lightmap", + "Scattering", + "Lighting", + "Shadow Cascade 0", + "Shadow Cascade 1", + "Shadow Cascade 2", + "Shadow Cascade 3", + "Shadow Cascade Indices", + "Linear Depth", + "Half Linear Depth", + "Half Normal", + "Mid Curvature", + "Mid Normal", + "Low Curvature", + "Low Normal", + "Curvature Occlusion", + "Debug Scattering", + "Ambient Occlusion", + "Ambient Occlusion Blurred", + "Ambient Occlusion Normal", + "Velocity", + "Custom", + ] + + valueVarSetter: function (mode) { framebuffer.setDebugMode(mode) } + } +} \ No newline at end of file diff --git a/scripts/developer/utilities/render/luci/ShadingModel.qml b/scripts/developer/utilities/render/luci/ShadingModel.qml index 2c16492e80..78ca7f1740 100644 --- a/scripts/developer/utilities/render/luci/ShadingModel.qml +++ b/scripts/developer/utilities/render/luci/ShadingModel.qml @@ -10,18 +10,8 @@ import QtQuick 2.7 -import stylesUit 1.0 -import controlsUit 1.0 as HifiControls - -import QtQuick.Controls 1.4 -import QtQuick.Layouts 1.3 - -import stylesUit 1.0 -import controlsUit 1.0 as HifiControls - import "../../lib/prop" as Prop - Column { id: shadingModel; @@ -32,9 +22,6 @@ Column { anchors.left: parent.left anchors.right: parent.right anchors.margins: hifi.dimensions.contentMargin.x - HifiControls.Label { - text: "Shading" - } Row { anchors.left: parent.left anchors.right: parent.right @@ -54,8 +41,8 @@ Column { ] Prop.PropCheckBox { text: modelData.split(":")[0] - checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + checked: shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } } } } @@ -76,8 +63,8 @@ Column { ] Prop.PropCheckBox { text: modelData.split(":")[0] - checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + checked: shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } } } } @@ -96,8 +83,8 @@ Column { ] Prop.PropCheckBox { text: modelData.split(":")[0] - checked: render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] - onCheckedChanged: { render.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } + checked: shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] + onCheckedChanged: { shadingModel.mainViewTask.getConfig(modelData.split(":")[1])[modelData.split(":")[2]] = checked } } } } diff --git a/scripts/developer/utilities/render/luci/ToneMapping.qml b/scripts/developer/utilities/render/luci/ToneMapping.qml new file mode 100644 index 0000000000..a76990e37c --- /dev/null +++ b/scripts/developer/utilities/render/luci/ToneMapping.qml @@ -0,0 +1,40 @@ +// +// ToneMapping.qml +// +// Created by Sam Gateau on 4/17/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 + +import "../../lib/prop" as Prop + +Column { + anchors.left: parent.left + anchors.right: parent.right + Prop.PropScalar { + label: "Exposure" + object: Render.getConfig("RenderMainView.ToneMapping") + property: "exposure" + min: -4 + max: 4 + anchors.left: parent.left + anchors.right: parent.right + } + Prop.PropEnum { + label: "Tone Curve" + object: Render.getConfig("RenderMainView.ToneMapping") + property: "curve" + enums: [ + "RGB", + "SRGB", + "Reinhard", + "Filmic", + ] + anchors.left: parent.left + anchors.right: parent.right + } +} diff --git a/scripts/developer/utilities/render/luci/qmldir b/scripts/developer/utilities/render/luci/qmldir index cf63b1c2b7..c88dfee714 100644 --- a/scripts/developer/utilities/render/luci/qmldir +++ b/scripts/developer/utilities/render/luci/qmldir @@ -1,2 +1,6 @@ -ShadingModel 1.0 ShadingModel.qml \ No newline at end of file +ShadingModel 1.0 ShadingModel.qml +ToneMapping 1.0 ToneMapping.qml +BoundingBoxes 1.0 BoundingBoxes.qml +Framebuffer 1.0 Framebuffer.qml +Antialiasing 1.0 Antialiasing.qml \ No newline at end of file From 7d412f9109299331472836edacb3d036af15b631 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 19 Apr 2019 11:03:43 +0200 Subject: [PATCH 49/85] Simplified material binding in shadow mode. Added opacity mask to shadow fade. Adjusted default bias. --- .../src/RenderableMaterialEntityItem.cpp | 8 +-- .../src/RenderableShapeEntityItem.cpp | 3 +- .../src/graphics/MaterialTextures.slh | 7 --- .../render-utils/src/MeshPartPayload.cpp | 11 +--- .../render-utils/src/RenderPipelines.cpp | 61 +++++++------------ libraries/render-utils/src/RenderPipelines.h | 6 +- libraries/render-utils/src/RenderShadowTask.h | 4 +- libraries/render-utils/src/model_shadow.slf | 10 ++- .../render-utils/src/model_shadow_fade.slf | 15 ++++- 9 files changed, 50 insertions(+), 75 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index 01d1098daa..9a634a85ad 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -313,11 +313,9 @@ void MaterialEntityRenderer::doRender(RenderArgs* args) { batch.setModelTransform(renderTransform); - if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { - drawMaterial->setTextureTransforms(textureTransform, MaterialMappingMode::UV, true); - - // bind the material - RenderPipelines::bindMaterial(drawMaterial, batch, args->_enableTexturing); + drawMaterial->setTextureTransforms(textureTransform, MaterialMappingMode::UV, true); + // bind the material + if (RenderPipelines::bindMaterial(drawMaterial, batch, args->_renderMode, args->_enableTexturing)) { args->_details._materialSwitches++; } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index d859d4b739..2548ae5914 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -291,8 +291,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { geometryCache->renderSolidShapeInstance(args, batch, geometryShape, outColor, pipeline); } } else { - if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { - RenderPipelines::bindMaterials(materials, batch, args->_enableTexturing); + if (RenderPipelines::bindMaterials(materials, batch, args->_renderMode, args->_enableTexturing)) { args->_details._materialSwitches++; } diff --git a/libraries/graphics/src/graphics/MaterialTextures.slh b/libraries/graphics/src/graphics/MaterialTextures.slh index 16c7f0e211..92e76e5736 100644 --- a/libraries/graphics/src/graphics/MaterialTextures.slh +++ b/libraries/graphics/src/graphics/MaterialTextures.slh @@ -149,13 +149,6 @@ float fetchScatteringMap(vec2 uv) { <@endfunc@> -<@func fetchMaterialAlbedoTextureCoord0(matKey, texcoord0, albedo)@> - if (getTexMapArray()._materialParams.y != 1.0 && clamp(<$texcoord0$>, vec2(0.0), vec2(1.0)) != <$texcoord0$>) { - discard; - } - vec4 <$albedo$> = fetchAlbedoMap(<$texcoord0$>); -<@endfunc@> - <@func fetchMaterialTexturesCoord0(matKey, texcoord0, albedo, roughness, normal, metallic, emissive, scattering)@> if (getTexMapArray()._materialParams.y != 1.0 && clamp(<$texcoord0$>, vec2(0.0), vec2(1.0)) != <$texcoord0$>) { discard; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 9470f11d59..7be5f93a95 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -154,8 +154,7 @@ void MeshPartPayload::render(RenderArgs* args) { bindMesh(batch); // apply material properties - if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { - RenderPipelines::bindMaterials(_drawMaterials, batch, args->_enableTexturing); + if (RenderPipelines::bindMaterials(_drawMaterials, batch, args->_renderMode, args->_enableTexturing)) { args->_details._materialSwitches++; } @@ -434,13 +433,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { } // apply material properties - if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { - RenderPipelines::bindMaterials(_drawMaterials, batch, args->_enableTexturing); - args->_details._materialSwitches++; - } else { - // We might have an opacity mask so we need to bind the albedo texture and material which might hold - // the alpha mask channel - RenderPipelines::bindMaterialsOpacityMask(_drawMaterials, batch, args->_enableTexturing); + if (RenderPipelines::bindMaterials(_drawMaterials, batch, args->_renderMode, args->_enableTexturing)) { args->_details._materialSwitches++; } diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index ba0a079c7f..cd4d787f0e 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -373,10 +373,10 @@ void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, con gpu::Shader::createProgram(deformed_model_shadow_fade_dq), state, extraBatchSetter, itemSetter); } -void RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) { +bool RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, render::Args::RenderMode renderMode, bool enableTextures) { graphics::MultiMaterial multiMaterial; multiMaterial.push(graphics::MaterialLayer(material, 0)); - bindMaterials(multiMaterial, batch, enableTextures); + return bindMaterials(multiMaterial, batch, renderMode, enableTextures); } void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial) { @@ -730,17 +730,21 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial multiMaterial.setInitialized(); } -static gpu::TextureTablePointer defaultMaterialTextures = std::make_shared(); - -void RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures) { +bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, render::Args::RenderMode renderMode, bool enableTextures) { if (multiMaterial.shouldUpdate()) { updateMultiMaterial(multiMaterial); } auto textureCache = DependencyManager::get(); + static gpu::TextureTablePointer defaultMaterialTextures = std::make_shared(); + static gpu::BufferView defaultMaterialSchema; + static std::once_flag once; std::call_once(once, [textureCache] { + graphics::MultiMaterial::Schema schema; + defaultMaterialSchema = gpu::BufferView(std::make_shared(sizeof(schema), (const gpu::Byte*) &schema, sizeof(schema))); + defaultMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); defaultMaterialTextures->setTexture(gr::Texture::MaterialMetallic, textureCache->getBlackTexture()); defaultMaterialTextures->setTexture(gr::Texture::MaterialRoughness, textureCache->getWhiteTexture()); @@ -750,49 +754,26 @@ void RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: // MaterialEmissiveLightmap has to be set later }); - auto& schemaBuffer = multiMaterial.getSchemaBuffer(); - batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer); - if (enableTextures) { - batch.setResourceTextureTable(multiMaterial.getTextureTable()); - } else { - auto key = multiMaterial.getMaterialKey(); - if (key.isLightmapMap()) { - defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); - } else if (key.isEmissiveMap()) { - defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); - } - batch.setResourceTextureTable(defaultMaterialTextures); - } -} - -static gpu::BufferView defaultMaterialSchema; - -void RenderPipelines::bindMaterialsOpacityMask(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures) { - if (multiMaterial.shouldUpdate()) { - updateMultiMaterial(multiMaterial); - } - - if (multiMaterial.getMaterialKey().isOpacityMaskMap()) { - auto textureCache = DependencyManager::get(); - - static std::once_flag once; - std::call_once(once, [textureCache] { - defaultMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture()); - }); - + // For shadows, we only need opacity mask information + if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE || multiMaterial.getMaterialKey().isOpacityMaskMap()) { auto& schemaBuffer = multiMaterial.getSchemaBuffer(); batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer); - if (enableTextures) { batch.setResourceTextureTable(multiMaterial.getTextureTable()); } else { + auto key = multiMaterial.getMaterialKey(); + if (key.isLightmapMap()) { + defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); + } else if (key.isEmissiveMap()) { + defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + } batch.setResourceTextureTable(defaultMaterialTextures); } + return true; } else { - if (defaultMaterialSchema._buffer == nullptr) { - graphics::MultiMaterial::Schema schema; - defaultMaterialSchema = gpu::BufferView(std::make_shared(sizeof(schema), (const gpu::Byte*) &schema, sizeof(schema))); - } + batch.setResourceTextureTable(defaultMaterialTextures); batch.setUniformBuffer(gr::Buffer::Material, defaultMaterialSchema); + return false; } } + diff --git a/libraries/render-utils/src/RenderPipelines.h b/libraries/render-utils/src/RenderPipelines.h index 4a99b21e74..5b04de08b6 100644 --- a/libraries/render-utils/src/RenderPipelines.h +++ b/libraries/render-utils/src/RenderPipelines.h @@ -12,13 +12,13 @@ #define hifi_RenderPipelines_h #include +#include class RenderPipelines { public: - static void bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures); static void updateMultiMaterial(graphics::MultiMaterial& multiMaterial); - static void bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures); - static void bindMaterialsOpacityMask(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures); + static bool bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, render::Args::RenderMode renderMode, bool enableTextures); + static bool bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, render::Args::RenderMode renderMode, bool enableTextures); }; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 1cfa786e43..ef469a7247 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -93,8 +93,8 @@ public: float constantBias1{ 0.15f }; float constantBias2{ 0.175f }; float constantBias3{ 0.2f }; - float slopeBias0{ 0.6f }; - float slopeBias1{ 0.6f }; + float slopeBias0{ 0.4f }; + float slopeBias1{ 0.45f }; float slopeBias2{ 0.65f }; float slopeBias3{ 0.7f }; diff --git a/libraries/render-utils/src/model_shadow.slf b/libraries/render-utils/src/model_shadow.slf index 956ece4923..6d8dfb7e2a 100644 --- a/libraries/render-utils/src/model_shadow.slf +++ b/libraries/render-utils/src/model_shadow.slf @@ -23,13 +23,11 @@ layout(location=0) out vec4 _fragColor; void main(void) { Material mat = getMaterial(); BITFIELD matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL)$> - if ((matKey & OPACITY_MASK_MAP_BIT) != 0) { - float opacity = 1.0; - <$fetchMaterialAlbedoTextureCoord0(matKey, _texCoord0, albedoTex)$> - <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; - <$discardTransparent(opacity)$>; - } + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; // pass-through to set z-buffer _fragColor = vec4(1.0, 1.0, 1.0, 0.0); diff --git a/libraries/render-utils/src/model_shadow_fade.slf b/libraries/render-utils/src/model_shadow_fade.slf index 210b5482c8..1f94d4a0ac 100644 --- a/libraries/render-utils/src/model_shadow_fade.slf +++ b/libraries/render-utils/src/model_shadow_fade.slf @@ -9,13 +9,18 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - +<@include graphics/Material.slh@> +<@include graphics/MaterialTextures.slh@> <@include render-utils/ShaderConstants.h@> +<$declareMaterialTextures(ALBEDO, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL)$> + <@include Fade.slh@> <$declareFadeFragment()$> layout(location=RENDER_UTILS_ATTR_POSITION_WS) in vec4 _positionWS; +layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01; +#define _texCoord0 _texCoord01.xy layout(location=0) out vec4 _fragColor; @@ -24,6 +29,14 @@ void main(void) { <$fetchFadeObjectParams(fadeParams)$> applyFadeClip(fadeParams, _positionWS.xyz); + Material mat = getMaterial(); + BITFIELD matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL, _SCRIBE_NULL)$> + + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + // pass-through to set z-buffer _fragColor = vec4(1.0, 1.0, 1.0, 0.0); } From 9065a032bde58b816aa688c594b7e7f2af152a76 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 18 Apr 2019 14:46:08 -0700 Subject: [PATCH 50/85] real fix From 38115b0c09461fe7b1f8572c070dc72fc9df653a Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 18 Apr 2019 21:00:15 -0700 Subject: [PATCH 51/85] simplify PTT logic --- interface/src/scripting/Audio.cpp | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index ce340f2665..bceafc3c42 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -174,16 +174,10 @@ void Audio::setPTTDesktop(bool enabled) { _pttDesktop = enabled; } }); - if (!enabled) { - if (_settingsLoaded) { - // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. - setMutedDesktop(true); - } - } else { - // Should be muted when not pushing to talk while PTT is enabled. + if (enabled || _settingsLoaded) { + // Set to default behavior (muted for Desktop) on Push-To-Talk disable or when enabled. Settings also need to be loaded. setMutedDesktop(true); } - if (changed) { emit pushToTalkChanged(enabled); emit pushToTalkDesktopChanged(enabled); @@ -204,14 +198,9 @@ void Audio::setPTTHMD(bool enabled) { _pttHMD = enabled; } }); - if (!enabled) { - if (_settingsLoaded) { - // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. - setMutedHMD(false); - } - } else { - // Should be muted when not pushing to talk while PTT is enabled. - setMutedHMD(true); + if (enabled || _settingsLoaded) { + // Set to default behavior (unmuted for HMD) on Push-To-Talk disable or muted for when PTT is enabled. + setMutedHMD(enabled); } if (changed) { From 7333b02f344ecaabf80a7f5a715033711e77217f Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 19 Apr 2019 09:48:30 -0700 Subject: [PATCH 52/85] Fix model processing occurring on main thread --- .../src/material-networking/MaterialCache.h | 1 + .../src/model-networking/ModelCache.cpp | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/libraries/material-networking/src/material-networking/MaterialCache.h b/libraries/material-networking/src/material-networking/MaterialCache.h index 4894054de4..7ed0453187 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.h +++ b/libraries/material-networking/src/material-networking/MaterialCache.h @@ -108,6 +108,7 @@ private: using NetworkMaterialResourcePointer = QSharedPointer; using MaterialMapping = std::vector>; +Q_DECLARE_METATYPE(MaterialMapping) class MaterialCache : public ResourceCache { public: diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 23b365dd03..72ae224d91 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -282,8 +282,16 @@ void GeometryReader::run() { hfmModel->scripts.push_back(script.toString()); } } + + // Do processing on the model + baker::Baker modelBaker(hfmModel, _mapping.second, _mapping.first); + modelBaker.run(); + + auto processedHFMModel = modelBaker.getHFMModel(); + auto materialMapping = modelBaker.getMaterialMapping(); + QMetaObject::invokeMethod(resource.data(), "setGeometryDefinition", - Q_ARG(HFMModel::Pointer, hfmModel), Q_ARG(GeometryMappingPair, _mapping)); + Q_ARG(HFMModel::Pointer, processedHFMModel), Q_ARG(MaterialMapping, materialMapping)); } catch (const std::exception&) { auto resource = _resource.toStrongRef(); if (resource) { @@ -317,7 +325,7 @@ public: void setExtra(void* extra) override; protected: - Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, const GeometryMappingPair& mapping); + Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping); private: ModelLoader _modelLoader; @@ -340,14 +348,10 @@ void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mapping, data, _combineParts, _request->getWebMediaType())); } -void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const GeometryMappingPair& mapping) { - // Do processing on the model - baker::Baker modelBaker(hfmModel, mapping.second, mapping.first); - modelBaker.run(); - +void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping) { // Assume ownership of the processed HFMModel - _hfmModel = modelBaker.getHFMModel(); - _materialMapping = modelBaker.getMaterialMapping(); + _hfmModel = hfmModel; + _materialMapping = materialMapping; // Copy materials QHash materialIDAtlas; From 7b74561b27ac91e000734819145ce427b380f3fe Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 18 Apr 2019 16:54:20 -0700 Subject: [PATCH 53/85] Fix baked models not preserving scale in some cases --- libraries/baking/src/FBXBaker.cpp | 21 ++++++ .../src/graphics-scripting/Forward.h | 5 +- .../GraphicsScriptingInterface.cpp | 4 + .../graphics-scripting/ScriptableModel.cpp | 4 + libraries/graphics/src/graphics/Material.h | 1 + .../src/material-networking/MaterialCache.cpp | 23 +++++- libraries/shared/src/RegisteredMetaTypes.cpp | 73 +++++++++++++++++++ libraries/shared/src/RegisteredMetaTypes.h | 4 + 8 files changed, 131 insertions(+), 4 deletions(-) diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp index 01897ee5e9..9b80041bcf 100644 --- a/libraries/baking/src/FBXBaker.cpp +++ b/libraries/baking/src/FBXBaker.cpp @@ -142,6 +142,27 @@ void FBXBaker::rewriteAndBakeSceneModels(const QVector& meshes, const } else if (object->name == "Texture" || object->name == "Video") { // this is an embedded texture, we need to remove it from the FBX object = rootChild.children.erase(object); + } else if (object->name == "Material") { + for (FBXNode& materialChild : object->children) { + if (materialChild.name == "Properties60" || materialChild.name == "Properties70") { + // This is a properties node + // Remove the material texture scale because that is now included in the material JSON + // Texture nodes are removed, so their texture scale is effectively gone already + static const QVariant MAYA_UV_SCALE = hifi::ByteArray("Maya|uv_scale"); + static const QVariant MAYA_UV_OFFSET = hifi::ByteArray("Maya|uv_offset"); + for (int i = 0; i < materialChild.children.size(); i++) { + const auto& prop = materialChild.children[i]; + const auto& propertyName = prop.properties.at(0); + if (propertyName == MAYA_UV_SCALE || + propertyName == MAYA_UV_OFFSET) { + materialChild.children.removeAt(i); + --i; + } + } + } + } + + object++; } else { object++; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index d2d330167d..6d1b9d83d2 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -59,8 +59,8 @@ namespace scriptable { * @property {string} occlusionMap * @property {string} lightmapMap * @property {string} scatteringMap - * @property {string} texCoordTransform0 - * @property {string} texCoordTransform1 + * @property {Mat4|string} texCoordTransform0 + * @property {Mat4|string} texCoordTransform1 * @property {string} lightmapParams * @property {string} materialParams * @property {boolean} defaultFallthrough @@ -93,6 +93,7 @@ namespace scriptable { QString occlusionMap; QString lightmapMap; QString scatteringMap; + std::array texCoordTransforms; bool defaultFallthrough; std::unordered_map propertyFallthroughs; // not actually exposed to script diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 3bd4af601c..4182b52309 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -470,9 +470,13 @@ namespace scriptable { // These need to be implemented, but set the fallthrough for now if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM0)) { obj.setProperty("texCoordTransform0", FALLTHROUGH); + } else { + obj.setProperty("texCoordTransform0", mat4toScriptValue(engine, material.texCoordTransforms[0])); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM1)) { obj.setProperty("texCoordTransform1", FALLTHROUGH); + } else { + obj.setProperty("texCoordTransform1", mat4toScriptValue(engine, material.texCoordTransforms[1])); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) { obj.setProperty("lightmapParams", FALLTHROUGH); diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index caee4ceb2a..8825a26bfe 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -119,6 +119,10 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint if (map && map->getTextureSource()) { scatteringMap = map->getTextureSource()->getUrl().toString(); } + + for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) { + texCoordTransforms[i] = material->getTexCoordTransform(i); + } } } diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index 80b247bed0..330feaa61c 100755 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -324,6 +324,7 @@ public: void setModel(const std::string& model) { _model = model; } glm::mat4 getTexCoordTransform(uint i) const { return _texcoordTransforms[i]; } + void setTexCoordTransform(uint i, const glm::mat4& mat4) { _texcoordTransforms[i] = mat4; } glm::vec2 getLightmapParams() const { return _lightmapParams; } glm::vec2 getMaterialParams() const { return _materialParams; } diff --git a/libraries/material-networking/src/material-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp index 5a5f4ab54b..9d6fc06d01 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.cpp +++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp @@ -177,6 +177,9 @@ std::pair> NetworkMaterialResource material->setModel(modelString); } + std::array hasTexcoordTransform; + std::array texcoordTransforms; + if (modelString == HIFI_PBR) { const QString FALLTHROUGH("fallthrough"); for (auto& key : materialJSON.keys()) { @@ -372,8 +375,12 @@ std::pair> NetworkMaterialResource if (valueString == FALLTHROUGH) { material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::TEXCOORDTRANSFORM0); } + } else if (value.isObject()) { + auto valueVariant = value.toVariant(); + glm::mat4 transform = mat4FromVariant(valueVariant); + hasTexcoordTransform[0] = true; + texcoordTransforms[0] = transform; } - // TODO: implement texCoordTransform0 } else if (key == "texCoordTransform1") { auto value = materialJSON.value(key); if (value.isString()) { @@ -381,8 +388,12 @@ std::pair> NetworkMaterialResource if (valueString == FALLTHROUGH) { material->setPropertyDoesFallthrough(graphics::Material::ExtraFlagBit::TEXCOORDTRANSFORM1); } + } else if (value.isObject()) { + auto valueVariant = value.toVariant(); + glm::mat4 transform = mat4FromVariant(valueVariant); + hasTexcoordTransform[1] = true; + texcoordTransforms[1] = transform; } - // TODO: implement texCoordTransform1 } else if (key == "lightmapParams") { auto value = materialJSON.value(key); if (value.isString()) { @@ -409,6 +420,14 @@ std::pair> NetworkMaterialResource } } } + + // Do this after the texture maps are defined, so it overrides the default transforms + for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) { + if (hasTexcoordTransform[i]) { + material->setTexCoordTransform(i, texcoordTransforms[i]); + } + } + return std::pair>(name, material); } diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 597e537d8d..e453b7bff4 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -636,6 +636,79 @@ void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4) { mat4[3][3] = object.property("r3c3").toVariant().toFloat(); } +QVariant mat4ToVariant(const glm::mat4& mat4) { + if (mat4 != mat4) { + // NaN + return QVariant(); + } + QVariantMap object; + + object["r0c0"] = mat4[0][0]; + object["r1c0"] = mat4[0][1]; + object["r2c0"] = mat4[0][2]; + object["r3c0"] = mat4[0][3]; + object["r0c1"] = mat4[1][0]; + object["r1c1"] = mat4[1][1]; + object["r2c1"] = mat4[1][2]; + object["r3c1"] = mat4[1][3]; + object["r0c2"] = mat4[2][0]; + object["r1c2"] = mat4[2][1]; + object["r2c2"] = mat4[2][2]; + object["r3c2"] = mat4[2][3]; + object["r0c3"] = mat4[3][0]; + object["r1c3"] = mat4[3][1]; + object["r2c3"] = mat4[3][2]; + object["r3c3"] = mat4[3][3]; + + return object; +} + +glm::mat4 mat4FromVariant(const QVariant& object, bool& valid) { + glm::mat4 mat4; + valid = false; + if (!object.isValid() || object.isNull()) { + return mat4; + } else { + const static auto getElement = [](const QVariantMap& map, const char * key, float& value, bool& everyConversionValid) { + auto variantValue = map[key]; + if (variantValue.canConvert()) { + value = variantValue.toFloat(); + } else { + everyConversionValid = false; + } + }; + + auto map = object.toMap(); + bool everyConversionValid = true; + + getElement(map, "r0c0", mat4[0][0], everyConversionValid); + getElement(map, "r1c0", mat4[0][1], everyConversionValid); + getElement(map, "r2c0", mat4[0][2], everyConversionValid); + getElement(map, "r3c0", mat4[0][3], everyConversionValid); + getElement(map, "r0c1", mat4[1][0], everyConversionValid); + getElement(map, "r1c1", mat4[1][1], everyConversionValid); + getElement(map, "r2c1", mat4[1][2], everyConversionValid); + getElement(map, "r3c1", mat4[1][3], everyConversionValid); + getElement(map, "r0c2", mat4[2][0], everyConversionValid); + getElement(map, "r1c2", mat4[2][1], everyConversionValid); + getElement(map, "r2c2", mat4[2][2], everyConversionValid); + getElement(map, "r3c2", mat4[2][3], everyConversionValid); + getElement(map, "r0c3", mat4[3][0], everyConversionValid); + getElement(map, "r1c3", mat4[3][1], everyConversionValid); + getElement(map, "r2c3", mat4[3][2], everyConversionValid); + getElement(map, "r3c3", mat4[3][3], everyConversionValid); + + if (everyConversionValid) { + valid = true; + } + } +} + +glm::mat4 mat4FromVariant(const QVariant& object) { + bool valid = false; + return mat4FromVariant(object, valid); +} + QScriptValue qVectorVec3ColorToScriptValue(QScriptEngine* engine, const QVector& vector) { QScriptValue array = engine->newArray(); for (int i = 0; i < vector.size(); i++) { diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index ea2c5b8354..96c64f7384 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -67,6 +67,10 @@ void registerMetaTypes(QScriptEngine* engine); QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4); void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4); +QVariant mat4ToVariant(const glm::mat4& mat4); +glm::mat4 mat4FromVariant(const QVariant& object, bool& valid); +glm::mat4 mat4FromVariant(const QVariant& object); + /**jsdoc * A 2-dimensional vector. * From 9cb5ed888a1b4a76a8880de889b33d2093857ba6 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 18 Apr 2019 17:40:56 -0700 Subject: [PATCH 54/85] Remove unnecessary hasTexcoordTransform --- .../src/material-networking/MaterialCache.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/material-networking/src/material-networking/MaterialCache.cpp b/libraries/material-networking/src/material-networking/MaterialCache.cpp index 9d6fc06d01..745504fb3d 100644 --- a/libraries/material-networking/src/material-networking/MaterialCache.cpp +++ b/libraries/material-networking/src/material-networking/MaterialCache.cpp @@ -177,7 +177,6 @@ std::pair> NetworkMaterialResource material->setModel(modelString); } - std::array hasTexcoordTransform; std::array texcoordTransforms; if (modelString == HIFI_PBR) { @@ -378,7 +377,6 @@ std::pair> NetworkMaterialResource } else if (value.isObject()) { auto valueVariant = value.toVariant(); glm::mat4 transform = mat4FromVariant(valueVariant); - hasTexcoordTransform[0] = true; texcoordTransforms[0] = transform; } } else if (key == "texCoordTransform1") { @@ -391,7 +389,6 @@ std::pair> NetworkMaterialResource } else if (value.isObject()) { auto valueVariant = value.toVariant(); glm::mat4 transform = mat4FromVariant(valueVariant); - hasTexcoordTransform[1] = true; texcoordTransforms[1] = transform; } } else if (key == "lightmapParams") { @@ -423,8 +420,9 @@ std::pair> NetworkMaterialResource // Do this after the texture maps are defined, so it overrides the default transforms for (int i = 0; i < graphics::Material::NUM_TEXCOORD_TRANSFORMS; i++) { - if (hasTexcoordTransform[i]) { - material->setTexCoordTransform(i, texcoordTransforms[i]); + mat4 newTransform = texcoordTransforms[i]; + if (newTransform != mat4() || newTransform != material->getTexCoordTransform(i)) { + material->setTexCoordTransform(i, newTransform); } } From 74b4b47a2a81c7348038a2f994bc361355ab52d7 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 18 Apr 2019 17:43:03 -0700 Subject: [PATCH 55/85] Save some bandwidth by not including unity texture transforms in material jsons --- .../src/graphics-scripting/GraphicsScriptingInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 4182b52309..eb4bfa197c 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -470,12 +470,12 @@ namespace scriptable { // These need to be implemented, but set the fallthrough for now if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM0)) { obj.setProperty("texCoordTransform0", FALLTHROUGH); - } else { + } else if (material.texCoordTransforms[0] != mat4()) { obj.setProperty("texCoordTransform0", mat4toScriptValue(engine, material.texCoordTransforms[0])); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::TEXCOORDTRANSFORM1)) { obj.setProperty("texCoordTransform1", FALLTHROUGH); - } else { + } else if (material.texCoordTransforms[1] != mat4()) { obj.setProperty("texCoordTransform1", mat4toScriptValue(engine, material.texCoordTransforms[1])); } if (hasPropertyFallthroughs && material.propertyFallthroughs.at(graphics::Material::LIGHTMAP_PARAMS)) { From 05a2b11c549c3c036b6776dca74aa5a5aeadae60 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 19 Apr 2019 09:00:28 -0700 Subject: [PATCH 56/85] Fix mat4FromVariant --- libraries/shared/src/RegisteredMetaTypes.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index e453b7bff4..98b67d3f75 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -701,6 +701,8 @@ glm::mat4 mat4FromVariant(const QVariant& object, bool& valid) { if (everyConversionValid) { valid = true; } + + return mat4; } } From 080cb36170036b2a51750b875956788ea94b15d3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 19 Apr 2019 12:12:22 -0700 Subject: [PATCH 57/85] bake existing materialMap --- libraries/baking/src/MaterialBaker.cpp | 4 ++ libraries/baking/src/MaterialBaker.h | 3 + libraries/baking/src/ModelBaker.cpp | 89 +++++++++++++++++++------- libraries/baking/src/ModelBaker.h | 5 ++ 4 files changed, 78 insertions(+), 23 deletions(-) diff --git a/libraries/baking/src/MaterialBaker.cpp b/libraries/baking/src/MaterialBaker.cpp index 79e31014fd..85e203a2cb 100644 --- a/libraries/baking/src/MaterialBaker.cpp +++ b/libraries/baking/src/MaterialBaker.cpp @@ -276,4 +276,8 @@ void MaterialBaker::setMaterials(const QHash& materials, addTexture(material.name, image::TextureUsage::SCATTERING_TEXTURE, material.scatteringTexture); addTexture(material.name, image::TextureUsage::LIGHTMAP_TEXTURE, material.lightmapTexture); } +} + +void MaterialBaker::setMaterials(const NetworkMaterialResourcePointer& materialResource) { + _materialResource = materialResource; } \ No newline at end of file diff --git a/libraries/baking/src/MaterialBaker.h b/libraries/baking/src/MaterialBaker.h index 7a7411142e..9469466cb9 100644 --- a/libraries/baking/src/MaterialBaker.h +++ b/libraries/baking/src/MaterialBaker.h @@ -31,6 +31,9 @@ public: QString getBakedMaterialData() const { return _bakedMaterialData; } void setMaterials(const QHash& materials, const QString& baseURL); + void setMaterials(const NetworkMaterialResourcePointer& materialResource); + + NetworkMaterialResourcePointer getNetworkMaterialResource() const { return _materialResource; } static void setNextOvenWorkerThreadOperator(std::function getNextOvenWorkerThreadOperator) { _getNextOvenWorkerThreadOperator = getNextOvenWorkerThreadOperator; } diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index c15def18fc..9de32158c5 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -241,9 +241,6 @@ void ModelBaker::bakeSourceCopy() { config->getJobConfig("BuildDracoMesh")->setEnabled(true); // Do not permit potentially lossy modification of joint data meant for runtime ((PrepareJointsConfig*)config->getJobConfig("PrepareJoints"))->passthrough = true; - // The resources parsed from this job will not be used for now - // TODO: Proper full baking of all materials for a model - config->getJobConfig("ParseMaterialMapping")->setEnabled(false); // Begin hfm baking baker.run(); @@ -261,7 +258,7 @@ void ModelBaker::bakeSourceCopy() { return; } - if (_hfmModel->materials.size() > 0) { + if (!_hfmModel->materials.isEmpty()) { _materialBaker = QSharedPointer( new MaterialBaker(_modelURL.fileName(), true, _bakedOutputDir), &MaterialBaker::deleteLater @@ -270,7 +267,7 @@ void ModelBaker::bakeSourceCopy() { connect(_materialBaker.data(), &MaterialBaker::finished, this, &ModelBaker::handleFinishedMaterialBaker); _materialBaker->bake(); } else { - outputBakedFST(); + bakeMaterialMap(); } } @@ -286,26 +283,14 @@ void ModelBaker::handleFinishedMaterialBaker() { auto baseName = relativeBakedMaterialURL.left(relativeBakedMaterialURL.lastIndexOf('.')); relativeBakedMaterialURL = baseName + BAKED_MATERIAL_EXTENSION; - // First we add the materials in the model - QJsonArray materialMapping; - for (auto material : _hfmModel->materials) { - QJsonObject json; - json["mat::" + material.name] = relativeBakedMaterialURL + "#" + material.name; - materialMapping.push_back(json); - } - - // The we add any existing mappings from the mapping - if (_mapping.contains(MATERIAL_MAPPING_FIELD)) { - QByteArray materialMapValue = _mapping[MATERIAL_MAPPING_FIELD].toByteArray(); - QJsonObject oldMaterialMapping = QJsonDocument::fromJson(materialMapValue).object(); - for (auto key : oldMaterialMapping.keys()) { + auto materialResource = baker->getNetworkMaterialResource(); + if (materialResource) { + for (auto materialName : materialResource->parsedMaterials.names) { QJsonObject json; - json[key] = oldMaterialMapping[key]; - materialMapping.push_back(json); + json[QString("mat::" + QString(materialName.c_str()))] = relativeBakedMaterialURL + "#" + materialName.c_str(); + _materialMappingJSON.push_back(json); } } - - _mapping[MATERIAL_MAPPING_FIELD] = QJsonDocument(materialMapping).toJson(QJsonDocument::Compact); } else { // this material failed to bake - this doesn't fail the entire bake but we need to add the errors from // the material to our warnings @@ -315,7 +300,62 @@ void ModelBaker::handleFinishedMaterialBaker() { handleWarning("Failed to bake the materials for model with URL " + _modelURL.toString()); } - outputBakedFST(); + bakeMaterialMap(); +} + +void ModelBaker::bakeMaterialMap() { + if (!_materialMapping.empty()) { + // TODO: The existing material map must be baked in order, so we do it all on this thread to preserve the order. + // It could be spread over multiple threads if we had a good way of preserving the order once all of the bakers are done + _materialBaker = QSharedPointer( + new MaterialBaker("materialMap" + QString::number(_materialMapIndex++), true, _bakedOutputDir), + &MaterialBaker::deleteLater + ); + _materialBaker->setMaterials(_materialMapping.front().second); + connect(_materialBaker.data(), &MaterialBaker::finished, this, &ModelBaker::handleFinishedMaterialMapBaker); + _materialBaker->bake(); + } else { + outputBakedFST(); + } +} + +void ModelBaker::handleFinishedMaterialMapBaker() { + auto baker = qobject_cast(sender()); + + if (baker) { + if (!baker->hasErrors()) { + // this MaterialBaker is done and everything went according to plan + qCDebug(model_baking) << "Adding baked material to FST mapping " << baker->getBakedMaterialData(); + + QString materialName; + { + auto materialResource = baker->getNetworkMaterialResource(); + if (materialResource) { + auto url = materialResource->getURL(); + if (!url.isEmpty()) { + QString urlString = url.toDisplayString(); + auto index = urlString.lastIndexOf("#"); + if (index != -1) { + materialName = urlString.right(urlString.length() - index); + } + } + } + } + + QJsonObject json; + json[QString(_materialMapping.front().first.c_str())] = baker->getMaterialData() + BAKED_MATERIAL_EXTENSION + materialName; + _materialMappingJSON.push_back(json); + } else { + // this material failed to bake - this doesn't fail the entire bake but we need to add the errors from + // the material to our warnings + _warningList << baker->getWarnings(); + } + } else { + handleWarning("Failed to bake the materialMap for model with URL " + _modelURL.toString() + " and mapping target " + _materialMapping.front().first.c_str()); + } + + _materialMapping.erase(_materialMapping.begin()); + bakeMaterialMap(); } void ModelBaker::outputUnbakedFST() { @@ -364,6 +404,9 @@ void ModelBaker::outputBakedFST() { outputMapping[FILENAME_FIELD] = _bakedModelURL.fileName(); outputMapping.remove(TEXDIR_FIELD); outputMapping.remove(COMMENT_FIELD); + if (!_materialMappingJSON.isEmpty()) { + outputMapping[MATERIAL_MAPPING_FIELD] = QJsonDocument(_materialMappingJSON).toJson(QJsonDocument::Compact); + } hifi::ByteArray fstOut = FSTReader::writeMapping(outputMapping); QFile fstOutputFile { outputFSTURL }; diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h index da42871d28..b98d9716e1 100644 --- a/libraries/baking/src/ModelBaker.h +++ b/libraries/baking/src/ModelBaker.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "Baker.h" #include "MaterialBaker.h" @@ -80,15 +81,19 @@ protected slots: void handleModelNetworkReply(); virtual void bakeSourceCopy(); void handleFinishedMaterialBaker(); + void handleFinishedMaterialMapBaker(); private: void outputUnbakedFST(); void outputBakedFST(); + void bakeMaterialMap(); bool _hasBeenBaked { false }; hfm::Model::Pointer _hfmModel; MaterialMapping _materialMapping; + int _materialMapIndex { 0 }; + QJsonArray _materialMappingJSON; QSharedPointer _materialBaker; }; From 65f4fac99304217eee4adabbf3df063b88194f89 Mon Sep 17 00:00:00 2001 From: raveenajain Date: Fri, 19 Apr 2019 21:40:57 +0100 Subject: [PATCH 58/85] move debug code --- libraries/fbx/src/GLTFSerializer.cpp | 40 ++++++++++++++++++++++++---- libraries/fbx/src/GLTFSerializer.h | 1 + 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index a4d18fb111..fe8bd3167f 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -909,7 +909,6 @@ bool GLTFSerializer::buildGeometry(HFMModel& hfmModel, const hifi::URL& url) { auto& node = _file.nodes[nodeIndex]; if (node.defined["mesh"]) { - qCDebug(modelformat) << "node_transforms" << node.transforms; foreach(auto &primitive, _file.meshes[node.mesh].primitives) { hfmModel.meshes.append(HFMMesh()); HFMMesh& mesh = hfmModel.meshes[hfmModel.meshes.size() - 1]; @@ -1276,7 +1275,6 @@ HFMTexture GLTFSerializer::getHFMTexture(const GLTFTexture& texture) { QString fname = hifi::URL(url).fileName(); hifi::URL textureUrl = _url.resolved(url); - qCDebug(modelformat) << "fname: " << fname; fbxtex.name = fname; fbxtex.filename = textureUrl.toEncoded(); @@ -1371,10 +1369,7 @@ bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int c blobstream.setVersion(QDataStream::Qt_5_9); blobstream.setFloatingPointPrecision(QDataStream::FloatingPointPrecision::SinglePrecision); - qCDebug(modelformat) << "size1: " << count; int dataskipped = blobstream.skipRawData(byteOffset); - qCDebug(modelformat) << "dataskipped: " << dataskipped; - int bufferCount = 0; switch (accessorType) { case GLTFAccessorType::SCALAR: @@ -1467,6 +1462,38 @@ void GLTFSerializer::retriangulate(const QVector& inIndices, const QVector< } } +void GLTFSerializer::glTFDebugDump() { + qCDebug(modelformat) << "---------------- Nodes ----------------"; + for (GLTFNode node : _file.nodes) { + if (node.defined["mesh"]) { + qCDebug(modelformat) << "\n"; + qCDebug(modelformat) << " node_transforms" << node.transforms; + qCDebug(modelformat) << "\n"; + } + } + + qCDebug(modelformat) << "---------------- Accessors ----------------"; + for (GLTFAccessor accessor : _file.accessors) { + qCDebug(modelformat) << "\n"; + qCDebug(modelformat) << "count: " << accessor.count; + qCDebug(modelformat) << "byteOffset: " << accessor.byteOffset; + qCDebug(modelformat) << "\n"; + } + + qCDebug(modelformat) << "---------------- Textures ----------------"; + for (GLTFTexture texture : _file.textures) { + if (texture.defined["source"]) { + qCDebug(modelformat) << "\n"; + QString url = _file.images[texture.source].uri; + QString fname = hifi::URL(url).fileName(); + qCDebug(modelformat) << "fname: " << fname; + qCDebug(modelformat) << "\n"; + } + } + + qCDebug(modelformat) << "\n"; +} + void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) { qCDebug(modelformat) << "---------------- hfmModel ----------------"; qCDebug(modelformat) << " hasSkeletonJoints =" << hfmModel.hasSkeletonJoints; @@ -1607,5 +1634,8 @@ void GLTFSerializer::hfmDebugDump(const HFMModel& hfmModel) { qCDebug(modelformat) << "\n"; } + qCDebug(modelformat) << "---------------- GLTF Model ----------------"; + glTFDebugDump(); + qCDebug(modelformat) << "\n"; } diff --git a/libraries/fbx/src/GLTFSerializer.h b/libraries/fbx/src/GLTFSerializer.h index a9e7152e5b..8784667d1e 100755 --- a/libraries/fbx/src/GLTFSerializer.h +++ b/libraries/fbx/src/GLTFSerializer.h @@ -784,6 +784,7 @@ private: void setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& material); HFMTexture getHFMTexture(const GLTFTexture& texture); + void glTFDebugDump(); void hfmDebugDump(const HFMModel& hfmModel); }; From f2312f77c1936d8d89768178f7972e900a3b8f04 Mon Sep 17 00:00:00 2001 From: raveenajain Date: Fri, 19 Apr 2019 22:47:47 +0100 Subject: [PATCH 59/85] remove unused var --- libraries/fbx/src/GLTFSerializer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index fe8bd3167f..0264accdc3 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -1368,8 +1368,8 @@ bool GLTFSerializer::readArray(const hifi::ByteArray& bin, int byteOffset, int c blobstream.setByteOrder(QDataStream::LittleEndian); blobstream.setVersion(QDataStream::Qt_5_9); blobstream.setFloatingPointPrecision(QDataStream::FloatingPointPrecision::SinglePrecision); + blobstream.skipRawData(byteOffset); - int dataskipped = blobstream.skipRawData(byteOffset); int bufferCount = 0; switch (accessorType) { case GLTFAccessorType::SCALAR: From 15e4df2b6124e1f9ab97cfa38aea2ff3133fae2c Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 19 Apr 2019 09:32:42 -0700 Subject: [PATCH 60/85] Fix domain baker not finishing (threading, material key) This addresses 2 cases where the domain baker would not finish 1. For material baking, we were storing a value in _entitiesNeedingRewrite with a different key than what we use when we later remove it from _entitiesNeedingRewrite 2. The domain baker runs on a worker thread. When starting a baker for an item in the domain, we do an invokeMethod, which will happen synchronously if the baker ends up on the same thread as the thread that the domain baker is on. This can cause issues if the baker finishes immediately. The case I saw was a local model url that immediately failed, and finished before return from the invokeMethod in the domain baker . --- tools/oven/src/DomainBaker.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index b6adee28dc..f2abf17854 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -171,7 +171,7 @@ void DomainBaker::addModelBaker(const QString& property, const QString& url, con // move the baker to the baker thread // and kickoff the bake baker->moveToThread(Oven::instance().getNextWorkerThread()); - QMetaObject::invokeMethod(baker.data(), "bake"); + QMetaObject::invokeMethod(baker.data(), "bake", Qt::QueuedConnection); // keep track of the total number of baking entities ++_totalNumberOfSubBakes; @@ -212,7 +212,7 @@ void DomainBaker::addTextureBaker(const QString& property, const QString& url, i // move the baker to a worker thread and kickoff the bake textureBaker->moveToThread(Oven::instance().getNextWorkerThread()); - QMetaObject::invokeMethod(textureBaker.data(), "bake"); + QMetaObject::invokeMethod(textureBaker.data(), "bake", Qt::QueuedConnection); // keep track of the total number of baking entities ++_totalNumberOfSubBakes; @@ -247,7 +247,7 @@ void DomainBaker::addScriptBaker(const QString& property, const QString& url, co // move the baker to a worker thread and kickoff the bake scriptBaker->moveToThread(Oven::instance().getNextWorkerThread()); - QMetaObject::invokeMethod(scriptBaker.data(), "bake"); + QMetaObject::invokeMethod(scriptBaker.data(), "bake", Qt::QueuedConnection); // keep track of the total number of baking entities ++_totalNumberOfSubBakes; @@ -272,7 +272,7 @@ void DomainBaker::addMaterialBaker(const QString& property, const QString& data, // setup a baker for this material QSharedPointer materialBaker { - new MaterialBaker(data, isURL, _contentOutputPath, destinationPath), + new MaterialBaker(materialData, isURL, _contentOutputPath, destinationPath), &MaterialBaker::deleteLater }; @@ -284,7 +284,7 @@ void DomainBaker::addMaterialBaker(const QString& property, const QString& data, // move the baker to a worker thread and kickoff the bake materialBaker->moveToThread(Oven::instance().getNextWorkerThread()); - QMetaObject::invokeMethod(materialBaker.data(), "bake"); + QMetaObject::invokeMethod(materialBaker.data(), "bake", Qt::QueuedConnection); // keep track of the total number of baking entities ++_totalNumberOfSubBakes; From ba02aa7098324996ec877a3caa3108d7c1537675 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 19 Apr 2019 16:56:55 -0700 Subject: [PATCH 61/85] making luci2 better --- .../utilities/lib/jet/qml/TaskPropView.qml | 12 ++- .../utilities/render/debugCulling.js | 2 +- scripts/developer/utilities/render/luci.js | 85 ++---------------- scripts/developer/utilities/render/luci.qml | 25 ++++++ .../render/{culling.qml => luci/Culling.qml} | 21 +++-- .../developer/utilities/render/luci/Page.js | 90 +++++++++++++++++++ .../developer/utilities/render/luci/qmldir | 3 +- scripts/developer/utilities/render/luci2.js | 87 ++++++++++++++++-- .../utilities/render/materialInspector.js | 8 +- 9 files changed, 236 insertions(+), 97 deletions(-) rename scripts/developer/utilities/render/{culling.qml => luci/Culling.qml} (86%) create mode 100644 scripts/developer/utilities/render/luci/Page.js diff --git a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml index 0587352b4e..165edcf2e1 100644 --- a/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml +++ b/scripts/developer/utilities/lib/jet/qml/TaskPropView.qml @@ -31,7 +31,15 @@ Prop.PropGroup { property var showProps: true property var showSubs: true property var jobEnabled: rootConfig.getConfig(jobPath).enabled - property var jobCpuTime: rootConfig.getConfig(jobPath).cpuRunTime.toPrecision(3) + property var jobCpuTime: pullCpuTime() + + function pullCpuTime() { + if (jobEnabled) { + return rootConfig.getConfig(jobPath).cpuRunTime.toPrecision(3); + } else { + return '.' + } + } property var toggleJobActivation: function() { console.log("the button has been pressed and jobEnabled is " + jobEnabled ) @@ -52,7 +60,7 @@ Prop.PropGroup { } Prop.PropLabel { id: cpuTime - visible: root.jobEnabled + visible: false // root.jobEnabled width: 50 text: jobCpuTime horizontalAlignment: Text.AlignLeft diff --git a/scripts/developer/utilities/render/debugCulling.js b/scripts/developer/utilities/render/debugCulling.js index 788c7cb4a0..e7c68fd717 100644 --- a/scripts/developer/utilities/render/debugCulling.js +++ b/scripts/developer/utilities/render/debugCulling.js @@ -10,7 +10,7 @@ // // Set up the qml ui -var qml = Script.resolvePath('culling.qml'); +var qml = Script.resolvePath('luci/Culling.qml'); var window = new OverlayWindow({ title: 'Render Draws', source: qml, diff --git a/scripts/developer/utilities/render/luci.js b/scripts/developer/utilities/render/luci.js index bae5c4646d..fd84f55e65 100644 --- a/scripts/developer/utilities/render/luci.js +++ b/scripts/developer/utilities/render/luci.js @@ -10,10 +10,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + + (function() { var AppUi = Script.require('appUi'); var MaterialInspector = Script.require('./materialInspector.js'); + var Page = Script.require('./luci/Page.js'); var moveDebugCursor = false; var onMousePressEvent = function (e) { @@ -43,83 +46,12 @@ Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 }; } - function Page(title, qmlurl, width, height, handleWindowFunc) { - this.title = title; - this.qml = qmlurl; - this.width = width; - this.height = height; - this.handleWindowFunc = handleWindowFunc; - this.window; - - print("Page: New Page:" + JSON.stringify(this)); - } - - Page.prototype.killView = function () { - print("Page: Kill window for page:" + JSON.stringify(this)); - if (this.window) { - print("Page: Kill window for page:" + this.title); - //this.window.closed.disconnect(function () { - // this.killView(); - //}); - this.window.close(); - this.window = false; - } - }; - - Page.prototype.createView = function () { - var that = this; - if (!this.window) { - print("Page: New window for page:" + this.title); - this.window = Desktop.createWindow(Script.resolvePath(this.qml), { - title: this.title, - presentationMode: Desktop.PresentationMode.NATIVE, - size: {x: this.width, y: this.height} - }); - this.handleWindowFunc(this.window); - this.window.closed.connect(function () { - that.killView(); - this.handleWindowFunc(undefined); - }); - } - }; - - - var Pages = function () { - this._pages = {}; - }; - - Pages.prototype.addPage = function (command, title, qmlurl, width, height, handleWindowFunc) { - if (handleWindowFunc === undefined) { - // Workaround for bad linter - handleWindowFunc = function(window){}; - } - this._pages[command] = new Page(title, qmlurl, width, height, handleWindowFunc); - }; - - Pages.prototype.open = function (command) { - print("Pages: command = " + command); - if (!this._pages[command]) { - print("Pages: unknown command = " + command); - return; - } - this._pages[command].createView(); - }; - - Pages.prototype.clear = function () { - for (var p in this._pages) { - print("Pages: kill page: " + p); - this._pages[p].killView(); - delete this._pages[p]; - } - this._pages = {}; - }; var pages = new Pages(); - pages.addPage('openEngineView', 'Render Engine', 'engineInspector.qml', 300, 400); - pages.addPage('openEngineLODView', 'Render LOD', 'lod.qml', 300, 400); - pages.addPage('openCullInspectorView', 'Cull Inspector', 'culling.qml', 300, 400); - pages.addPage('openMaterialInspectorView', 'Material Inspector', 'materialInspector.qml', 300, 400, MaterialInspector.setWindow); + pages.addPage('openEngineLODView', 'Render LOD', '../lod.qml', 300, 400); + pages.addPage('openCullInspectorView', 'Cull Inspector', '../luci/Culling.qml', 300, 400); + pages.addPage('openMaterialInspectorView', 'Material Inspector', '../materialInspector.qml', 300, 400, MaterialInspector.setWindow); function fromQml(message) { if (pages.open(message.method)) { @@ -132,7 +64,7 @@ ui = new AppUi({ buttonName: "LUCI", home: Script.resolvePath("deferredLighting.qml"), - additionalAppScreens: Script.resolvePath("engineInspector.qml"), + additionalAppScreens : Script.resolvePath("engineInspector.qml"), onMessage: fromQml, normalButton: Script.resolvePath("../../../system/assets/images/luci-i.svg"), activeButton: Script.resolvePath("../../../system/assets/images/luci-a.svg") @@ -144,8 +76,5 @@ Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent); Controller.mouseMoveEvent.disconnect(onMouseMoveEvent); pages.clear(); - // killEngineInspectorView(); - // killCullInspectorView(); - // killEngineLODWindow(); }); }()); diff --git a/scripts/developer/utilities/render/luci.qml b/scripts/developer/utilities/render/luci.qml index 6ceee9a8d0..a904ec52fc 100644 --- a/scripts/developer/utilities/render/luci.qml +++ b/scripts/developer/utilities/render/luci.qml @@ -62,6 +62,31 @@ Rectangle { Antialiasing {} } } + Prop.PropFolderPanel { + label: "Culling" + panelFrameData: Component { + Culling {} + } + } + Prop.PropFolderPanel { + label: "Tools" + panelFrameData: Component { + Row { + HifiControls.Button { + text: "LOD" + onClicked: { + sendToScript({method: "openEngineLODView"}); + } + } + HifiControls.Button { + text: "Material" + onClicked: { + sendToScript({method: "openMaterialInspectorView"}); + } + } + } + } + } Jet.TaskPropView { id: "le" jobPath: "" diff --git a/scripts/developer/utilities/render/culling.qml b/scripts/developer/utilities/render/luci/Culling.qml similarity index 86% rename from scripts/developer/utilities/render/culling.qml rename to scripts/developer/utilities/render/luci/Culling.qml index 801cb5b573..d881ddf7a6 100644 --- a/scripts/developer/utilities/render/culling.qml +++ b/scripts/developer/utilities/render/luci/Culling.qml @@ -9,11 +9,16 @@ // import QtQuick 2.5 import QtQuick.Controls 1.4 -import "configSlider" + +import "../../lib/prop" as Prop Column { id: root spacing: 8 + + anchors.left: parent.left; + anchors.right: parent.right; + property var sceneOctree: Render.getConfig("RenderMainView.DrawSceneOctree"); property var itemSelection: Render.getConfig("RenderMainView.DrawItemSelection"); @@ -36,6 +41,10 @@ Column { GroupBox { title: "Culling" + + anchors.left: parent.left; + anchors.right: parent.right; + Row { spacing: 8 Column { @@ -91,6 +100,7 @@ Column { } } } + } GroupBox { @@ -103,13 +113,14 @@ Column { anchors.right: parent.right; Repeater { model: [ "Opaque:RenderMainView.DrawOpaqueDeferred", "Transparent:RenderMainView.DrawTransparentDeferred", "Light:RenderMainView.DrawLight", - "Opaque Overlays:RenderMainView.DrawOverlay3DOpaque", "Transparent Overlays:RenderMainView.DrawOverlay3DTransparent" ] - ConfigSlider { + "Opaque InFront:RenderMainView.DrawInFrontOpaque", "Transparent InFront:RenderMainView.DrawInFrontTransparent", + "Opaque HUD:RenderMainView.DrawHUDOpaque", "Transparent HUD:RenderMainView.DrawHUDTransparent" ] + Prop.PropScalar { label: qsTr(modelData.split(":")[0]) integral: true - config: Render.getConfig(modelData.split(":")[1]) + object: Render.getConfig(modelData.split(":")[1]) property: "maxDrawn" - max: config.numDrawn + max: object.numDrawn min: -1 } } diff --git a/scripts/developer/utilities/render/luci/Page.js b/scripts/developer/utilities/render/luci/Page.js new file mode 100644 index 0000000000..06c9704abf --- /dev/null +++ b/scripts/developer/utilities/render/luci/Page.js @@ -0,0 +1,90 @@ +// +// Page.js +// +// Sam Gateau, created on 4/19/2019 +// Copyright 2019 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 +// +"use strict"; + +(function() { +function Page(title, qmlurl, width, height, onViewCreated, onViewClosed) { + this.title = title; + this.qml = qmlurl; + this.width = width; + this.height = height; + this.onViewCreated = onViewCreated; + this.onViewClosed = onViewClosed; + + this.window; + + print("Page: New Page:" + JSON.stringify(this)); +} + +Page.prototype.killView = function () { + print("Page: Kill window for page:" + JSON.stringify(this)); + if (this.window) { + print("Page: Kill window for page:" + this.title); + //this.window.closed.disconnect(function () { + // this.killView(); + //}); + this.window.close(); + this.window = false; + } +}; + +Page.prototype.createView = function () { + var that = this; + if (!this.window) { + print("Page: New window for page:" + this.title); + this.window = Desktop.createWindow(Script.resolvePath(this.qml), { + title: this.title, + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: this.width, y: this.height} + }); + this.onViewCreated(this.window); + this.window.closed.connect(function () { + that.killView(); + that.onViewClosed(); + }); + } +}; + + +Pages = function () { + this._pages = {}; +}; + +Pages.prototype.addPage = function (command, title, qmlurl, width, height, onViewCreated, onViewClosed) { + if (onViewCreated === undefined) { + // Workaround for bad linter + onViewCreated = function(window) {}; + } + if (onViewClosed === undefined) { + // Workaround for bad linter + onViewClosed = function() {}; + } + this._pages[command] = new Page(title, qmlurl, width, height, onViewCreated, onViewClosed); +}; + +Pages.prototype.open = function (command) { + print("Pages: command = " + command); + if (!this._pages[command]) { + print("Pages: unknown command = " + command); + return; + } + this._pages[command].createView(); +}; + +Pages.prototype.clear = function () { + for (var p in this._pages) { + print("Pages: kill page: " + p); + this._pages[p].killView(); + delete this._pages[p]; + } + this._pages = {}; +}; + +}()); diff --git a/scripts/developer/utilities/render/luci/qmldir b/scripts/developer/utilities/render/luci/qmldir index c88dfee714..7a7d6a8ca6 100644 --- a/scripts/developer/utilities/render/luci/qmldir +++ b/scripts/developer/utilities/render/luci/qmldir @@ -3,4 +3,5 @@ ShadingModel 1.0 ShadingModel.qml ToneMapping 1.0 ToneMapping.qml BoundingBoxes 1.0 BoundingBoxes.qml Framebuffer 1.0 Framebuffer.qml -Antialiasing 1.0 Antialiasing.qml \ No newline at end of file +Antialiasing 1.0 Antialiasing.qml +Culling 1.0 Culling.qml \ No newline at end of file diff --git a/scripts/developer/utilities/render/luci2.js b/scripts/developer/utilities/render/luci2.js index d6bb005795..a34cf88415 100644 --- a/scripts/developer/utilities/render/luci2.js +++ b/scripts/developer/utilities/render/luci2.js @@ -1,10 +1,83 @@ + + +var MaterialInspector = Script.require('./materialInspector.js'); +var Page = Script.require('./luci/Page.js'); + + function openView() { - // Set up the qml ui - var window = Desktop.createWindow(Script.resolvePath('luci.qml'), { - title: this.title, - presentationMode: Desktop.PresentationMode.NATIVE, - size: {x: 300, y: 400} - }); //window.closed.connect(function() { Script.stop(); }); + + + var pages = new Pages(); + function fromQml(message) { + if (pages.open(message.method)) { + return; + } + } + + var luciWindow + function openLuciWindow(window) { + if (luciWindow !== undefined) { + activeWindow.fromQml.disconnect(fromQml); + } + if (window !== undefined) { + window.fromQml.connect(fromQml); + } + luciWindow = window; + + + var moveDebugCursor = false; + var onMousePressEvent = function (e) { + if (e.isMiddleButton) { + moveDebugCursor = true; + setDebugCursor(e.x, e.y); + } + }; + Controller.mousePressEvent.connect(onMousePressEvent); + + var onMouseReleaseEvent = function () { + moveDebugCursor = false; + }; + Controller.mouseReleaseEvent.connect(onMouseReleaseEvent); + + var onMouseMoveEvent = function (e) { + if (moveDebugCursor) { + setDebugCursor(e.x, e.y); + } + }; + Controller.mouseMoveEvent.connect(onMouseMoveEvent); + + function setDebugCursor(x, y) { + var nx = 2.0 * (x / Window.innerWidth) - 1.0; + var ny = 1.0 - 2.0 * ((y) / (Window.innerHeight)); + + Render.getConfig("RenderMainView").getConfig("DebugDeferredBuffer").size = { x: nx, y: ny, z: 1.0, w: 1.0 }; + } + + } + + function closeLuciWindow() { + if (luciWindow !== undefined) { + activeWindow.fromQml.disconnect(fromQml); + } + luciWindow = {}; + + Controller.mousePressEvent.disconnect(onMousePressEvent); + Controller.mouseReleaseEvent.disconnect(onMouseReleaseEvent); + Controller.mouseMoveEvent.disconnect(onMouseMoveEvent); + pages.clear(); + } + + pages.addPage('Luci', 'Luci', '../luci.qml', 300, 420, openLuciWindow, closeLuciWindow); + pages.addPage('openEngineLODView', 'Render LOD', '../lod.qml', 300, 400); + pages.addPage('openMaterialInspectorView', 'Material Inspector', '../materialInspector.qml', 300, 400, MaterialInspector.setWindow, MaterialInspector.setWindow); + + pages.open('Luci'); + + + return pages; } -openView(); \ No newline at end of file + + +openView(); + diff --git a/scripts/developer/utilities/render/materialInspector.js b/scripts/developer/utilities/render/materialInspector.js index 76e5da5cd0..98d9f769fb 100644 --- a/scripts/developer/utilities/render/materialInspector.js +++ b/scripts/developer/utilities/render/materialInspector.js @@ -109,7 +109,9 @@ function mouseReleaseEvent(event) { } function killWindow() { - setWindow(undefined); + activeWindow = undefined; + + // setWindow(undefined); } function toQml(message) { @@ -138,14 +140,14 @@ function setSelectedObject(id, type) { function setWindow(window) { if (activeWindow !== undefined) { setSelectedObject(Uuid.NULL, ""); - activeWindow.closed.disconnect(killWindow); + // activeWindow.closed.disconnect(killWindow); activeWindow.fromQml.disconnect(fromQml); Controller.mousePressEvent.disconnect(mousePressEvent); Controller.mouseReleaseEvent.disconnect(mouseReleaseEvent); activeWindow.close(); } if (window !== undefined) { - window.closed.connect(killWindow); + // window.closed.connect(killWindow); window.fromQml.connect(fromQml); Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent); From 5e251b10336314d13724d1b37c18b1c1b20500d8 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 19 Apr 2019 17:03:43 -0700 Subject: [PATCH 62/85] Don't double-free an openssl struct --- interface/src/commerce/Wallet.cpp | 604 +++++++++++++++--------------- 1 file changed, 303 insertions(+), 301 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 5644f9ea4c..e1e872ac24 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -41,326 +41,246 @@ #include "ui/SecurityImageProvider.h" #include "scripting/HMDScriptingInterface.h" -static const char* KEY_FILE = "hifikey"; -static const char* INSTRUCTIONS_FILE = "backup_instructions.html"; -static const char* IMAGE_HEADER = "-----BEGIN SECURITY IMAGE-----\n"; -static const char* IMAGE_FOOTER = "-----END SECURITY IMAGE-----\n"; +namespace { + const char* KEY_FILE = "hifikey"; + const char* INSTRUCTIONS_FILE = "backup_instructions.html"; + const char* IMAGE_HEADER = "-----BEGIN SECURITY IMAGE-----\n"; + const char* IMAGE_FOOTER = "-----END SECURITY IMAGE-----\n"; -void initialize() { - static bool initialized = false; - if (!initialized) { - SSL_load_error_strings(); - SSL_library_init(); - OpenSSL_add_all_algorithms(); - initialized = true; - } -} - -QString keyFilePath() { - auto accountManager = DependencyManager::get(); - return PathUtils::getAppDataFilePath(QString("%1.%2").arg(accountManager->getAccountInfo().getUsername(), KEY_FILE)); -} -bool Wallet::copyKeyFileFrom(const QString& pathname) { - QString existing = getKeyFilePath(); - qCDebug(commerce) << "Old keyfile" << existing; - if (!existing.isEmpty()) { - QString backup = QString(existing).insert(existing.indexOf(KEY_FILE) - 1, - QDateTime::currentDateTime().toString(Qt::ISODate).replace(":", "")); - qCDebug(commerce) << "Renaming old keyfile to" << backup; - if (!QFile::rename(existing, backup)) { - qCCritical(commerce) << "Unable to backup" << existing << "to" << backup; - return false; + void initialize() { + static bool initialized = false; + if (!initialized) { + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + initialized = true; } } - QString destination = keyFilePath(); - bool result = QFile::copy(pathname, destination); - qCDebug(commerce) << "copy" << pathname << "to" << destination << "=>" << result; - return result; -} -// use the cached _passphrase if it exists, otherwise we need to prompt -int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { - // just return a hardcoded pwd for now - auto wallet = DependencyManager::get(); - auto passphrase = wallet->getPassphrase(); - if (passphrase && !passphrase->isEmpty()) { - QString saltedPassphrase(*passphrase); - saltedPassphrase.append(wallet->getSalt()); - strcpy(password, saltedPassphrase.toUtf8().constData()); - return static_cast(passphrase->size()); - } else { - // this shouldn't happen - so lets log it to tell us we have - // a problem with the flow... - qCCritical(commerce) << "no cached passphrase while decrypting!"; - return 0; + QString keyFilePath() { + auto accountManager = DependencyManager::get(); + return PathUtils::getAppDataFilePath(QString("%1.%2").arg(accountManager->getAccountInfo().getUsername(), KEY_FILE)); } -} -EC_KEY* readKeys(QString filename) { - QFile file(filename); - EC_KEY* key = NULL; - if (file.open(QFile::ReadOnly)) { - // file opened successfully - qCDebug(commerce) << "opened key file" << filename; + // use the cached _passphrase if it exists, otherwise we need to prompt + int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { + // just return a hardcoded pwd for now + auto wallet = DependencyManager::get(); + auto passphrase = wallet->getPassphrase(); + if (passphrase && !passphrase->isEmpty()) { + QString saltedPassphrase(*passphrase); + saltedPassphrase.append(wallet->getSalt()); + strcpy(password, saltedPassphrase.toUtf8().constData()); + return static_cast(passphrase->size()); + } else { + // this shouldn't happen - so lets log it to tell us we have + // a problem with the flow... + qCCritical(commerce) << "no cached passphrase while decrypting!"; + return 0; + } + } - QByteArray pemKeyBytes = file.readAll(); - BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length()); - if ((key = PEM_read_bio_EC_PUBKEY(bufio, NULL, NULL, NULL))) { - // now read private key + EC_KEY* readKeys(QString filename) { + QFile file(filename); + EC_KEY* key = NULL; + if (file.open(QFile::ReadOnly)) { + // file opened successfully + qCDebug(commerce) << "opened key file" << filename; - qCDebug(commerce) << "read public key"; + QByteArray pemKeyBytes = file.readAll(); + BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length()); + if ((key = PEM_read_bio_EC_PUBKEY(bufio, NULL, NULL, NULL))) { + // now read private key - if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) { - qCDebug(commerce) << "read private key"; - BIO_free(bufio); - file.close(); + qCDebug(commerce) << "read public key"; + + if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) { + qCDebug(commerce) << "read private key"; + } else { + qCDebug(commerce) << "failed to read private key"; + } } else { - qCDebug(commerce) << "failed to read private key"; + qCDebug(commerce) << "failed to read public key"; } - } else { - qCDebug(commerce) << "failed to read public key"; - } - BIO_free(bufio); - file.close(); - } else { - qCDebug(commerce) << "failed to open key file" << filename; - } - return key; -} - -bool Wallet::writeBackupInstructions() { - QString inputFilename(PathUtils::resourcesPath() + "html/commerce/backup_instructions.html"); - QString outputFilename = PathUtils::getAppDataFilePath(INSTRUCTIONS_FILE); - QFile inputFile(inputFilename); - QFile outputFile(outputFilename); - bool retval = false; - - if (getKeyFilePath().isEmpty()) { - return false; - } - - if (QFile::exists(inputFilename) && inputFile.open(QIODevice::ReadOnly)) { - if (outputFile.open(QIODevice::ReadWrite)) { - // Read the data from the original file, then close it - QByteArray fileData = inputFile.readAll(); - inputFile.close(); - - // Translate the data from the original file into a QString - QString text(fileData); - - // Replace the necessary string - text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath()); - - // Write the new text back to the file - outputFile.write(text.toUtf8()); - - // Close the output file - outputFile.close(); - - retval = true; - qCDebug(commerce) << "wrote html file successfully"; - } else { - qCDebug(commerce) << "failed to open output html file" << outputFilename; - } - } else { - qCDebug(commerce) << "failed to open input html file" << inputFilename; - } - return retval; -} - -bool writeKeys(QString filename, EC_KEY* keys) { - BIO* bio = BIO_new(BIO_s_mem()); - bool retval = false; - if (!PEM_write_bio_EC_PUBKEY(bio, keys)) { - BIO_free(bio); - qCCritical(commerce) << "failed to write public key"; - return retval; - } - - if (!PEM_write_bio_ECPrivateKey(bio, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { - BIO_free(bio); - qCCritical(commerce) << "failed to write private key"; - return retval; - } - - QFile file(filename); - if (!file.open(QIODevice::WriteOnly)) { - const char* bio_data; - long bio_size = BIO_get_mem_data(bio, &bio_data); - - QByteArray keyBytes(bio_data, bio_size); - file.write(keyBytes); - retval = true; - qCDebug(commerce) << "wrote keys successfully"; - file.close(); - } else { - qCDebug(commerce) << "failed to open key file" << filename; - } - BIO_free(bio); - return retval; -} - -bool Wallet::setWallet(const QByteArray& wallet) { - QFile file(keyFilePath()); - if (!file.open(QIODevice::WriteOnly)) { - qCCritical(commerce) << "Unable to open wallet for write in" << keyFilePath(); - return false; - } - if (file.write(wallet) != wallet.count()) { - qCCritical(commerce) << "Unable to write wallet in" << keyFilePath(); - return false; - } - file.close(); - return true; -} -QByteArray Wallet::getWallet() { - QFile file(keyFilePath()); - if (!file.open(QIODevice::ReadOnly)) { - qCInfo(commerce) << "No existing wallet in" << keyFilePath(); - return QByteArray(); - } - QByteArray wallet = file.readAll(); - file.close(); - return wallet; -} - -QPair generateECKeypair() { - EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); - QPair retval{}; - - EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE); - if (!EC_KEY_generate_key(keyPair)) { - qCDebug(commerce) << "Error generating EC Keypair -" << ERR_get_error(); - return retval; - } - - // grab the public key and private key from the file - unsigned char* publicKeyDER = NULL; - int publicKeyLength = i2d_EC_PUBKEY(keyPair, &publicKeyDER); - - unsigned char* privateKeyDER = NULL; - int privateKeyLength = i2d_ECPrivateKey(keyPair, &privateKeyDER); - - if (publicKeyLength <= 0 || privateKeyLength <= 0) { - qCDebug(commerce) << "Error getting DER public or private key from EC struct -" << ERR_get_error(); - - // cleanup the EC struct - EC_KEY_free(keyPair); - - // cleanup the public and private key DER data, if required - if (publicKeyLength > 0) { - OPENSSL_free(publicKeyDER); - } - - if (privateKeyLength > 0) { - OPENSSL_free(privateKeyDER); - } - - return retval; - } - - if (!writeKeys(keyFilePath(), keyPair)) { - qCDebug(commerce) << "couldn't save keys!"; - return retval; - } - - EC_KEY_free(keyPair); - - // prepare the return values. TODO: Fix this - we probably don't really even want the - // private key at all (better to read it when we need it?). Or maybe we do, when we have - // multiple keys? - retval.first = new QByteArray(reinterpret_cast(publicKeyDER), publicKeyLength); - retval.second = new QByteArray(reinterpret_cast(privateKeyDER), privateKeyLength); - - // cleanup the publicKeyDER and publicKeyDER data - OPENSSL_free(publicKeyDER); - OPENSSL_free(privateKeyDER); - return retval; -} -// END copied code (which will soon change) - -// the public key can just go into a byte array -QByteArray readPublicKey(QString filename) { - QByteArray retval; - QFile file(filename); - if (file.open(QIODevice::ReadOnly)) { - // file opened successfully - qCDebug(commerce) << "opened key file" << filename; - - QByteArray pemKeyBytes = file.readAll(); - BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length()); - - EC_KEY* key = PEM_read_bio_EC_PUBKEY(bufio, NULL, NULL, NULL); - if (key) { - // file read successfully - unsigned char* publicKeyDER = NULL; - int publicKeyLength = i2d_EC_PUBKEY(key, &publicKeyDER); - // TODO: check for 0 length? - - // cleanup - EC_KEY_free(key); - - qCDebug(commerce) << "parsed public key file successfully"; - - QByteArray retval((char*)publicKeyDER, publicKeyLength); - OPENSSL_free(publicKeyDER); BIO_free(bufio); file.close(); + } else { + qCDebug(commerce) << "failed to open key file" << filename; + } + return key; + } + + bool writeKeys(QString filename, EC_KEY* keys) { + BIO* bio = BIO_new(BIO_s_mem()); + bool retval = false; + if (!PEM_write_bio_EC_PUBKEY(bio, keys)) { + BIO_free(bio); + qCCritical(commerce) << "failed to write public key"; return retval; - } else { - qCDebug(commerce) << "couldn't parse" << filename; } - BIO_free(bufio); - file.close(); - } else { - qCDebug(commerce) << "couldn't open" << filename; - } - return QByteArray(); -} -// the private key should be read/copied into heap memory. For now, we need the EC_KEY struct -// so I'll return that. -EC_KEY* readPrivateKey(QString filename) { - QFile file(filename); - EC_KEY* key = NULL; - if (file.open(QIODevice::ReadOnly)) { - // file opened successfully - qCDebug(commerce) << "opened key file" << filename; - - QByteArray pemKeyBytes = file.readAll(); - BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length()); - - if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) { - qCDebug(commerce) << "parsed private key file successfully"; - - } else { - qCDebug(commerce) << "couldn't parse" << filename; - // if the passphrase is wrong, then let's not cache it - DependencyManager::get()->setPassphrase(""); + if (!PEM_write_bio_ECPrivateKey(bio, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { + BIO_free(bio); + qCCritical(commerce) << "failed to write private key"; + return retval; } - BIO_free(bufio); - file.close(); - } else { - qCDebug(commerce) << "couldn't open" << filename; - } - return key; -} -// QT's QByteArray will convert to Base64 without any embedded newlines. This just -// writes it with embedded newlines, which is more readable. -void outputBase64WithNewlines(QFile& file, const QByteArray& b64Array) { - for (int i = 0; i < b64Array.size(); i += 64) { - file.write(b64Array.mid(i, 64)); - file.write("\n"); - } -} + QFile file(filename); + if (!file.open(QIODevice::WriteOnly)) { + const char* bio_data; + long bio_size = BIO_get_mem_data(bio, &bio_data); -void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) { - // use the ones in the wallet - auto wallet = DependencyManager::get(); - memcpy(ivec, wallet->getIv(), 16); - memcpy(ckey, wallet->getCKey(), 32); -} + QByteArray keyBytes(bio_data, bio_size); + file.write(keyBytes); + retval = true; + qCDebug(commerce) << "wrote keys successfully"; + file.close(); + } else { + qCDebug(commerce) << "failed to open key file" << filename; + } + BIO_free(bio); + return retval; + } + + QPair generateECKeypair() { + EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); + QPair retval {}; + + EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE); + if (!EC_KEY_generate_key(keyPair)) { + qCDebug(commerce) << "Error generating EC Keypair -" << ERR_get_error(); + return retval; + } + + // grab the public key and private key from the file + unsigned char* publicKeyDER = NULL; + int publicKeyLength = i2d_EC_PUBKEY(keyPair, &publicKeyDER); + + unsigned char* privateKeyDER = NULL; + int privateKeyLength = i2d_ECPrivateKey(keyPair, &privateKeyDER); + + if (publicKeyLength <= 0 || privateKeyLength <= 0) { + qCDebug(commerce) << "Error getting DER public or private key from EC struct -" << ERR_get_error(); + + // cleanup the EC struct + EC_KEY_free(keyPair); + + // cleanup the public and private key DER data, if required + if (publicKeyLength > 0) { + OPENSSL_free(publicKeyDER); + } + + if (privateKeyLength > 0) { + OPENSSL_free(privateKeyDER); + } + + return retval; + } + + if (!writeKeys(keyFilePath(), keyPair)) { + qCDebug(commerce) << "couldn't save keys!"; + return retval; + } + + EC_KEY_free(keyPair); + + // prepare the return values. TODO: Fix this - we probably don't really even want the + // private key at all (better to read it when we need it?). Or maybe we do, when we have + // multiple keys? + retval.first = new QByteArray(reinterpret_cast(publicKeyDER), publicKeyLength); + retval.second = new QByteArray(reinterpret_cast(privateKeyDER), privateKeyLength); + + // cleanup the publicKeyDER and publicKeyDER data + OPENSSL_free(publicKeyDER); + OPENSSL_free(privateKeyDER); + return retval; + } + // END copied code (which will soon change) + + // the public key can just go into a byte array + QByteArray readPublicKey(QString filename) { + QByteArray retval; + QFile file(filename); + if (file.open(QIODevice::ReadOnly)) { + // file opened successfully + qCDebug(commerce) << "opened key file" << filename; + + QByteArray pemKeyBytes = file.readAll(); + BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length()); + + EC_KEY* key = PEM_read_bio_EC_PUBKEY(bufio, NULL, NULL, NULL); + if (key) { + // file read successfully + unsigned char* publicKeyDER = NULL; + int publicKeyLength = i2d_EC_PUBKEY(key, &publicKeyDER); + // TODO: check for 0 length? + + // cleanup + EC_KEY_free(key); + + qCDebug(commerce) << "parsed public key file successfully"; + + QByteArray retval((char*)publicKeyDER, publicKeyLength); + OPENSSL_free(publicKeyDER); + BIO_free(bufio); + file.close(); + return retval; + } else { + qCDebug(commerce) << "couldn't parse" << filename; + } + BIO_free(bufio); + file.close(); + } else { + qCDebug(commerce) << "couldn't open" << filename; + } + return QByteArray(); + } + + // the private key should be read/copied into heap memory. For now, we need the EC_KEY struct + // so I'll return that. + EC_KEY* readPrivateKey(QString filename) { + QFile file(filename); + EC_KEY* key = NULL; + if (file.open(QIODevice::ReadOnly)) { + // file opened successfully + qCDebug(commerce) << "opened key file" << filename; + + QByteArray pemKeyBytes = file.readAll(); + BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length()); + + if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) { + qCDebug(commerce) << "parsed private key file successfully"; + + } else { + qCDebug(commerce) << "couldn't parse" << filename; + // if the passphrase is wrong, then let's not cache it + DependencyManager::get()->setPassphrase(""); + } + BIO_free(bufio); + file.close(); + } else { + qCDebug(commerce) << "couldn't open" << filename; + } + return key; + } + + // QT's QByteArray will convert to Base64 without any embedded newlines. This just + // writes it with embedded newlines, which is more readable. + void outputBase64WithNewlines(QFile& file, const QByteArray& b64Array) { + for (int i = 0; i < b64Array.size(); i += 64) { + file.write(b64Array.mid(i, 64)); + file.write("\n"); + } + } + + void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) { + // use the ones in the wallet + auto wallet = DependencyManager::get(); + memcpy(ivec, wallet->getIv(), 16); + memcpy(ckey, wallet->getCKey(), 32); + } + +} // close unnamed namespace Wallet::Wallet() { auto nodeList = DependencyManager::get(); @@ -423,6 +343,88 @@ Wallet::~Wallet() { } } +bool Wallet::setWallet(const QByteArray& wallet) { + QFile file(keyFilePath()); + if (!file.open(QIODevice::WriteOnly)) { + qCCritical(commerce) << "Unable to open wallet for write in" << keyFilePath(); + return false; + } + if (file.write(wallet) != wallet.count()) { + qCCritical(commerce) << "Unable to write wallet in" << keyFilePath(); + return false; + } + file.close(); + return true; +} +QByteArray Wallet::getWallet() { + QFile file(keyFilePath()); + if (!file.open(QIODevice::ReadOnly)) { + qCInfo(commerce) << "No existing wallet in" << keyFilePath(); + return QByteArray(); + } + QByteArray wallet = file.readAll(); + file.close(); + return wallet; +} + +bool Wallet::copyKeyFileFrom(const QString& pathname) { + QString existing = getKeyFilePath(); + qCDebug(commerce) << "Old keyfile" << existing; + if (!existing.isEmpty()) { + QString backup = QString(existing).insert(existing.indexOf(KEY_FILE) - 1, + QDateTime::currentDateTime().toString(Qt::ISODate).replace(":", "")); + qCDebug(commerce) << "Renaming old keyfile to" << backup; + if (!QFile::rename(existing, backup)) { + qCCritical(commerce) << "Unable to backup" << existing << "to" << backup; + return false; + } + } + QString destination = keyFilePath(); + bool result = QFile::copy(pathname, destination); + qCDebug(commerce) << "copy" << pathname << "to" << destination << "=>" << result; + return result; +} + +bool Wallet::writeBackupInstructions() { + QString inputFilename(PathUtils::resourcesPath() + "html/commerce/backup_instructions.html"); + QString outputFilename = PathUtils::getAppDataFilePath(INSTRUCTIONS_FILE); + QFile inputFile(inputFilename); + QFile outputFile(outputFilename); + bool retval = false; + + if (getKeyFilePath().isEmpty()) { + return false; + } + + if (QFile::exists(inputFilename) && inputFile.open(QIODevice::ReadOnly)) { + if (outputFile.open(QIODevice::ReadWrite)) { + // Read the data from the original file, then close it + QByteArray fileData = inputFile.readAll(); + inputFile.close(); + + // Translate the data from the original file into a QString + QString text(fileData); + + // Replace the necessary string + text.replace(QString("HIFIKEY_PATH_REPLACEME"), keyFilePath()); + + // Write the new text back to the file + outputFile.write(text.toUtf8()); + + // Close the output file + outputFile.close(); + + retval = true; + qCDebug(commerce) << "wrote html file successfully"; + } else { + qCDebug(commerce) << "failed to open output html file" << outputFilename; + } + } else { + qCDebug(commerce) << "failed to open input html file" << inputFilename; + } + return retval; +} + bool Wallet::setPassphrase(const QString& passphrase) { if (_passphrase) { delete _passphrase; From 55f3c3a11a6e5176588a69959f3e137c3dcd7501 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Fri, 19 Apr 2019 18:21:31 -0700 Subject: [PATCH 63/85] allowing context change to change mute state --- interface/src/scripting/Audio.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index bceafc3c42..5dd1488606 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -351,6 +351,13 @@ void Audio::onContextChanged() { changed = true; } }); + if (_settingsLoaded) { + if (isHMD) { + setMuted(getMutedHMD()); + } else { + setMuted(getMutedDesktop()); + } + } if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); } From 0f2dbd7b6ae3de2854dee283e156a23b29c3bec0 Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 4 Apr 2019 17:36:10 -0700 Subject: [PATCH 64/85] Fix mac warnings --- interface/src/Application.cpp | 6 +++--- interface/src/avatar/AvatarManager.cpp | 2 +- interface/src/ui/LoginDialog.cpp | 2 +- libraries/animation/src/AnimVariant.h | 2 +- libraries/entities/src/EntityItem.cpp | 2 +- .../model-baker/src/model-baker/BuildDracoMeshTask.cpp | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7ba45da1fd..08dfa63715 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2337,7 +2337,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->setPrecisionPicking(rayPickID, value); }); - EntityItem::setBillboardRotationOperator([this](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode, const glm::vec3& frustumPos) { + EntityItem::setBillboardRotationOperator([](const glm::vec3& position, const glm::quat& rotation, BillboardMode billboardMode, const glm::vec3& frustumPos) { if (billboardMode == BillboardMode::YAW) { //rotate about vertical to face the camera glm::vec3 dPosition = frustumPos - position; @@ -2365,7 +2365,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->setKickConfirmationOperator([this] (const QUuid& nodeID) { userKickConfirmation(nodeID); }); - render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([this](const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) { + render::entities::WebEntityRenderer::setAcquireWebSurfaceOperator([=](const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) { bool isTablet = url == TabletScriptingInterface::QML; if (htmlContent) { webSurface = DependencyManager::get()->acquire(render::entities::WebEntityRenderer::QML); @@ -2405,7 +2405,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo const uint8_t TABLET_FPS = 90; webSurface->setMaxFps(isTablet ? TABLET_FPS : DEFAULT_MAX_FPS); }); - render::entities::WebEntityRenderer::setReleaseWebSurfaceOperator([this](QSharedPointer& webSurface, bool& cachedWebSurface, std::vector& connections) { + render::entities::WebEntityRenderer::setReleaseWebSurfaceOperator([=](QSharedPointer& webSurface, bool& cachedWebSurface, std::vector& connections) { QQuickItem* rootItem = webSurface->getRootItem(); // Fix for crash in QtWebEngineCore when rapidly switching domains diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 8274259922..ba1309d5b8 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -727,7 +727,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic boxHit._distance = FLT_MAX; for (size_t i = 0; i < hit._boundJoints.size(); i++) { - assert(hit._boundJoints[i] < multiSpheres.size()); + assert(hit._boundJoints[i] < (int)multiSpheres.size()); auto &mSphere = multiSpheres[hit._boundJoints[i]]; if (mSphere.isValid()) { float boundDistance = FLT_MAX; diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index b4f504822f..459b224474 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -138,7 +138,7 @@ void LoginDialog::login(const QString& username, const QString& password) const void LoginDialog::loginThroughOculus() { qDebug() << "Attempting to login through Oculus"; if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) { - oculusPlatformPlugin->requestNonceAndUserID([this] (QString nonce, QString oculusID) { + oculusPlatformPlugin->requestNonceAndUserID([] (QString nonce, QString oculusID) { DependencyManager::get()->requestAccessTokenWithOculus(nonce, oculusID); }); } diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index eb9ebd33dd..a8bdb885e5 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -261,7 +261,7 @@ public: qCDebug(animation) << " " << pair.first << "=" << pair.second.getString(); break; default: - assert(("invalid AnimVariant::Type", false)); + assert(false); } } } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index a532064b6c..e1ede9192a 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -791,7 +791,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bool otherOverwrites = overwriteLocalData && !weOwnSimulation; // calculate hasGrab once outside the lambda rather than calling it every time inside bool hasGrab = stillHasGrabAction(); - auto shouldUpdate = [this, lastEdited, otherOverwrites, filterRejection, hasGrab](quint64 updatedTimestamp, bool valueChanged) { + auto shouldUpdate = [lastEdited, otherOverwrites, filterRejection, hasGrab](quint64 updatedTimestamp, bool valueChanged) { if (hasGrab) { return false; } diff --git a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp index 2e378965de..25a45cefe5 100644 --- a/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp +++ b/libraries/model-baker/src/model-baker/BuildDracoMeshTask.cpp @@ -52,7 +52,7 @@ std::vector createMaterialList(const hfm::Mesh& mesh) { } std::unique_ptr createDracoMesh(const hfm::Mesh& mesh, const std::vector& normals, const std::vector& tangents, const std::vector& materialList) { - Q_ASSERT(normals.size() == 0 || normals.size() == mesh.vertices.size()); + Q_ASSERT(normals.size() == 0 || (int)normals.size() == mesh.vertices.size()); Q_ASSERT(mesh.colors.size() == 0 || mesh.colors.size() == mesh.vertices.size()); Q_ASSERT(mesh.texCoords.size() == 0 || mesh.texCoords.size() == mesh.vertices.size()); From 97632534e52434776023946f7546457737b3a654 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 22 Apr 2019 09:14:32 -0700 Subject: [PATCH 65/85] REvert unecessary modified files to upstream master, just leaving the true new feature in this branch --- .../hmd/DebugHmdDisplayPlugin.cpp | 5 - .../gpu-gl-common/src/gpu/gl/GLBackend.cpp | 8 +- .../gpu-gl-common/src/gpu/gl/GLBackend.h | 10 +- .../src/gpu/gl/GLBackendTransform.cpp | 45 +- .../gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp | 21 +- libraries/gpu/src/gpu/Context.cpp | 945 ------------------ libraries/gpu/src/gpu/Framebuffer.cpp | 30 +- libraries/gpu/src/gpu/Framebuffer.h | 11 +- libraries/gpu/src/gpu/Texture.h | 8 +- libraries/gpu/src/gpu/Transform.slh | 11 +- libraries/shaders/headers/450/header.glsl | 1 - tools/shadergen.py | 4 - 12 files changed, 21 insertions(+), 1078 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp index cd7478d002..b4527ff90c 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp @@ -26,8 +26,6 @@ bool DebugHmdDisplayPlugin::isSupported() const { void DebugHmdDisplayPlugin::resetSensors() { _currentRenderFrameInfo.renderPose = glm::mat4(); // identity - _currentRenderFrameInfo.renderPose = glm::translate(glm::mat4(), glm::vec3(0.0f, 1.76f, 0.0f)); - } bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) { @@ -37,8 +35,6 @@ bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) { // FIXME simulate head movement //_currentRenderFrameInfo.renderPose = ; //_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose; - _currentRenderFrameInfo.renderPose = glm::translate(glm::mat4(), glm::vec3(0.0f, 1.76f, 0.0f)); - _currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose; withNonPresentThreadLock([&] { _frameInfos[frameIndex] = _currentRenderFrameInfo; @@ -78,7 +74,6 @@ bool DebugHmdDisplayPlugin::internalActivate() { // uncomment to capture a quarter size frame //_renderTargetSize /= 2; _cullingProjection = _eyeProjections[0]; - // This must come after the initialization, so that the values calculated // above are available during the customizeContext call (when not running // in threaded present mode) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 844c1ce2bf..fef458f536 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -447,24 +447,20 @@ void GLBackend::render(const Batch& batch) { renderPassTransfer(batch); } -//#ifdef GPU_STEREO_DRAWCALL_INSTANCED -#ifdef GPU_STEREO_VIEWPORT_CLIPPED +#ifdef GPU_STEREO_DRAWCALL_INSTANCED if (_stereo.isStereo()) { glEnable(GL_CLIP_DISTANCE0); } #endif -//#endif { GL_PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render"); renderPassDraw(batch); } -//#ifdef GPU_STEREO_DRAWCALL_INSTANCED -#ifdef GPU_STEREO_VIEWPORT_CLIPPED +#ifdef GPU_STEREO_DRAWCALL_INSTANCED if (_stereo.isStereo()) { glDisable(GL_CLIP_DISTANCE0); } #endif -//#endif // Restore the saved stereo state for the next batch _stereo._enable = savedStereo; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 327488da36..ffd0466b79 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -36,8 +36,7 @@ #define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE #else //#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER -//#define GPU_STEREO_TECHNIQUE_INSTANCED -#define GPU_STEREO_TECHNIQUE_INSTANCED_MULTIVIEWPORT +#define GPU_STEREO_TECHNIQUE_INSTANCED #endif // Let these be configured by the one define picked above @@ -52,13 +51,6 @@ #ifdef GPU_STEREO_TECHNIQUE_INSTANCED #define GPU_STEREO_DRAWCALL_INSTANCED -#define GPU_STEREO_VIEWPORT_CLIPPED -#define GPU_STEREO_CAMERA_BUFFER -#endif - -#ifdef GPU_STEREO_TECHNIQUE_INSTANCED_MULTIVIEWPORT -#define GPU_STEREO_DRAWCALL_INSTANCED -#define GPU_STEREO_MULTI_VIEWPORT #define GPU_STEREO_CAMERA_BUFFER #endif diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp index 698a70af5a..555a0a1e41 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTransform.cpp @@ -39,48 +39,6 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) #ifdef GPU_STEREO_DRAWCALL_INSTANCED { - #ifdef GPU_STEREO_MULTI_VIEWPORT - ivec4& vp = _transform._viewport; - auto sideWidth = vp.z / 2; - - vec4 leftRight[3]; - - // Mono - leftRight[0] = vp; - // adding this here as im doing Layered, force the first viewport here to be half of it - leftRight[0].x = 0; - leftRight[0].z = sideWidth; - - // Left side - leftRight[1] = vp; - leftRight[1].x = 0; - leftRight[1].z = sideWidth; - - // right side - leftRight[2] = vp; - leftRight[2].x = sideWidth; - leftRight[2].z = sideWidth; - - glViewportArrayv(0, 3, (float*)leftRight); - - // Where we assign the GL viewport - if (_stereo.isStereo()) { - - // ivec4 leftRight[3]; - // leftRight[0] = vp; - vp.z /= 2; - /* leftRight[1] = vp; // left side - leftRight[2] = vp; // right side - leftRight[2].x += vp.z; - glViewportArrayv(0, 3, (float*) leftRight); -*/ - if (_stereo._pass) { - vp.x += vp.z; - } - } else { - // glViewport(vp.x, vp.y, vp.z, vp.w); - } - #else ivec4& vp = _transform._viewport; glViewport(vp.x, vp.y, vp.z, vp.w); @@ -91,7 +49,6 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) vp.x += vp.z; } } - #endif } #else if (!_inRenderTransferPass && !isStereo()) { @@ -166,7 +123,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo if (_invalidView || _invalidProj || _invalidViewport) { size_t offset = _cameraUboSize * _cameras.size(); - Vec2 finalJitter = _projectionJitter / Vec2(framebufferSize); + Vec2 finalJitter = _projectionJitter / Vec2(framebufferSize); _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); if (stereo.isStereo()) { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp index a86e6637a2..7a299e792b 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendOutput.cpp @@ -60,23 +60,12 @@ public: } if (gltexture) { - if (!_gpuObject.isLayered()) { - if (gltexture->_target == GL_TEXTURE_2D) { - glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0); - } else if (gltexture->_target == GL_TEXTURE_2D_MULTISAMPLE) { - glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0); - } else { - glNamedFramebufferTextureLayer(_id, colorAttachments[unit], gltexture->_texture, 0, b._subresource); - } + if (gltexture->_target == GL_TEXTURE_2D) { + glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0); + } else if (gltexture->_target == GL_TEXTURE_2D_MULTISAMPLE) { + glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0); } else { - if (gltexture->_target == GL_TEXTURE_2D) { - glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0); - } else if (gltexture->_target == GL_TEXTURE_2D_MULTISAMPLE) { - glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0); - } else { - glNamedFramebufferTextureLayer(_id, colorAttachments[unit], gltexture->_texture, 0, - b._subresource); - } + glNamedFramebufferTextureLayer(_id, colorAttachments[unit], gltexture->_texture, 0, b._subresource); } _colorBuffers.push_back(colorAttachments[unit]); } else { diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index e3c75a24d6..45ee4263a3 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -421,951 +421,6 @@ void gpu::doInBatch(const char* name, const std::shared_ptr& context, const std::function& f) { auto batch = Context::acquireBatch(name); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - f(*batch); context->appendFrameBatch(batch); } diff --git a/libraries/gpu/src/gpu/Framebuffer.cpp b/libraries/gpu/src/gpu/Framebuffer.cpp index 41406b4b93..e88d986da6 100755 --- a/libraries/gpu/src/gpu/Framebuffer.cpp +++ b/libraries/gpu/src/gpu/Framebuffer.cpp @@ -82,14 +82,6 @@ uint32 Framebuffer::getFrameCount() const { } } -uint16 evalRenderBufferNumLayers(const Texture& texture, uint16 subresource) { - if (subresource == TextureView::UNDEFINED_SUBRESOURCE) { - return texture.getNumSlices(); - } else { - return 1; - } -} - bool Framebuffer::validateTargetCompatibility(const Texture& texture, uint32 subresource) const { if (texture.getType() == Texture::TEX_1D) { return false; @@ -100,8 +92,7 @@ bool Framebuffer::validateTargetCompatibility(const Texture& texture, uint32 sub } else { if ((texture.getWidth() == getWidth()) && (texture.getHeight() == getHeight()) && - (texture.getNumSamples() == getNumSamples()) && - (evalRenderBufferNumLayers(texture, subresource) == getNumLayers())) { + (texture.getNumSamples() == getNumSamples())) { return true; } else { return false; @@ -109,7 +100,7 @@ bool Framebuffer::validateTargetCompatibility(const Texture& texture, uint32 sub } } -void Framebuffer::updateSize(const TexturePointer& texture, uint32 subresource) { +void Framebuffer::updateSize(const TexturePointer& texture) { if (!isEmpty()) { return; } @@ -118,9 +109,8 @@ void Framebuffer::updateSize(const TexturePointer& texture, uint32 subresource) _width = texture->getWidth(); _height = texture->getHeight(); _numSamples = texture->getNumSamples(); - _numLayers = evalRenderBufferNumLayers(*texture, subresource); } else { - _width = _height = _numSamples = _numLayers = 0; + _width = _height = _numSamples = 0; } } @@ -148,14 +138,6 @@ uint16 Framebuffer::getNumSamples() const { } } -uint16 Framebuffer::getNumLayers() const { - if (isSwapchain()) { - return getSwapchain()->getNumLayers(); - } else { - return _numLayers; - } -} - // Render buffers int Framebuffer::setRenderBuffer(uint32 slot, const TexturePointer& texture, uint32 subresource) { if (isSwapchain()) { @@ -182,7 +164,7 @@ int Framebuffer::setRenderBuffer(uint32 slot, const TexturePointer& texture, uin ++_colorStamps[slot]; - updateSize(texture, subresource); + updateSize(texture); // assign the new one _renderBuffers[slot] = TextureView(texture, subresource); @@ -209,7 +191,7 @@ void Framebuffer::removeRenderBuffers() { renderBuffer._texture.reset(); } - updateSize(TexturePointer(nullptr), TextureView::UNDEFINED_SUBRESOURCE); + updateSize(TexturePointer(nullptr)); } @@ -258,7 +240,7 @@ bool Framebuffer::assignDepthStencilBuffer(const TexturePointer& texture, const } ++_depthStamp; - updateSize(texture, subresource); + updateSize(texture); // assign the new one _depthStencilBuffer = TextureView(texture, subresource, format); diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index 3d2b869958..47b2775d52 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -27,7 +27,6 @@ public: uint16 getWidth() const { return _width; } uint16 getHeight() const { return _height; } uint16 getNumSamples() const { return _numSamples; } - uint16 getNumLayers() const { return _numLayers; } bool hasDepthStencil() const { return _hasDepthStencil; } bool isFullscreen() const { return _isFullscreen; } @@ -55,7 +54,6 @@ protected: uint16 _width = 1; uint16 _height = 1; uint16 _numSamples = 1; - uint16 _numLayers = 1; uint16 _swapInterval = 0; bool _hasDepthStencil = false; @@ -117,6 +115,7 @@ public: uint32 getDepthStencilBufferSubresource() const; Format getDepthStencilBufferFormat() const; + // Properties Masks getBufferMask() const { return _bufferMask; } bool isEmpty() const { return (_bufferMask == 0); } @@ -131,10 +130,6 @@ public: uint16 getWidth() const; uint16 getHeight() const; uint16 getNumSamples() const; - - uint16 getNumLayers() const; - bool isLayered() const { return getNumLayers() > 1; } - const std::string& getName() const { return _name; } void setName(const std::string& name) { _name = name; } @@ -175,9 +170,7 @@ protected: uint16 _height = 0; uint16 _numSamples = 0; - uint16 _numLayers = 0; - - void updateSize(const TexturePointer& texture, uint32 subresource); + void updateSize(const TexturePointer& texture); bool assignDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource); friend class Serializer; diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 537169bf67..26ff86af9c 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -646,11 +646,9 @@ typedef std::vector< TexturePointer > Textures; class TextureView { public: typedef Resource::Size Size; - - static const uint16 UNDEFINED_SUBRESOURCE { uint16(-1) }; TexturePointer _texture = TexturePointer(NULL); - uint16 _subresource = UNDEFINED_SUBRESOURCE; + uint16 _subresource = 0; Element _element = Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); TextureView() {}; @@ -662,7 +660,7 @@ public: // create the TextureView and own the Texture TextureView(Texture* newTexture, const Element& element) : _texture(newTexture), - _subresource(UNDEFINED_SUBRESOURCE), + _subresource(0), _element(element) {}; TextureView(const TexturePointer& texture, uint16 subresource, const Element& element) : @@ -684,8 +682,6 @@ public: bool operator !() const { return (!_texture); } bool isValid() const { return bool(_texture); } - - bool isSubresource() const { return (_subresource != UNDEFINED_SUBRESOURCE); } }; typedef std::vector TextureViews; diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index 484ad7ebd2..3015de7e0e 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -168,17 +168,10 @@ TransformObject getTransformObject() { vec2 eyeOffsetScale = vec2(-0.5, +0.5); uint eyeIndex = uint(_stereoSide); #if !defined(GPU_GLES) || (defined(HAVE_EXT_clip_cull_distance) && !defined(VULKAN)) -#ifdef GPU_GL450 - /* gl_ViewportIndex = _stereoSide + 1; - // gl_ViewportIndex = 2 - _stereoSide; - */// THIs is the layered version - gl_Layer = _stereoSide; -#else gl_ClipDistance[0] = dot(<$clipPos$>, eyeClipEdge[eyeIndex]); #endif -#endif - // float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w; - // <$clipPos$>.x = newClipPosX; + float newClipPosX = <$clipPos$>.x * 0.5 + eyeOffsetScale[eyeIndex] * <$clipPos$>.w; + <$clipPos$>.x = newClipPosX; #endif #else diff --git a/libraries/shaders/headers/450/header.glsl b/libraries/shaders/headers/450/header.glsl index e3f0021d16..ef0ec09414 100644 --- a/libraries/shaders/headers/450/header.glsl +++ b/libraries/shaders/headers/450/header.glsl @@ -1,4 +1,3 @@ -#extension GL_ARB_shader_viewport_layer_array : require #define GPU_SSBO_TRANSFORM_OBJECT #define BITFIELD int #define LAYOUT(X) layout(X) diff --git a/tools/shadergen.py b/tools/shadergen.py index 4ce4d2818f..f82b471f17 100644 --- a/tools/shadergen.py +++ b/tools/shadergen.py @@ -215,10 +215,6 @@ def processCommand(line): if (dialect == '310es'): spirvCrossDialect = '320es' spirvCrossArgs = [spirvCrossExec, '--output', glslFile, spirvFile, '--version', spirvCrossDialect] if (dialect == '410'): spirvCrossArgs.append('--no-420pack-extension') - if (dialect == '450'): - spirvCrossArgs.append('--extension') - spirvCrossArgs.append('GL_ARB_shader_viewport_layer_array') - executeSubprocess(spirvCrossArgs) else: # This logic is necessary because cmake will agressively keep re-executing the shadergen From 98a7e4e7110996ea972d0b859af7eb9783502b24 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 22 Apr 2019 11:34:58 -0700 Subject: [PATCH 66/85] fix version not being preserved --- tools/oven/src/DomainBaker.cpp | 14 ++++---------- tools/oven/src/DomainBaker.h | 2 ++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index b6adee28dc..fc065619d7 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include "Gzip.h" @@ -132,10 +131,10 @@ void DomainBaker::loadLocalFile() { } // read the file contents to a JSON document - auto jsonDocument = QJsonDocument::fromJson(fileContents); + _json = QJsonDocument::fromJson(fileContents); // grab the entities object from the root JSON object - _entities = jsonDocument.object()[ENTITIES_OBJECT_KEY].toArray(); + _entities = _json.object()[ENTITIES_OBJECT_KEY].toArray(); if (_entities.isEmpty()) { // add an error to our list stating that the models file was empty @@ -749,15 +748,10 @@ void DomainBaker::writeNewEntitiesFile() { // time to write out a main models.json.gz file // first setup a document with the entities array below the entities key - QJsonDocument entitiesDocument; - - QJsonObject rootObject; - rootObject[ENTITIES_OBJECT_KEY] = _entities; - - entitiesDocument.setObject(rootObject); + _json.object()[ENTITIES_OBJECT_KEY] = _entities; // turn that QJsonDocument into a byte array ready for compression - QByteArray jsonByteArray = entitiesDocument.toJson(); + QByteArray jsonByteArray = _json.toJson(); // compress the json byte array using gzip QByteArray compressedJson; diff --git a/tools/oven/src/DomainBaker.h b/tools/oven/src/DomainBaker.h index e8102ec7e8..d4414bbf99 100644 --- a/tools/oven/src/DomainBaker.h +++ b/tools/oven/src/DomainBaker.h @@ -12,6 +12,7 @@ #ifndef hifi_DomainBaker_h #define hifi_DomainBaker_h +#include #include #include #include @@ -59,6 +60,7 @@ private: QString _originalOutputPath; QUrl _destinationPath; + QJsonDocument _json; QJsonArray _entities; QHash> _modelBakers; From 8dabcb76b8b0ea917fcbefdf71b8674cc5bca780 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 22 Apr 2019 21:30:57 +0200 Subject: [PATCH 67/85] Taking into account comments --- libraries/render-utils/src/RenderPipelines.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index cd4d787f0e..ac2eb8e475 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -755,18 +755,21 @@ bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: }); // For shadows, we only need opacity mask information - if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE || multiMaterial.getMaterialKey().isOpacityMaskMap()) { + auto key = multiMaterial.getMaterialKey(); + if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE || key.isOpacityMaskMap()) { auto& schemaBuffer = multiMaterial.getSchemaBuffer(); batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer); if (enableTextures) { batch.setResourceTextureTable(multiMaterial.getTextureTable()); } else { - auto key = multiMaterial.getMaterialKey(); - if (key.isLightmapMap()) { - defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); - } else if (key.isEmissiveMap()) { - defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + if (renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) { + if (key.isLightmapMap()) { + defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture()); + } else if (key.isEmissiveMap()) { + defaultMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture()); + } } + batch.setResourceTextureTable(defaultMaterialTextures); } return true; From 22c2bfc07515dcfd5bea86fab87577d33348b996 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 22 Apr 2019 15:03:12 -0700 Subject: [PATCH 68/85] fix material target parentIDChanged --- scripts/system/html/js/entityProperties.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 4cee3c0bc7..d7800ada5d 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -2950,7 +2950,7 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI */ function parentIDChanged() { - if (currentSelections.length === 1 && currentSelections[0].type === "Material") { + if (currentSelections.length === 1 && currentSelections[0].properties.type === "Material") { requestMaterialTarget(); } } From 4fc97effda095a84131b89059d0a347a78511bfd Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 22 Apr 2019 16:22:18 -0700 Subject: [PATCH 69/85] combine snapshotOperator lists per sam's suggestions --- interface/src/Application.cpp | 22 ++++++------------- interface/src/Application.h | 7 ++---- interface/src/graphics/GraphicsEngine.cpp | 3 --- interface/src/ui/Snapshot.cpp | 4 ++-- interface/src/ui/SnapshotAnimated.cpp | 2 +- .../display-plugins/OpenGLDisplayPlugin.cpp | 15 +++++-------- libraries/gpu/src/gpu/Frame.h | 3 +-- 7 files changed, 18 insertions(+), 38 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 27c6ef2f64..f46d0d3ddd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8441,23 +8441,15 @@ void Application::loadAvatarBrowser() const { void Application::addSnapshotOperator(const SnapshotOperator& snapshotOperator) { std::lock_guard lock(_snapshotMutex); _snapshotOperators.push(snapshotOperator); + _hasPrimarySnapshot |= std::get<2>(snapshotOperator); } bool Application::takeSnapshotOperators(std::queue& snapshotOperators) { std::lock_guard lock(_snapshotMutex); + bool hasPrimarySnapshot = _hasPrimarySnapshot; + _hasPrimarySnapshot = false; _snapshotOperators.swap(snapshotOperators); - return !snapshotOperators.empty(); -} - -void Application::addSecondarySnapshotOperator(const SecondarySnapshotOperator& snapshotOperator) { - std::lock_guard lock(_snapshotMutex); - _secondarySnapshotOperators.push(snapshotOperator); -} - -bool Application::takeSecondarySnapshotOperators(std::queue& snapshotOperators) { - std::lock_guard lock(_snapshotMutex); - _secondarySnapshotOperators.swap(snapshotOperators); - return !snapshotOperators.empty(); + return hasPrimarySnapshot; } void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { @@ -8476,15 +8468,15 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, DependencyManager::get()); }); } - }, aspectRatio }); + }, aspectRatio, true }); } void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) { - addSecondarySnapshotOperator([notify, filename](const QImage& snapshot) { + addSnapshotOperator({ [notify, filename](const QImage& snapshot) { QString snapshotPath = DependencyManager::get()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation()); emit DependencyManager::get()->stillSnapshotTaken(snapshotPath, notify); - }); + }, 0, false }); } void Application::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 967790d034..12523f3108 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -345,12 +345,9 @@ public: void toggleAwayMode(); #endif - using SnapshotOperator = std::pair, float>; - using SecondarySnapshotOperator = std::function; + using SnapshotOperator = std::tuple, float, bool>; void addSnapshotOperator(const SnapshotOperator& snapshotOperator); bool takeSnapshotOperators(std::queue& snapshotOperators); - void addSecondarySnapshotOperator(const SecondarySnapshotOperator& snapshotOperator); - bool takeSecondarySnapshotOperators(std::queue& snapshotOperators); signals: void svoImportRequested(const QString& url); @@ -798,7 +795,7 @@ private: SharedSoundPointer _sampleSound; std::mutex _snapshotMutex; std::queue _snapshotOperators; - std::queue _secondarySnapshotOperators; + bool _hasPrimarySnapshot { false }; DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin; QString _autoSwitchDisplayModeSupportedHMDPluginName; diff --git a/interface/src/graphics/GraphicsEngine.cpp b/interface/src/graphics/GraphicsEngine.cpp index 09471a9570..267822baf2 100644 --- a/interface/src/graphics/GraphicsEngine.cpp +++ b/interface/src/graphics/GraphicsEngine.cpp @@ -245,7 +245,6 @@ void GraphicsEngine::render_performFrame() { } std::queue snapshotOperators; - std::queue secondarySnapshotOperators; if (!_programsCompiled.load()) { gpu::doInBatch("splashFrame", _gpuContext, [&](gpu::Batch& batch) { batch.setFramebuffer(finalFramebuffer); @@ -274,7 +273,6 @@ void GraphicsEngine::render_performFrame() { renderArgs._hudOperator = displayPlugin->getHUDOperator(); renderArgs._hudTexture = qApp->getApplicationOverlay().getOverlayTexture(); renderArgs._takingSnapshot = qApp->takeSnapshotOperators(snapshotOperators); - qApp->takeSecondarySnapshotOperators(secondarySnapshotOperators); renderArgs._blitFramebuffer = finalFramebuffer; render_runRenderFrame(&renderArgs); } @@ -290,7 +288,6 @@ void GraphicsEngine::render_performFrame() { } }; frame->snapshotOperators = snapshotOperators; - frame->secondarySnapshotOperators = secondarySnapshotOperators; // deliver final scene rendering commands to the display plugin { PROFILE_RANGE(render, "/pluginOutput"); diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 94523fa6dd..93d9188cc8 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -168,7 +168,7 @@ void Snapshot::takeNextSnapshot() { if (_taking360Snapshot) { if (!_waitingOnSnapshot) { _waitingOnSnapshot = true; - qApp->addSecondarySnapshotOperator([this](const QImage& snapshot) { + qApp->addSnapshotOperator({ [this](const QImage& snapshot) { // Order is: // 0. Down // 1. Front @@ -202,7 +202,7 @@ void Snapshot::takeNextSnapshot() { _waitingOnSnapshot = false; _snapshotIndex++; - }); + }, 0, false }); } } else { _snapshotTimer.stop(); diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index 03ec86e342..f8f6a30635 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -86,7 +86,7 @@ void SnapshotAnimated::captureFrames() { SnapshotAnimated::snapshotAnimatedTimerRunning = false; } } - }, SnapshotAnimated::aspectRatio }); + }, SnapshotAnimated::aspectRatio, true }); } else { // Notify the user that we're processing the snapshot // This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called. diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index e80102ec85..4c3de004bd 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -725,20 +725,15 @@ void OpenGLDisplayPlugin::present() { PROFILE_RANGE_EX(render, "snapshotOperators", 0xffff00ff, frameId) while (!_currentFrame->snapshotOperators.empty()) { auto& snapshotOperator = _currentFrame->snapshotOperators.front(); - snapshotOperator.first(getScreenshot(snapshotOperator.second)); + if (std::get<2>(snapshotOperator)) { + std::get<0>(snapshotOperator)(getScreenshot(std::get<1>(snapshotOperator))); + } else { + std::get<0>(snapshotOperator)(getSecondaryCameraScreenshot()); + } _currentFrame->snapshotOperators.pop(); } } - { // If we have any secondary camera snapshots this frame, handle them - PROFILE_RANGE_EX(render, "secondarySnapshotOperators", 0x00ff00ff, frameId) - while (!_currentFrame->secondarySnapshotOperators.empty()) { - auto& snapshotOperator = _currentFrame->secondarySnapshotOperators.front(); - snapshotOperator(getSecondaryCameraScreenshot()); - _currentFrame->secondarySnapshotOperators.pop(); - } - } - // Take the composite framebuffer and send it to the output device { PROFILE_RANGE_EX(render, "internalPresent", 0xff00ffff, frameId) diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index 541b9393d3..9aa99e50f7 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -42,8 +42,7 @@ namespace gpu { /// How to process the framebuffer when the frame dies. MUST BE THREAD SAFE FramebufferRecycler framebufferRecycler; - std::queue, float>> snapshotOperators; - std::queue> secondarySnapshotOperators; + std::queue, float, bool>> snapshotOperators; protected: friend class Deserializer; From 7f4292440abb7869cd7d20ec148aa151e791644f Mon Sep 17 00:00:00 2001 From: Clement Date: Fri, 12 Apr 2019 09:10:12 -0700 Subject: [PATCH 70/85] Fix oven integration in the asset server --- assignment-client/src/assets/AssetServer.cpp | 134 +++++++++++------- assignment-client/src/assets/AssetServer.h | 7 +- .../src/assets/BakeAssetTask.cpp | 34 +++-- assignment-client/src/assets/BakeAssetTask.h | 2 +- 4 files changed, 111 insertions(+), 66 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index c2aec9b058..88f81f639b 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1326,12 +1326,40 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, } void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, - QString bakedTempOutputDir, QVector bakedFilePaths) { + QString bakedTempOutputDir) { bool errorCompletingBake { false }; QString errorReason; qDebug() << "Completing bake for " << originalAssetHash; + + + QDir outputDir(bakedTempOutputDir); + auto directories = outputDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + assert(directories.size() == 1); + QString bakedDirectoryPath; + for (const auto& dirName : directories) { + outputDir.cd(dirName); + if (outputDir.exists("baked") && outputDir.exists("original")) { + bakedDirectoryPath = outputDir.filePath("baked"); + } + outputDir.cdUp(); + } + + assert(!bakedDirectoryPath.isEmpty()); + + QDirIterator it(bakedDirectoryPath, QDirIterator::Subdirectories); + QVector bakedFilePaths; + while (it.hasNext()) { + it.next(); + if (it.fileInfo().isFile()) { + bakedFilePaths.push_back(it.filePath()); + } + } + + QDir bakedDirectory(bakedDirectoryPath); + QString redirectTarget; + for (auto& filePath : bakedFilePaths) { // figure out the hash for the contents of this file QFile file(filePath); @@ -1340,62 +1368,59 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina AssetUtils::AssetHash bakedFileHash; - if (file.open(QIODevice::ReadOnly)) { - QCryptographicHash hasher(QCryptographicHash::Sha256); - - if (hasher.addData(&file)) { - bakedFileHash = hasher.result().toHex(); - } else { - // stop handling this bake, couldn't hash the contents of the file - errorCompletingBake = true; - errorReason = "Failed to finalize bake"; - break; - } - - // first check that we don't already have this bake file in our list - auto bakeFileDestination = _filesDirectory.absoluteFilePath(bakedFileHash); - if (!QFile::exists(bakeFileDestination)) { - // copy each to our files folder (with the hash as their filename) - if (!file.copy(_filesDirectory.absoluteFilePath(bakedFileHash))) { - // stop handling this bake, couldn't copy the bake file into our files directory - errorCompletingBake = true; - errorReason = "Failed to copy baked assets to asset server"; - break; - } - } - - // setup the mapping for this bake file - auto relativeFilePath = QUrl(filePath).fileName(); - qDebug() << "Relative file path is: " << relativeFilePath; - if (relativeFilePath.endsWith(".fbx", Qt::CaseInsensitive)) { - // for an FBX file, we replace the filename with the simple name - // (to handle the case where two mapped assets have the same hash but different names) - relativeFilePath = BAKED_ASSET_SIMPLE_FBX_NAME; - } else if (relativeFilePath.endsWith(".js", Qt::CaseInsensitive)) { - relativeFilePath = BAKED_ASSET_SIMPLE_JS_NAME; - } else if (!originalAssetPath.endsWith(".fbx", Qt::CaseInsensitive)) { - relativeFilePath = BAKED_ASSET_SIMPLE_TEXTURE_NAME; - } - - QString bakeMapping = getBakeMapping(originalAssetHash, relativeFilePath); - - // add a mapping (under the hidden baked folder) for this file resulting from the bake - if (setMapping(bakeMapping, bakedFileHash)) { - qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash; - } else { - qDebug() << "Failed to set mapping"; - // stop handling this bake, couldn't add a mapping for this bake file - errorCompletingBake = true; - errorReason = "Failed to finalize bake"; - break; - } - } else { + if (!file.open(QIODevice::ReadOnly)) { qDebug() << "Failed to open baked file: " << filePath; // stop handling this bake, we couldn't open one of the files for reading errorCompletingBake = true; errorReason = "Failed to finalize bake"; break; } + + QCryptographicHash hasher(QCryptographicHash::Sha256); + + if (!hasher.addData(&file)) { + // stop handling this bake, couldn't hash the contents of the file + errorCompletingBake = true; + errorReason = "Failed to finalize bake"; + break; + } + + bakedFileHash = hasher.result().toHex(); + + // first check that we don't already have this bake file in our list + auto bakeFileDestination = _filesDirectory.absoluteFilePath(bakedFileHash); + if (!QFile::exists(bakeFileDestination)) { + // copy each to our files folder (with the hash as their filename) + if (!file.copy(_filesDirectory.absoluteFilePath(bakedFileHash))) { + // stop handling this bake, couldn't copy the bake file into our files directory + errorCompletingBake = true; + errorReason = "Failed to copy baked assets to asset server"; + break; + } + } + + // setup the mapping for this bake file + auto relativeFilePath = bakedDirectory.relativeFilePath(filePath); + qDebug() << "Relative file path is: " << relativeFilePath; + + QString bakeMapping = getBakeMapping(originalAssetHash, relativeFilePath); + + // Check if this is the file we should redirect to when someone asks for the original asset + if ((relativeFilePath.endsWith(".baked.fst", Qt::CaseInsensitive) && originalAssetPath.endsWith(".fbx")) || + (relativeFilePath.endsWith(".texmeta.json", Qt::CaseInsensitive) && !originalAssetPath.endsWith(".fbx"))) { + redirectTarget = bakeMapping; + } + + // add a mapping (under the hidden baked folder) for this file resulting from the bake + if (!setMapping(bakeMapping, bakedFileHash)) { + qDebug() << "Failed to set mapping"; + // stop handling this bake, couldn't add a mapping for this bake file + errorCompletingBake = true; + errorReason = "Failed to finalize bake"; + break; + } + + qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash; } for (auto& filePath : bakedFilePaths) { @@ -1411,9 +1436,12 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina auto type = assetTypeForFilename(originalAssetPath); auto currentTypeVersion = currentBakeVersionForAssetType(type); + assert(!redirectTarget.isEmpty()); + AssetMeta meta; meta.bakeVersion = currentTypeVersion; meta.failedLastBake = errorCompletingBake; + meta.redirectTarget = redirectTarget; if (errorCompletingBake) { qWarning() << "Could not complete bake for" << originalAssetHash; @@ -1435,6 +1463,7 @@ void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath static const QString BAKE_VERSION_KEY = "bake_version"; static const QString FAILED_LAST_BAKE_KEY = "failed_last_bake"; static const QString LAST_BAKE_ERRORS_KEY = "last_bake_errors"; +static const QString REDIRECT_TARGET_KEY = "redirect_target"; std::pair AssetServer::readMetaFile(AssetUtils::AssetHash hash) { auto metaFilePath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + "meta.json"; @@ -1461,6 +1490,7 @@ std::pair AssetServer::readMetaFile(AssetUtils::AssetHash hash) auto bakeVersion = root[BAKE_VERSION_KEY]; auto failedLastBake = root[FAILED_LAST_BAKE_KEY]; auto lastBakeErrors = root[LAST_BAKE_ERRORS_KEY]; + auto redirectTarget = root[REDIRECT_TARGET_KEY]; if (bakeVersion.isDouble() && failedLastBake.isBool() @@ -1470,6 +1500,7 @@ std::pair AssetServer::readMetaFile(AssetUtils::AssetHash hash) meta.bakeVersion = bakeVersion.toInt(); meta.failedLastBake = failedLastBake.toBool(); meta.lastBakeErrors = lastBakeErrors.toString(); + meta.redirectTarget = redirectTarget.toString(); return { true, meta }; } else { @@ -1488,6 +1519,7 @@ bool AssetServer::writeMetaFile(AssetUtils::AssetHash originalAssetHash, const A metaFileObject[BAKE_VERSION_KEY] = (int)meta.bakeVersion; metaFileObject[FAILED_LAST_BAKE_KEY] = meta.failedLastBake; metaFileObject[LAST_BAKE_ERRORS_KEY] = meta.lastBakeErrors; + metaFileObject[REDIRECT_TARGET_KEY] = meta.redirectTarget; QJsonDocument metaFileDoc; metaFileDoc.setObject(metaFileObject); diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index b3d0f18a8f..fe84df5141 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -62,12 +62,10 @@ enum class ScriptBakeVersion : BakeVersion { }; struct AssetMeta { - AssetMeta() { - } - BakeVersion bakeVersion { INITIAL_BAKE_VERSION }; bool failedLastBake { false }; QString lastBakeErrors; + QString redirectTarget; }; class BakeAssetTask; @@ -139,8 +137,7 @@ private: void bakeAsset(const AssetUtils::AssetHash& assetHash, const AssetUtils::AssetPath& assetPath, const QString& filePath); /// Move baked content for asset to baked directory and update baked status - void handleCompletedBake(QString originalAssetHash, QString assetPath, QString bakedTempOutputDir, - QVector bakedFilePaths); + void handleCompletedBake(QString originalAssetHash, QString assetPath, QString bakedTempOutputDir); void handleFailedBake(QString originalAssetHash, QString assetPath, QString errors); void handleAbortedBake(QString originalAssetHash, QString assetPath); diff --git a/assignment-client/src/assets/BakeAssetTask.cpp b/assignment-client/src/assets/BakeAssetTask.cpp index ecb4ede5d8..7ca87fe4e3 100644 --- a/assignment-client/src/assets/BakeAssetTask.cpp +++ b/assignment-client/src/assets/BakeAssetTask.cpp @@ -57,12 +57,19 @@ void BakeAssetTask::run() { return; } + // Make a new temporary directory for the Oven to work in QString tempOutputDir = PathUtils::generateTemporaryDir(); + + // Copy file to bake the temporary dir and give a name the oven can work with + auto assetName = _assetPath.split("/").last(); + auto tempAssetPath = tempOutputDir + "/" + assetName; + QFile::copy(_filePath, tempAssetPath); + auto base = QFileInfo(QCoreApplication::applicationFilePath()).absoluteDir(); QString path = base.absolutePath() + "/oven"; QString extension = _assetPath.mid(_assetPath.lastIndexOf('.') + 1); QStringList args { - "-i", _filePath, + "-i", tempAssetPath, "-o", tempOutputDir, "-t", extension, }; @@ -72,7 +79,7 @@ void BakeAssetTask::run() { QEventLoop loop; connect(_ovenProcess.get(), static_cast(&QProcess::finished), - this, [&loop, this, tempOutputDir](int exitCode, QProcess::ExitStatus exitStatus) { + this, [&loop, this, tempOutputDir, tempAssetPath](int exitCode, QProcess::ExitStatus exitStatus) { qDebug() << "Baking process finished: " << exitCode << exitStatus; if (exitStatus == QProcess::CrashExit) { @@ -82,18 +89,20 @@ void BakeAssetTask::run() { QString errors = "Fatal error occurred while baking"; emit bakeFailed(_assetHash, _assetPath, errors); } - } else if (exitCode == OVEN_STATUS_CODE_SUCCESS) { - QDir outputDir = tempOutputDir; - auto files = outputDir.entryInfoList(QDir::Files); - QVector outputFiles; - for (auto& file : files) { - outputFiles.push_back(file.absoluteFilePath()); + if (!QDir(tempOutputDir).rmdir(".")) { + qWarning() << "Failed to remove temporary directory:" << tempOutputDir; } + } else if (exitCode == OVEN_STATUS_CODE_SUCCESS) { + // Remove temp copy of the original asset + QFile::remove(tempAssetPath); - emit bakeComplete(_assetHash, _assetPath, tempOutputDir, outputFiles); + emit bakeComplete(_assetHash, _assetPath, tempOutputDir); } else if (exitStatus == QProcess::NormalExit && exitCode == OVEN_STATUS_CODE_ABORT) { _wasAborted.store(true); emit bakeAborted(_assetHash, _assetPath); + if (!QDir(tempOutputDir).rmdir(".")) { + qWarning() << "Failed to remove temporary directory:" << tempOutputDir; + } } else { QString errors; if (exitCode == OVEN_STATUS_CODE_FAIL) { @@ -108,6 +117,9 @@ void BakeAssetTask::run() { } } emit bakeFailed(_assetHash, _assetPath, errors); + if (!QDir(tempOutputDir).rmdir(".")) { + qWarning() << "Failed to remove temporary directory:" << tempOutputDir; + } } loop.quit(); @@ -115,9 +127,13 @@ void BakeAssetTask::run() { qDebug() << "Starting oven for " << _assetPath; _ovenProcess->start(path, args, QIODevice::ReadOnly); + qDebug() << "Running:" << path << args; if (!_ovenProcess->waitForStarted(-1)) { QString errors = "Oven process failed to start"; emit bakeFailed(_assetHash, _assetPath, errors); + if (!QDir(tempOutputDir).rmdir(".")) { + qWarning() << "Failed to remove temporary directory:" << tempOutputDir; + } return; } diff --git a/assignment-client/src/assets/BakeAssetTask.h b/assignment-client/src/assets/BakeAssetTask.h index 24b070d08a..2d50a26bc1 100644 --- a/assignment-client/src/assets/BakeAssetTask.h +++ b/assignment-client/src/assets/BakeAssetTask.h @@ -37,7 +37,7 @@ public slots: void abort(); signals: - void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir, QVector outputFiles); + void bakeComplete(QString assetHash, QString assetPath, QString tempOutputDir); void bakeFailed(QString assetHash, QString assetPath, QString errors); void bakeAborted(QString assetHash, QString assetPath); From 080ef882a87a74643870cd35b8009210616bdb88 Mon Sep 17 00:00:00 2001 From: Clement Date: Fri, 12 Apr 2019 11:26:40 -0700 Subject: [PATCH 71/85] Update Asset Server redirect and baking conditions --- assignment-client/src/assets/AssetServer.cpp | 132 +++++++++---------- 1 file changed, 60 insertions(+), 72 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 88f81f639b..4805d2fe4c 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -107,6 +107,10 @@ BakeVersion currentBakeVersionForAssetType(BakedAssetType type) { } } +QString getBakeMapping(const AssetUtils::AssetHash& hash, const QString& relativeFilePath) { + return AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath; +} + const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; void AssetServer::bakeAsset(const AssetUtils::AssetHash& assetHash, const AssetUtils::AssetPath& assetPath, const QString& filePath) { @@ -141,26 +145,22 @@ std::pair AssetServer::getAssetStatus(const A return { AssetUtils::Baked, "" }; } - auto dotIndex = path.lastIndexOf("."); - if (dotIndex == -1) { + BakedAssetType type = assetTypeForFilename(path); + + if (type == BakedAssetType::Undefined) { return { AssetUtils::Irrelevant, "" }; } - auto extension = path.mid(dotIndex + 1); + bool loaded; + AssetMeta meta; + std::tie(loaded, meta) = readMetaFile(hash); - QString bakedFilename; - - if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) { - bakedFilename = BAKED_MODEL_SIMPLE_NAME; - } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) { - bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; - } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extension)) { - bakedFilename = BAKED_SCRIPT_SIMPLE_NAME; - } else { - return { AssetUtils::Irrelevant, "" }; + QString bakedFilename = bakedFilenameForAssetType(type); + auto bakedPath = getBakeMapping(hash, bakedFilename); + if (loaded && !meta.redirectTarget.isEmpty()) { + bakedPath = meta.redirectTarget; } - auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + bakedFilename; auto jt = _fileMappings.find(bakedPath); if (jt != _fileMappings.end()) { if (jt->second == hash) { @@ -168,14 +168,8 @@ std::pair AssetServer::getAssetStatus(const A } else { return { AssetUtils::Baked, "" }; } - } else { - bool loaded; - AssetMeta meta; - - std::tie(loaded, meta) = readMetaFile(hash); - if (loaded && meta.failedLastBake) { - return { AssetUtils::Error, meta.lastBakeErrors }; - } + } else if (loaded && meta.failedLastBake) { + return { AssetUtils::Error, meta.lastBakeErrors }; } return { AssetUtils::Pending, "" }; @@ -227,8 +221,16 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU return false; } + bool loaded; + AssetMeta meta; + std::tie(loaded, meta) = readMetaFile(assetHash); + QString bakedFilename = bakedFilenameForAssetType(type); - auto bakedPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + assetHash + "/" + bakedFilename; + auto bakedPath = getBakeMapping(assetHash, bakedFilename); + if (loaded && !meta.redirectTarget.isEmpty()) { + bakedPath = meta.redirectTarget; + } + auto mappingIt = _fileMappings.find(bakedPath); bool bakedMappingExists = mappingIt != _fileMappings.end(); @@ -238,10 +240,6 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU return false; } - bool loaded; - AssetMeta meta; - std::tie(loaded, meta) = readMetaFile(assetHash); - if (type == BakedAssetType::Texture && !loaded) { return false; } @@ -633,36 +631,33 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketLi if (it != _fileMappings.end()) { // check if we should re-direct to a baked asset - - // first, figure out from the mapping extension what type of file this is - auto assetPathExtension = assetPath.mid(assetPath.lastIndexOf('.') + 1).toLower(); - - auto type = assetTypeForFilename(assetPath); - QString bakedRootFile = bakedFilenameForAssetType(type); - auto originalAssetHash = it->second; QString redirectedAssetHash; - QString bakedAssetPath; quint8 wasRedirected = false; bool bakingDisabled = false; - if (!bakedRootFile.isEmpty()) { - // we ran into an asset for which we could have a baked version, let's check if it's ready - bakedAssetPath = AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + originalAssetHash + "/" + bakedRootFile; - auto bakedIt = _fileMappings.find(bakedAssetPath); + bool loaded; + AssetMeta meta; + std::tie(loaded, meta) = readMetaFile(originalAssetHash); - if (bakedIt != _fileMappings.end()) { - if (bakedIt->second != originalAssetHash) { - qDebug() << "Did find baked version for: " << originalAssetHash << assetPath; - // we found a baked version of the requested asset to serve, redirect to that - redirectedAssetHash = bakedIt->second; - wasRedirected = true; - } else { - qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath << " (disabled)"; - bakingDisabled = true; - } + auto type = assetTypeForFilename(assetPath); + QString bakedRootFile = bakedFilenameForAssetType(type); + QString bakedAssetPath = getBakeMapping(originalAssetHash, bakedRootFile); + + if (loaded && !meta.redirectTarget.isEmpty()) { + bakedAssetPath = meta.redirectTarget; + } + + auto bakedIt = _fileMappings.find(bakedAssetPath); + if (bakedIt != _fileMappings.end()) { + if (bakedIt->second != originalAssetHash) { + qDebug() << "Did find baked version for: " << originalAssetHash << assetPath; + // we found a baked version of the requested asset to serve, redirect to that + redirectedAssetHash = bakedIt->second; + wasRedirected = true; } else { - qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath; + qDebug() << "Did not find baked version for: " << originalAssetHash << assetPath << " (disabled)"; + bakingDisabled = true; } } @@ -684,20 +679,13 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, NLPacketLi auto query = QUrlQuery(url.query()); bool isSkybox = query.hasQueryItem("skybox"); - if (isSkybox) { - bool loaded; - AssetMeta meta; - std::tie(loaded, meta) = readMetaFile(originalAssetHash); - - if (!loaded) { - AssetMeta needsBakingMeta; - needsBakingMeta.bakeVersion = NEEDS_BAKING_BAKE_VERSION; - - writeMetaFile(originalAssetHash, needsBakingMeta); - if (!bakingDisabled) { - maybeBake(assetPath, originalAssetHash); - } + if (isSkybox && !loaded) { + AssetMeta needsBakingMeta; + needsBakingMeta.bakeVersion = NEEDS_BAKING_BAKE_VERSION; + writeMetaFile(originalAssetHash, needsBakingMeta); + if (!bakingDisabled) { + maybeBake(assetPath, originalAssetHash); } } } @@ -1297,14 +1285,6 @@ bool AssetServer::renameMapping(AssetUtils::AssetPath oldPath, AssetUtils::Asset } } -static const QString BAKED_ASSET_SIMPLE_FBX_NAME = "asset.fbx"; -static const QString BAKED_ASSET_SIMPLE_TEXTURE_NAME = "texture.ktx"; -static const QString BAKED_ASSET_SIMPLE_JS_NAME = "asset.js"; - -QString getBakeMapping(const AssetUtils::AssetHash& hash, const QString& relativeFilePath) { - return AssetUtils::HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath; -} - void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, QString errors) { qDebug() << "Failed to bake: " << originalAssetHash << assetPath << "(" << errors << ")"; @@ -1553,10 +1533,18 @@ bool AssetServer::setBakingEnabled(const AssetUtils::AssetPathList& paths, bool if (type == BakedAssetType::Undefined) { continue; } - QString bakedFilename = bakedFilenameForAssetType(type); auto hash = it->second; + + bool loaded; + AssetMeta meta; + std::tie(loaded, meta) = readMetaFile(hash); + + QString bakedFilename = bakedFilenameForAssetType(type); auto bakedMapping = getBakeMapping(hash, bakedFilename); + if (loaded && !meta.redirectTarget.isEmpty()) { + bakedMapping = meta.redirectTarget; + } auto it = _fileMappings.find(bakedMapping); bool currentlyDisabled = (it != _fileMappings.end() && it->second == hash); From 9deb4e47adc0001b8055c0144445ec969f25fdb2 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 23 Apr 2019 08:46:54 -0700 Subject: [PATCH 72/85] comment out warning --- libraries/shared/src/VariantMapToScriptValue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/VariantMapToScriptValue.cpp b/libraries/shared/src/VariantMapToScriptValue.cpp index 437f60a2ed..b3c02d99f4 100644 --- a/libraries/shared/src/VariantMapToScriptValue.cpp +++ b/libraries/shared/src/VariantMapToScriptValue.cpp @@ -44,7 +44,7 @@ QScriptValue variantToScriptValue(QVariant& qValue, QScriptEngine& scriptEngine) if (qValue.canConvert()) { return qValue.toFloat(); } - qCDebug(shared) << "unhandled QScript type" << qValue.type(); + //qCDebug(shared) << "unhandled QScript type" << qValue.type(); break; } From ad22af1379781f2bece4f4a6d5a8a861d2c7015a Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 22 Apr 2019 19:08:15 -0700 Subject: [PATCH 73/85] set AudioClient mute state as catch-all --- interface/src/scripting/Audio.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 5dd1488606..b406c097e7 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -352,11 +352,11 @@ void Audio::onContextChanged() { } }); if (_settingsLoaded) { - if (isHMD) { - setMuted(getMutedHMD()); - } else { - setMuted(getMutedDesktop()); - } + bool isMuted = isHMD ? getMutedHMD() : getMutedDesktop(); + setMuted(isMuted); + // always set audio client muted state on context changed - sometimes setMuted does not catch it. + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); } if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); From 48aef3e6d9ac0e79967ab18be4ef99c59f2c3951 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 23 Apr 2019 08:45:38 -0700 Subject: [PATCH 74/85] build error --- interface/src/Application.cpp | 10 +++++----- interface/src/ui/Snapshot.cpp | 4 ++-- interface/src/ui/SnapshotAnimated.cpp | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f46d0d3ddd..77c3d438a4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8441,7 +8441,7 @@ void Application::loadAvatarBrowser() const { void Application::addSnapshotOperator(const SnapshotOperator& snapshotOperator) { std::lock_guard lock(_snapshotMutex); _snapshotOperators.push(snapshotOperator); - _hasPrimarySnapshot |= std::get<2>(snapshotOperator); + _hasPrimarySnapshot = _hasPrimarySnapshot || std::get<2>(snapshotOperator); } bool Application::takeSnapshotOperators(std::queue& snapshotOperators) { @@ -8453,7 +8453,7 @@ bool Application::takeSnapshotOperators(std::queue& snapshotOp } void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) { - addSnapshotOperator({ [notify, includeAnimated, aspectRatio, filename](const QImage& snapshot) { + addSnapshotOperator(std::make_tuple([notify, includeAnimated, aspectRatio, filename](const QImage& snapshot) { QString path = DependencyManager::get()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation()); // If we're not doing an animated snapshot as well... @@ -8468,15 +8468,15 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, DependencyManager::get()); }); } - }, aspectRatio, true }); + }, aspectRatio, true)); } void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) { - addSnapshotOperator({ [notify, filename](const QImage& snapshot) { + addSnapshotOperator(std::make_tuple([notify, filename](const QImage& snapshot) { QString snapshotPath = DependencyManager::get()->saveSnapshot(snapshot, filename, TestScriptingInterface::getInstance()->getTestResultsLocation()); emit DependencyManager::get()->stillSnapshotTaken(snapshotPath, notify); - }, 0, false }); + }, 0.0f, false)); } void Application::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) { diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 93d9188cc8..d97c401351 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -168,7 +168,7 @@ void Snapshot::takeNextSnapshot() { if (_taking360Snapshot) { if (!_waitingOnSnapshot) { _waitingOnSnapshot = true; - qApp->addSnapshotOperator({ [this](const QImage& snapshot) { + qApp->addSnapshotOperator(std::make_tuple([this](const QImage& snapshot) { // Order is: // 0. Down // 1. Front @@ -202,7 +202,7 @@ void Snapshot::takeNextSnapshot() { _waitingOnSnapshot = false; _snapshotIndex++; - }, 0, false }); + }, 0.0f, false)); } } else { _snapshotTimer.stop(); diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index f8f6a30635..b8cffca8ab 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -60,7 +60,7 @@ void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio void SnapshotAnimated::captureFrames() { if (SnapshotAnimated::snapshotAnimatedTimerRunning) { - qApp->addSnapshotOperator({ [](const QImage& snapshot) { + qApp->addSnapshotOperator(std::make_tuple([](const QImage& snapshot) { // Get a screenshot from the display, then scale the screenshot down, // then convert it to the image format the GIF library needs, // then save all that to the QImage named "frame" @@ -86,7 +86,7 @@ void SnapshotAnimated::captureFrames() { SnapshotAnimated::snapshotAnimatedTimerRunning = false; } } - }, SnapshotAnimated::aspectRatio, true }); + }, SnapshotAnimated::aspectRatio, true)); } else { // Notify the user that we're processing the snapshot // This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called. From 8a7503ef51a15013c0eee06a270b5c10f57dccfd Mon Sep 17 00:00:00 2001 From: amer cerkic Date: Tue, 23 Apr 2019 11:45:25 -0700 Subject: [PATCH 75/85] turning on blit present for quest --- libraries/oculusMobile/src/ovr/VrHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/oculusMobile/src/ovr/VrHandler.cpp b/libraries/oculusMobile/src/ovr/VrHandler.cpp index a7b0f9f8ee..aff1f256b5 100644 --- a/libraries/oculusMobile/src/ovr/VrHandler.cpp +++ b/libraries/oculusMobile/src/ovr/VrHandler.cpp @@ -28,7 +28,7 @@ static AAssetManager* ASSET_MANAGER = nullptr; -#define USE_BLIT_PRESENT 0 +#define USE_BLIT_PRESENT 1 #if !USE_BLIT_PRESENT From 1c888d63f05bb4635ac7cc1da07709ef3c16698b Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 23 Apr 2019 13:44:36 -0700 Subject: [PATCH 76/85] don't try to bake materialURL if it points to materialData --- tools/oven/src/DomainBaker.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index fc065619d7..ee50dc1588 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -409,7 +409,10 @@ void DomainBaker::enumerateEntities() { // Materials if (entity.contains(MATERIAL_URL_KEY)) { - addMaterialBaker(MATERIAL_URL_KEY, entity[MATERIAL_URL_KEY].toString(), true, *it); + QString materialURL = entity[MATERIAL_URL_KEY].toString(); + if (!materialURL.startsWith("materialData")) { + addMaterialBaker(MATERIAL_URL_KEY, materialURL, true, *it); + } } if (entity.contains(MATERIAL_DATA_KEY)) { addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it, _destinationPath); From b1f00d6f22d6a261218fe11efea94d8e0f99bcd1 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 11 Apr 2019 16:41:31 -0700 Subject: [PATCH 77/85] attempt to handle atp redirects from .fbx to .baked.fst --- .../src/material-networking/TextureCache.cpp | 6 +- .../src/model-networking/ModelCache.cpp | 315 ++++++++---------- .../src/model-networking/ModelCache.h | 43 ++- 3 files changed, 171 insertions(+), 193 deletions(-) diff --git a/libraries/material-networking/src/material-networking/TextureCache.cpp b/libraries/material-networking/src/material-networking/TextureCache.cpp index 6ceb5d328a..25e7eeb25d 100644 --- a/libraries/material-networking/src/material-networking/TextureCache.cpp +++ b/libraries/material-networking/src/material-networking/TextureCache.cpp @@ -635,11 +635,9 @@ void NetworkTexture::makeLocalRequest() { } bool NetworkTexture::handleFailedRequest(ResourceRequest::Result result) { - if (_currentlyLoadingResourceType != ResourceType::KTX - && result == ResourceRequest::Result::RedirectFail) { - + if (_shouldFailOnRedirect && result == ResourceRequest::Result::RedirectFail) { auto newPath = _request->getRelativePathUrl(); - if (newPath.fileName().endsWith(".ktx")) { + if (newPath.fileName().toLower().endsWith(".ktx")) { _currentlyLoadingResourceType = ResourceType::KTX; _activeUrl = newPath; _shouldFailOnRedirect = false; diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 26fc0095c5..b914faaab9 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -31,8 +31,6 @@ Q_LOGGING_CATEGORY(trace_resource_parse_geometry, "trace.resource.parse.geometry") -class GeometryReader; - class GeometryExtra { public: const GeometryMappingPair& mapping; @@ -87,113 +85,6 @@ namespace std { }; } -QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) { - return textureBaseUrl.isValid() ? textureBaseUrl : url; -} - -class GeometryMappingResource : public GeometryResource { - Q_OBJECT -public: - GeometryMappingResource(const QUrl& url) : GeometryResource(url) {}; - - QString getType() const override { return "GeometryMapping"; } - - virtual void downloadFinished(const QByteArray& data) override; - -private slots: - void onGeometryMappingLoaded(bool success); - -private: - GeometryResource::Pointer _geometryResource; - QMetaObject::Connection _connection; -}; - -void GeometryMappingResource::downloadFinished(const QByteArray& data) { - PROFILE_ASYNC_BEGIN(resource_parse_geometry, "GeometryMappingResource::downloadFinished", _url.toString(), - { { "url", _url.toString() } }); - - // store parsed contents of FST file - _mapping = FSTReader::readMapping(data); - - QString filename = _mapping.value("filename").toString(); - - if (filename.isNull()) { - finishedLoading(false); - } else { - const QString baseURL = _mapping.value("baseURL").toString(); - const QUrl base = _effectiveBaseURL.resolved(baseURL); - QUrl url = base.resolved(filename); - - QString texdir = _mapping.value(TEXDIR_FIELD).toString(); - if (!texdir.isNull()) { - if (!texdir.endsWith('/')) { - texdir += '/'; - } - _textureBaseUrl = resolveTextureBaseUrl(url, base.resolved(texdir)); - } else { - _textureBaseUrl = url.resolved(QUrl(".")); - } - - auto scripts = FSTReader::getScripts(base, _mapping); - if (scripts.size() > 0) { - _mapping.remove(SCRIPT_FIELD); - for (auto &scriptPath : scripts) { - _mapping.insertMulti(SCRIPT_FIELD, scriptPath); - } - } - - auto animGraphVariant = _mapping.value("animGraphUrl"); - - if (animGraphVariant.isValid()) { - QUrl fstUrl(animGraphVariant.toString()); - if (fstUrl.isValid()) { - _animGraphOverrideUrl = base.resolved(fstUrl); - } else { - _animGraphOverrideUrl = QUrl(); - } - } else { - _animGraphOverrideUrl = QUrl(); - } - - auto modelCache = DependencyManager::get(); - GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseUrl, false }; - - // Get the raw GeometryResource - _geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash()(extra)).staticCast(); - // Avoid caching nested resources - their references will be held by the parent - _geometryResource->_isCacheable = false; - - if (_geometryResource->isLoaded()) { - onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty()); - } else { - if (_connection) { - disconnect(_connection); - } - - _connection = connect(_geometryResource.data(), &Resource::finished, - this, &GeometryMappingResource::onGeometryMappingLoaded); - } - } -} - -void GeometryMappingResource::onGeometryMappingLoaded(bool success) { - if (success && _geometryResource) { - _hfmModel = _geometryResource->_hfmModel; - _materialMapping = _geometryResource->_materialMapping; - _meshParts = _geometryResource->_meshParts; - _meshes = _geometryResource->_meshes; - _materials = _geometryResource->_materials; - - // Avoid holding onto extra references - _geometryResource.reset(); - // Make sure connection will not trigger again - disconnect(_connection); // FIXME Should not have to do this - } - - PROFILE_ASYNC_END(resource_parse_geometry, "GeometryMappingResource::downloadFinished", _url.toString()); - finishedLoading(success); -} - class GeometryReader : public QRunnable { public: GeometryReader(const ModelLoader& modelLoader, QWeakPointer& resource, const QUrl& url, const GeometryMappingPair& mapping, @@ -308,47 +199,137 @@ void GeometryReader::run() { } } -class GeometryDefinitionResource : public GeometryResource { - Q_OBJECT -public: - GeometryDefinitionResource(const ModelLoader& modelLoader, const QUrl& url) : GeometryResource(url), _modelLoader(modelLoader) {} - GeometryDefinitionResource(const GeometryDefinitionResource& other) : - GeometryResource(other), - _modelLoader(other._modelLoader), - _mapping(other._mapping), - _combineParts(other._combineParts) {} +QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) { + return textureBaseUrl.isValid() ? textureBaseUrl : url; +} - QString getType() const override { return "GeometryDefinition"; } +GeometryResource::GeometryResource(const GeometryResource& other) : + Resource(other), + Geometry(other), + _modelLoader(other._modelLoader), + _mappingPair(other._mappingPair), + _textureBaseURL(other._textureBaseURL), + _combineParts(other._combineParts), + _isCacheable(other._isCacheable) +{ + if (other._geometryResource) { + _startedLoading = false; + } +} - virtual void downloadFinished(const QByteArray& data) override; +void GeometryResource::downloadFinished(const QByteArray& data) { + if (_activeUrl.fileName().toLower().endsWith(".fst")) { + PROFILE_ASYNC_BEGIN(resource_parse_geometry, "GeometryResource::downloadFinished", _url.toString(), { { "url", _url.toString() } }); - void setExtra(void* extra) override; + // store parsed contents of FST file + _mapping = FSTReader::readMapping(data); -protected: - Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping); + QString filename = _mapping.value("filename").toString(); -private: - ModelLoader _modelLoader; - GeometryMappingPair _mapping; - bool _combineParts; -}; + if (filename.isNull()) { + finishedLoading(false); + } else { + const QString baseURL = _mapping.value("baseURL").toString(); + const QUrl base = _effectiveBaseURL.resolved(baseURL); + QUrl url = base.resolved(filename); -void GeometryDefinitionResource::setExtra(void* extra) { + QString texdir = _mapping.value(TEXDIR_FIELD).toString(); + if (!texdir.isNull()) { + if (!texdir.endsWith('/')) { + texdir += '/'; + } + _textureBaseURL = resolveTextureBaseUrl(url, base.resolved(texdir)); + } else { + _textureBaseURL = url.resolved(QUrl(".")); + } + + auto scripts = FSTReader::getScripts(base, _mapping); + if (scripts.size() > 0) { + _mapping.remove(SCRIPT_FIELD); + for (auto &scriptPath : scripts) { + _mapping.insertMulti(SCRIPT_FIELD, scriptPath); + } + } + + auto animGraphVariant = _mapping.value("animGraphUrl"); + + if (animGraphVariant.isValid()) { + QUrl fstUrl(animGraphVariant.toString()); + if (fstUrl.isValid()) { + _animGraphOverrideUrl = base.resolved(fstUrl); + } else { + _animGraphOverrideUrl = QUrl(); + } + } else { + _animGraphOverrideUrl = QUrl(); + } + + auto modelCache = DependencyManager::get(); + GeometryExtra extra { GeometryMappingPair(base, _mapping), _textureBaseURL, false }; + + // Get the raw GeometryResource + _geometryResource = modelCache->getResource(url, QUrl(), &extra, std::hash()(extra)).staticCast(); + // Avoid caching nested resources - their references will be held by the parent + _geometryResource->_isCacheable = false; + + if (_geometryResource->isLoaded()) { + onGeometryMappingLoaded(!_geometryResource->getURL().isEmpty()); + } else { + if (_connection) { + disconnect(_connection); + } + + _connection = connect(_geometryResource.data(), &Resource::finished, this, &GeometryResource::onGeometryMappingLoaded); + } + } + } else { + if (_url != _effectiveBaseURL) { + _url = _effectiveBaseURL; + _textureBaseURL = _effectiveBaseURL; + } + QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mappingPair, data, _combineParts, _request->getWebMediaType())); + } +} + +bool GeometryResource::handleFailedRequest(ResourceRequest::Result result) { + if (_shouldFailOnRedirect && result == ResourceRequest::Result::RedirectFail) { + auto newPath = _request->getRelativePathUrl(); + if (newPath.fileName().toLower().endsWith(".fst")) { + _activeUrl = newPath; + _shouldFailOnRedirect = false; + makeRequest(); + return true; + } + } + return Resource::handleFailedRequest(result); +} + +void GeometryResource::onGeometryMappingLoaded(bool success) { + if (success && _geometryResource) { + _hfmModel = _geometryResource->_hfmModel; + _materialMapping = _geometryResource->_materialMapping; + _meshParts = _geometryResource->_meshParts; + _meshes = _geometryResource->_meshes; + _materials = _geometryResource->_materials; + + // Avoid holding onto extra references + _geometryResource.reset(); + // Make sure connection will not trigger again + disconnect(_connection); // FIXME Should not have to do this + } + + PROFILE_ASYNC_END(resource_parse_geometry, "GeometryResource::downloadFinished", _url.toString()); + finishedLoading(success); +} + +void GeometryResource::setExtra(void* extra) { const GeometryExtra* geometryExtra = static_cast(extra); - _mapping = geometryExtra ? geometryExtra->mapping : GeometryMappingPair(QUrl(), QVariantHash()); - _textureBaseUrl = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl(); + _mappingPair = geometryExtra ? geometryExtra->mapping : GeometryMappingPair(QUrl(), QVariantHash()); + _textureBaseURL = geometryExtra ? resolveTextureBaseUrl(_url, geometryExtra->textureBaseUrl) : QUrl(); _combineParts = geometryExtra ? geometryExtra->combineParts : true; } -void GeometryDefinitionResource::downloadFinished(const QByteArray& data) { - if (_url != _effectiveBaseURL) { - _url = _effectiveBaseURL; - _textureBaseUrl = _effectiveBaseURL; - } - QThreadPool::globalInstance()->start(new GeometryReader(_modelLoader, _self, _effectiveBaseURL, _mapping, data, _combineParts, _request->getWebMediaType())); -} - -void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping) { +void GeometryResource::setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping) { // Assume ownership of the processed HFMModel _hfmModel = hfmModel; _materialMapping = materialMapping; @@ -357,7 +338,7 @@ void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmMode QHash materialIDAtlas; for (const HFMMaterial& material : _hfmModel->materials) { materialIDAtlas[material.materialID] = _materials.size(); - _materials.push_back(std::make_shared(material, _textureBaseUrl)); + _materials.push_back(std::make_shared(material, _textureBaseURL)); } std::shared_ptr meshes = std::make_shared(); @@ -380,6 +361,23 @@ void GeometryDefinitionResource::setGeometryDefinition(HFMModel::Pointer hfmMode finishedLoading(true); } +void GeometryResource::deleter() { + resetTextures(); + Resource::deleter(); +} + +void GeometryResource::setTextures() { + if (_hfmModel) { + for (const HFMMaterial& material : _hfmModel->materials) { + _materials.push_back(std::make_shared(material, _textureBaseURL)); + } + } +} + +void GeometryResource::resetTextures() { + _materials.clear(); +} + ModelCache::ModelCache() { const qint64 GEOMETRY_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; setUnusedResourceCacheSize(GEOMETRY_DEFAULT_UNUSED_MAX_SIZE); @@ -392,26 +390,14 @@ ModelCache::ModelCache() { } QSharedPointer ModelCache::createResource(const QUrl& url) { - Resource* resource = nullptr; - if (url.path().toLower().endsWith(".fst")) { - resource = new GeometryMappingResource(url); - } else { - resource = new GeometryDefinitionResource(_modelLoader, url); - } - - return QSharedPointer(resource, &Resource::deleter); + return QSharedPointer(new GeometryResource(url, _modelLoader), &GeometryResource::deleter); } QSharedPointer ModelCache::createResourceCopy(const QSharedPointer& resource) { - if (resource->getURL().path().toLower().endsWith(".fst")) { - return QSharedPointer(new GeometryMappingResource(*resource.staticCast()), &Resource::deleter); - } else { - return QSharedPointer(new GeometryDefinitionResource(*resource.staticCast()), &Resource::deleter); - } + return QSharedPointer(new GeometryResource(*resource.staticCast()), &GeometryResource::deleter); } -GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, - const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) { +GeometryResource::Pointer ModelCache::getGeometryResource(const QUrl& url, const GeometryMappingPair& mapping, const QUrl& textureBaseUrl) { bool combineParts = true; GeometryExtra geometryExtra = { mapping, textureBaseUrl, combineParts }; GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra, std::hash()(geometryExtra)).staticCast(); @@ -531,23 +517,6 @@ const std::shared_ptr Geometry::getShapeMaterial(int partID) co return nullptr; } -void GeometryResource::deleter() { - resetTextures(); - Resource::deleter(); -} - -void GeometryResource::setTextures() { - if (_hfmModel) { - for (const HFMMaterial& material : _hfmModel->materials) { - _materials.push_back(std::make_shared(material, _textureBaseUrl)); - } - } -} - -void GeometryResource::resetTextures() { - _materials.clear(); -} - void GeometryResourceWatcher::startWatching() { connect(_resource.data(), &Resource::finished, this, &GeometryResourceWatcher::resourceFinished); connect(_resource.data(), &Resource::onRefresh, this, &GeometryResourceWatcher::resourceRefreshed); diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index ca1ceaff16..034a00912a 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -24,8 +24,6 @@ class MeshPart; -class GeometryMappingResource; - using GeometryMappingPair = std::pair; Q_DECLARE_METATYPE(GeometryMappingPair) @@ -60,8 +58,6 @@ public: const QVariantHash& getMapping() const { return _mapping; } protected: - friend class GeometryMappingResource; - // Shared across all geometries, constant throughout lifetime std::shared_ptr _hfmModel; MaterialMapping _materialMapping; @@ -80,23 +76,30 @@ private: /// A geometry loaded from the network. class GeometryResource : public Resource, public Geometry { + Q_OBJECT public: using Pointer = QSharedPointer; - GeometryResource(const QUrl& url) : Resource(url) {} - GeometryResource(const GeometryResource& other) : - Resource(other), - Geometry(other), - _textureBaseUrl(other._textureBaseUrl), - _isCacheable(other._isCacheable) {} + GeometryResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) { _shouldFailOnRedirect = !url.fileName().toLower().endsWith(".fst"); } + GeometryResource(const GeometryResource& other); - virtual bool areTexturesLoaded() const override { return isLoaded() && Geometry::areTexturesLoaded(); } + QString getType() const override { return "Geometry"; } virtual void deleter() override; + virtual void downloadFinished(const QByteArray& data) override; + bool handleFailedRequest(ResourceRequest::Result result) override; + void setExtra(void* extra) override; + + virtual bool areTexturesLoaded() const override { return isLoaded() && Geometry::areTexturesLoaded(); } + +private slots: + void onGeometryMappingLoaded(bool success); + protected: friend class ModelCache; - friend class GeometryMappingResource; + + Q_INVOKABLE void setGeometryDefinition(HFMModel::Pointer hfmModel, const MaterialMapping& materialMapping); // Geometries may not hold onto textures while cached - that is for the texture cache // Instead, these methods clear and reset textures from the geometry when caching/loading @@ -104,10 +107,18 @@ protected: void setTextures(); void resetTextures(); - QUrl _textureBaseUrl; - virtual bool isCacheable() const override { return _loaded && _isCacheable; } - bool _isCacheable { true }; + +private: + ModelLoader _modelLoader; + GeometryMappingPair _mappingPair; + QUrl _textureBaseURL; + bool _combineParts; + + GeometryResource::Pointer _geometryResource; + QMetaObject::Connection _connection; + + bool _isCacheable{ true }; }; class GeometryResourceWatcher : public QObject { @@ -158,7 +169,7 @@ public: const QUrl& textureBaseUrl = QUrl()); protected: - friend class GeometryMappingResource; + friend class GeometryResource; virtual QSharedPointer createResource(const QUrl& url) override; QSharedPointer createResourceCopy(const QSharedPointer& resource) override; From 3605b3d560ef4fa56619a351384f305cf1b9e14e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 12 Apr 2019 15:49:34 -0700 Subject: [PATCH 78/85] Patch interface redirect code --- .../src/model-networking/ModelCache.cpp | 15 +-------------- .../src/model-networking/ModelCache.h | 3 +-- libraries/networking/src/ResourceCache.cpp | 1 + 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index b914faaab9..26bd20d967 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -218,7 +218,7 @@ GeometryResource::GeometryResource(const GeometryResource& other) : } void GeometryResource::downloadFinished(const QByteArray& data) { - if (_activeUrl.fileName().toLower().endsWith(".fst")) { + if (_effectiveBaseURL.fileName().toLower().endsWith(".fst")) { PROFILE_ASYNC_BEGIN(resource_parse_geometry, "GeometryResource::downloadFinished", _url.toString(), { { "url", _url.toString() } }); // store parsed contents of FST file @@ -291,19 +291,6 @@ void GeometryResource::downloadFinished(const QByteArray& data) { } } -bool GeometryResource::handleFailedRequest(ResourceRequest::Result result) { - if (_shouldFailOnRedirect && result == ResourceRequest::Result::RedirectFail) { - auto newPath = _request->getRelativePathUrl(); - if (newPath.fileName().toLower().endsWith(".fst")) { - _activeUrl = newPath; - _shouldFailOnRedirect = false; - makeRequest(); - return true; - } - } - return Resource::handleFailedRequest(result); -} - void GeometryResource::onGeometryMappingLoaded(bool success) { if (success && _geometryResource) { _hfmModel = _geometryResource->_hfmModel; diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index 034a00912a..f9ae2dccd6 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -80,7 +80,7 @@ class GeometryResource : public Resource, public Geometry { public: using Pointer = QSharedPointer; - GeometryResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) { _shouldFailOnRedirect = !url.fileName().toLower().endsWith(".fst"); } + GeometryResource(const QUrl& url, const ModelLoader& modelLoader) : Resource(url), _modelLoader(modelLoader) {} GeometryResource(const GeometryResource& other); QString getType() const override { return "Geometry"; } @@ -88,7 +88,6 @@ public: virtual void deleter() override; virtual void downloadFinished(const QByteArray& data) override; - bool handleFailedRequest(ResourceRequest::Result result) override; void setExtra(void* extra) override; virtual bool areTexturesLoaded() const override { return isLoaded() && Geometry::areTexturesLoaded(); } diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index d5abb27a27..746c28a306 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -577,6 +577,7 @@ Resource::Resource(const Resource& other) : Resource::Resource(const QUrl& url) : _url(url), _activeUrl(url), + _effectiveBaseURL(url), _requestID(++requestID) { init(); } From 329a76d4d67000372b0dd8ed83cba9b8af33cc9d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Fri, 12 Apr 2019 17:51:03 -0700 Subject: [PATCH 79/85] Fix unix warning --- libraries/networking/src/ResourceCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 746c28a306..44d3d1ee4d 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -576,8 +576,8 @@ Resource::Resource(const Resource& other) : Resource::Resource(const QUrl& url) : _url(url), - _activeUrl(url), _effectiveBaseURL(url), + _activeUrl(url), _requestID(++requestID) { init(); } From 77c5ea5fa8ef93abbad02cd74c6347d3e150fa45 Mon Sep 17 00:00:00 2001 From: Clement Date: Mon, 15 Apr 2019 19:08:44 -0700 Subject: [PATCH 80/85] Fix Asset Server reporting bad status for textures --- assignment-client/src/assets/AssetServer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 4805d2fe4c..297a622c25 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -146,7 +146,6 @@ std::pair AssetServer::getAssetStatus(const A } BakedAssetType type = assetTypeForFilename(path); - if (type == BakedAssetType::Undefined) { return { AssetUtils::Irrelevant, "" }; } @@ -155,6 +154,12 @@ std::pair AssetServer::getAssetStatus(const A AssetMeta meta; std::tie(loaded, meta) = readMetaFile(hash); + // We create a meta file for Skyboxes at runtime when they get requested + // Otherwise, textures don't get baked by themselves. + if (type == BakedAssetType::Texture && !loaded) { + return { AssetUtils::Irrelevant, "" }; + } + QString bakedFilename = bakedFilenameForAssetType(type); auto bakedPath = getBakeMapping(hash, bakedFilename); if (loaded && !meta.redirectTarget.isEmpty()) { @@ -240,6 +245,8 @@ bool AssetServer::needsToBeBaked(const AssetUtils::AssetPath& path, const AssetU return false; } + // We create a meta file for Skyboxes at runtime when they get requested + // Otherwise, textures don't get baked by themselves. if (type == BakedAssetType::Texture && !loaded) { return false; } From 86b63410989314ae69651d2d3c2ad3571f882cb6 Mon Sep 17 00:00:00 2001 From: Clement Date: Tue, 16 Apr 2019 19:40:59 -0700 Subject: [PATCH 81/85] Add error reporting + Make temp dir erase safer --- assignment-client/src/assets/AssetServer.cpp | 91 ++++++++++++------- .../src/assets/BakeAssetTask.cpp | 54 +++++------ libraries/shared/src/PathUtils.cpp | 24 +++++ libraries/shared/src/PathUtils.h | 1 + 4 files changed, 102 insertions(+), 68 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 297a622c25..502cf15aa2 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -1314,27 +1314,57 @@ void AssetServer::handleFailedBake(QString originalAssetHash, QString assetPath, void AssetServer::handleCompletedBake(QString originalAssetHash, QString originalAssetPath, QString bakedTempOutputDir) { + auto reportCompletion = [this, originalAssetPath, originalAssetHash](bool errorCompletingBake, + QString errorReason, + QString redirectTarget) { + auto type = assetTypeForFilename(originalAssetPath); + auto currentTypeVersion = currentBakeVersionForAssetType(type); + + AssetMeta meta; + meta.bakeVersion = currentTypeVersion; + meta.failedLastBake = errorCompletingBake; + meta.redirectTarget = redirectTarget; + + if (errorCompletingBake) { + qWarning() << "Could not complete bake for" << originalAssetHash; + meta.lastBakeErrors = errorReason; + } + + writeMetaFile(originalAssetHash, meta); + + _pendingBakes.remove(originalAssetHash); + }; + bool errorCompletingBake { false }; QString errorReason; + QString redirectTarget; qDebug() << "Completing bake for " << originalAssetHash; - - + // Find the directory containing the baked content QDir outputDir(bakedTempOutputDir); + QString outputDirName = outputDir.dirName(); auto directories = outputDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - assert(directories.size() == 1); QString bakedDirectoryPath; for (const auto& dirName : directories) { outputDir.cd(dirName); if (outputDir.exists("baked") && outputDir.exists("original")) { bakedDirectoryPath = outputDir.filePath("baked"); + break; } outputDir.cdUp(); } + if (bakedDirectoryPath.isEmpty()) { + errorCompletingBake = true; + errorReason = "Failed to find baking output"; - assert(!bakedDirectoryPath.isEmpty()); + // Cleanup temporary output directory + PathUtils::deleteMyTemporaryDir(outputDirName); + reportCompletion(errorCompletingBake, errorReason, redirectTarget); + return; + } + // Compile list of all the baked files QDirIterator it(bakedDirectoryPath, QDirIterator::Subdirectories); QVector bakedFilePaths; while (it.hasNext()) { @@ -1343,9 +1373,17 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina bakedFilePaths.push_back(it.filePath()); } } + if (bakedFilePaths.isEmpty()) { + errorCompletingBake = true; + errorReason = "Baking output has no files"; + + // Cleanup temporary output directory + PathUtils::deleteMyTemporaryDir(outputDirName); + reportCompletion(errorCompletingBake, errorReason, redirectTarget); + return; + } QDir bakedDirectory(bakedDirectoryPath); - QString redirectTarget; for (auto& filePath : bakedFilePaths) { // figure out the hash for the contents of this file @@ -1359,7 +1397,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina qDebug() << "Failed to open baked file: " << filePath; // stop handling this bake, we couldn't open one of the files for reading errorCompletingBake = true; - errorReason = "Failed to finalize bake"; + errorReason = "Could not open baked file " + file.fileName(); break; } @@ -1368,7 +1406,7 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina if (!hasher.addData(&file)) { // stop handling this bake, couldn't hash the contents of the file errorCompletingBake = true; - errorReason = "Failed to finalize bake"; + errorReason = "Could not hash data for " + file.fileName(); break; } @@ -1388,13 +1426,15 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina // setup the mapping for this bake file auto relativeFilePath = bakedDirectory.relativeFilePath(filePath); - qDebug() << "Relative file path is: " << relativeFilePath; QString bakeMapping = getBakeMapping(originalAssetHash, relativeFilePath); // Check if this is the file we should redirect to when someone asks for the original asset if ((relativeFilePath.endsWith(".baked.fst", Qt::CaseInsensitive) && originalAssetPath.endsWith(".fbx")) || (relativeFilePath.endsWith(".texmeta.json", Qt::CaseInsensitive) && !originalAssetPath.endsWith(".fbx"))) { + if (!redirectTarget.isEmpty()) { + qWarning() << "Found multiple baked redirect target for" << originalAssetPath; + } redirectTarget = bakeMapping; } @@ -1403,41 +1443,22 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina qDebug() << "Failed to set mapping"; // stop handling this bake, couldn't add a mapping for this bake file errorCompletingBake = true; - errorReason = "Failed to finalize bake"; + errorReason = "Failed to set mapping for baked file " + file.fileName(); break; } qDebug() << "Added" << bakeMapping << "for bake file" << bakedFileHash << "from bake of" << originalAssetHash; } - for (auto& filePath : bakedFilePaths) { - QFile file(filePath); - if (!file.remove()) { - qWarning() << "Failed to remove temporary file:" << filePath; - } - } - if (!QDir(bakedTempOutputDir).rmdir(".")) { - qWarning() << "Failed to remove temporary directory:" << bakedTempOutputDir; + + if (redirectTarget.isEmpty()) { + errorCompletingBake = true; + errorReason = "Could not find root file for baked output"; } - auto type = assetTypeForFilename(originalAssetPath); - auto currentTypeVersion = currentBakeVersionForAssetType(type); - - assert(!redirectTarget.isEmpty()); - - AssetMeta meta; - meta.bakeVersion = currentTypeVersion; - meta.failedLastBake = errorCompletingBake; - meta.redirectTarget = redirectTarget; - - if (errorCompletingBake) { - qWarning() << "Could not complete bake for" << originalAssetHash; - meta.lastBakeErrors = errorReason; - } - - writeMetaFile(originalAssetHash, meta); - - _pendingBakes.remove(originalAssetHash); + // Cleanup temporary output directory + PathUtils::deleteMyTemporaryDir(outputDirName); + reportCompletion(errorCompletingBake, errorReason, redirectTarget); } void AssetServer::handleAbortedBake(QString originalAssetHash, QString assetPath) { diff --git a/assignment-client/src/assets/BakeAssetTask.cpp b/assignment-client/src/assets/BakeAssetTask.cpp index 7ca87fe4e3..99fff4f180 100644 --- a/assignment-client/src/assets/BakeAssetTask.cpp +++ b/assignment-client/src/assets/BakeAssetTask.cpp @@ -36,21 +36,6 @@ BakeAssetTask::BakeAssetTask(const AssetUtils::AssetHash& assetHash, const Asset }); } -void cleanupTempFiles(QString tempOutputDir, std::vector files) { - for (const auto& filename : files) { - QFile f { filename }; - if (!f.remove()) { - qDebug() << "Failed to remove:" << filename; - } - } - if (!tempOutputDir.isEmpty()) { - QDir dir { tempOutputDir }; - if (!dir.rmdir(".")) { - qDebug() << "Failed to remove temporary directory:" << tempOutputDir; - } - } -}; - void BakeAssetTask::run() { if (_isBaking.exchange(true)) { qWarning() << "Tried to start bake asset task while already baking"; @@ -59,11 +44,24 @@ void BakeAssetTask::run() { // Make a new temporary directory for the Oven to work in QString tempOutputDir = PathUtils::generateTemporaryDir(); + QString tempOutputDirName = QDir(tempOutputDir).dirName(); + if (tempOutputDir.isEmpty()) { + QString errors = "Could not create temporary working directory"; + emit bakeFailed(_assetHash, _assetPath, errors); + PathUtils::deleteMyTemporaryDir(tempOutputDirName); + return; + } // Copy file to bake the temporary dir and give a name the oven can work with auto assetName = _assetPath.split("/").last(); auto tempAssetPath = tempOutputDir + "/" + assetName; - QFile::copy(_filePath, tempAssetPath); + auto sucess = QFile::copy(_filePath, tempAssetPath); + if (!sucess) { + QString errors = "Couldn't copy file to bake to temporary directory"; + emit bakeFailed(_assetHash, _assetPath, errors); + PathUtils::deleteMyTemporaryDir(tempOutputDirName); + return; + } auto base = QFileInfo(QCoreApplication::applicationFilePath()).absoluteDir(); QString path = base.absolutePath() + "/oven"; @@ -79,30 +77,23 @@ void BakeAssetTask::run() { QEventLoop loop; connect(_ovenProcess.get(), static_cast(&QProcess::finished), - this, [&loop, this, tempOutputDir, tempAssetPath](int exitCode, QProcess::ExitStatus exitStatus) { + this, [&loop, this, tempOutputDir, tempAssetPath, tempOutputDirName](int exitCode, QProcess::ExitStatus exitStatus) { qDebug() << "Baking process finished: " << exitCode << exitStatus; if (exitStatus == QProcess::CrashExit) { + PathUtils::deleteMyTemporaryDir(tempOutputDirName); if (_wasAborted) { emit bakeAborted(_assetHash, _assetPath); } else { QString errors = "Fatal error occurred while baking"; emit bakeFailed(_assetHash, _assetPath, errors); } - if (!QDir(tempOutputDir).rmdir(".")) { - qWarning() << "Failed to remove temporary directory:" << tempOutputDir; - } } else if (exitCode == OVEN_STATUS_CODE_SUCCESS) { - // Remove temp copy of the original asset - QFile::remove(tempAssetPath); - emit bakeComplete(_assetHash, _assetPath, tempOutputDir); } else if (exitStatus == QProcess::NormalExit && exitCode == OVEN_STATUS_CODE_ABORT) { _wasAborted.store(true); + PathUtils::deleteMyTemporaryDir(tempOutputDirName); emit bakeAborted(_assetHash, _assetPath); - if (!QDir(tempOutputDir).rmdir(".")) { - qWarning() << "Failed to remove temporary directory:" << tempOutputDir; - } } else { QString errors; if (exitCode == OVEN_STATUS_CODE_FAIL) { @@ -116,10 +107,8 @@ void BakeAssetTask::run() { errors = "Unknown error occurred while baking"; } } + PathUtils::deleteMyTemporaryDir(tempOutputDirName); emit bakeFailed(_assetHash, _assetPath, errors); - if (!QDir(tempOutputDir).rmdir(".")) { - qWarning() << "Failed to remove temporary directory:" << tempOutputDir; - } } loop.quit(); @@ -128,12 +117,11 @@ void BakeAssetTask::run() { qDebug() << "Starting oven for " << _assetPath; _ovenProcess->start(path, args, QIODevice::ReadOnly); qDebug() << "Running:" << path << args; - if (!_ovenProcess->waitForStarted(-1)) { + if (!_ovenProcess->waitForStarted()) { + PathUtils::deleteMyTemporaryDir(tempOutputDirName); + QString errors = "Oven process failed to start"; emit bakeFailed(_assetHash, _assetPath, errors); - if (!QDir(tempOutputDir).rmdir(".")) { - qWarning() << "Failed to remove temporary directory:" << tempOutputDir; - } return; } diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 60b426e46d..be60533406 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -185,6 +185,30 @@ QString PathUtils::generateTemporaryDir() { return ""; } +bool PathUtils::deleteMyTemporaryDir(QString dirName) { + QDir rootTempDir = QDir::tempPath(); + + QString appName = qApp->applicationName(); + QRegularExpression re { "^" + QRegularExpression::escape(appName) + "\\-(?\\d+)\\-(?\\d+)$" }; + + auto match = re.match(dirName); + auto pid = match.capturedRef("pid").toLongLong(); + + if (match.hasMatch() && rootTempDir.exists(dirName) && pid == qApp->applicationPid()) { + auto absoluteDirPath = QDir(rootTempDir.absoluteFilePath(dirName)); + + bool success = absoluteDirPath.removeRecursively(); + if (success) { + qDebug() << " Removing temporary directory: " << absoluteDirPath.absolutePath(); + } else { + qDebug() << " Failed to remove temporary directory: " << absoluteDirPath.absolutePath(); + } + return success; + } + + return false; +} + // Delete all temporary directories for an application int PathUtils::removeTemporaryApplicationDirs(QString appName) { if (appName.isNull()) { diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h index 0096cb6c90..cac5c58ca4 100644 --- a/libraries/shared/src/PathUtils.h +++ b/libraries/shared/src/PathUtils.h @@ -56,6 +56,7 @@ public: static QString getAppLocalDataFilePath(const QString& filename); static QString generateTemporaryDir(); + static bool deleteMyTemporaryDir(QString dirName); static int removeTemporaryApplicationDirs(QString appName = QString::null); From 2c3dd153dd5e9a54707e876e82e569df67439272 Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 17 Apr 2019 12:57:15 -0700 Subject: [PATCH 82/85] Fix typo --- assignment-client/src/assets/BakeAssetTask.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/BakeAssetTask.cpp b/assignment-client/src/assets/BakeAssetTask.cpp index 99fff4f180..7c845f9b38 100644 --- a/assignment-client/src/assets/BakeAssetTask.cpp +++ b/assignment-client/src/assets/BakeAssetTask.cpp @@ -55,8 +55,8 @@ void BakeAssetTask::run() { // Copy file to bake the temporary dir and give a name the oven can work with auto assetName = _assetPath.split("/").last(); auto tempAssetPath = tempOutputDir + "/" + assetName; - auto sucess = QFile::copy(_filePath, tempAssetPath); - if (!sucess) { + auto success = QFile::copy(_filePath, tempAssetPath); + if (!success) { QString errors = "Couldn't copy file to bake to temporary directory"; emit bakeFailed(_assetHash, _assetPath, errors); PathUtils::deleteMyTemporaryDir(tempOutputDirName); From 464e5924d62875ad475317525a6cf60735c7b15b Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 23 Apr 2019 16:50:54 -0700 Subject: [PATCH 83/85] changing method name to reflect recent change in code --- .../resources/qml/hifi/commerce/marketplace/Marketplace.qml | 2 +- interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml index 5f8cc2eefb..65453ba21d 100644 --- a/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml +++ b/interface/resources/qml/hifi/commerce/marketplace/Marketplace.qml @@ -952,7 +952,7 @@ Rectangle { text: "LOG IN" onClicked: { - sendToScript({method: 'needsLogIn_loginClicked'}); + sendToScript({method: 'marketplace_loginClicked'}); } } diff --git a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml index 0a1593161a..e17196b492 100644 --- a/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml +++ b/interface/resources/qml/hifi/commerce/wallet/NeedsLogIn.qml @@ -137,7 +137,7 @@ Item { width: parent.width/2 - anchors.leftMargin*2; text: "Cancel" onClicked: { - sendToScript({method: 'needsLogIn_cancelClicked'}); + sendToScript({method: 'passphrasePopup_cancelClicked'}); } } @@ -155,7 +155,7 @@ Item { width: parent.width/2 - anchors.rightMargin*2; text: "Log In" onClicked: { - sendToScript({method: 'needsLogIn_loginClicked'}); + sendToScript({method: 'marketplace_loginClicked'}); } } } From 084759a91853575cdfe61bbafaf9d485dc9dcd55 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 24 Apr 2019 16:32:39 -0700 Subject: [PATCH 84/85] send update after changing entity --- interface/src/avatar/MyAvatar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 25c7a788b3..0680c22774 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1630,7 +1630,9 @@ void MyAvatar::handleChangedAvatarEntityData() { if (!skip) { sanitizeAvatarEntityProperties(properties); entityTree->withWriteLock([&] { - entityTree->updateEntity(id, properties); + if (entityTree->updateEntity(id, properties)) { + packetSender->queueEditAvatarEntityMessage(entityTree, id); + } }); } } From 6b33f4e02739ad062e85859cbc6309effb8c495c Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 25 Apr 2019 10:31:05 -0700 Subject: [PATCH 85/85] Don't call function if flow is not active and made it thread safe --- interface/src/avatar/MyAvatar.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 25c7a788b3..92c2e68c92 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -5811,12 +5811,19 @@ void MyAvatar::releaseGrab(const QUuid& grabID) { } void MyAvatar::addAvatarHandsToFlow(const std::shared_ptr& otherAvatar) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "addAvatarHandsToFlow", + Q_ARG(const std::shared_ptr&, otherAvatar)); + return; + } auto &flow = _skeletonModel->getRig().getFlow(); - for (auto &handJointName : HAND_COLLISION_JOINTS) { - int jointIndex = otherAvatar->getJointIndex(handJointName); - if (jointIndex != -1) { - glm::vec3 position = otherAvatar->getJointPosition(jointIndex); - flow.setOthersCollision(otherAvatar->getID(), jointIndex, position); + if (otherAvatar != nullptr && flow.getActive()) { + for (auto &handJointName : HAND_COLLISION_JOINTS) { + int jointIndex = otherAvatar->getJointIndex(handJointName); + if (jointIndex != -1) { + glm::vec3 position = otherAvatar->getJointPosition(jointIndex); + flow.setOthersCollision(otherAvatar->getID(), jointIndex, position); + } } } }